/Trident-WEBGPU

Primary LanguageMakefileMIT LicenseMIT

Live demo

showcase.mp4

Features

  • Automatic dynamic LOD
  • Frustum culling
  • Backface culling
  • Occlusion culling (HiZ)
  • Small features culling
  • Render graph
  • Instanced meshes
  • Unity like API
  • Cascaded shadow maps
  • PBR (with albedo, height and normal map support)
  • GPU driven
  • Deferred rendering
  • WEBGPU

Note this project is mostly experimental meaning that the features listed above are implemented but they may have bugs or not be cohesive with one another.

While the code is performant (caching pipelines/bindgroups/views etc) due to the lack of multi indirect draw calls some bottlenecks are present. For example rendering 100^3 cubes with InstancedMesh is more performant than using the GPUDriven pass, this is because drawIndirect always uses 128 triangles, while when instancing a cube it can use 36 triangles.

Examples (may change)

Setup

const renderer = Renderer.Create(canvas, "webgpu");
const scene = new Scene(renderer);
const mainCameraGameObject = new GameObject(scene);
mainCameraGameObject.transform.position.set(0,0,-15);
const camera = mainCameraGameObject.AddComponent(Camera);
camera.SetPerspective(72, canvas.width / canvas.height, 0.5, 50000);
camera.transform.LookAt(new Vector3(0,0,1))

const controls = new OrbitControls(camera);
controls.connect(canvas);

GPUDriven

const bunnyObj = await OBJLoaderIndexed.load("./bunny.obj");
const bunnyGeometry = new Geometry();
bunnyGeometry.attributes.set("position", new VertexAttribute(bunnyObj.vertices));
bunnyGeometry.index = new IndexAttribute(bunnyObj.indices);

const n = 30;
for (let x = 0; x < n; x++) {
    for (let y = 0; y < n; y++) {
        for (let z = 0; z < n; z++) {
            const gameObject = new GameObject(scene);
            gameObject.transform.scale.set(20, 20, 20);

            gameObject.transform.position.set(x * 10, y * 10, z * 10);
            const meshletMesh = gameObject.AddComponent(MeshletMesh);

            await meshletMesh.SetGeometry(bunnyGeometry);
        }
    }
}

Instanced

const cube = new GameObject(scene);
const cubeMeshInstanced = cube.AddComponent(InstancedMesh);
await cubeMeshInstanced.SetGeometry(Geometry.Cube());
const position = new Vector3(0,0,0);
const rotation = new Quaternion();
const scale = new Vector3(1,1,1);
const m = new Matrix4();
let i = 0;
for (let x = 0; x < n; x++) {
    for (let y = 0; y < n; y++) {
        for (let z = 0; z < n; z++) {
            position.set(x * 10, y * 10, z * 10);
            m.compose(position, rotation, scale);
            cubeMeshInstanced.SetMatrixAt(i, m);
            i++;
        }
    }
}

Credits

This was built based on a lot of open source resources online, such as @zeux meshoptimizer/niagara etc.
Credit was not always given and apologies for that, when the project stabilizes a bit it will be updated with the proper sources, if you see some portions of your code feel free to reach out and it will be updated.