-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
Description
Description
Use Case
I'm rendering complex scenes with massive object counts using optimized instanced rendering. My rendering pipeline includes:
Spatial partitioning: AABB bounding boxes for hierarchical scene organisation
Optimized frustum culling: O(1) near/far corner detection using signbit-based plane testing https://www.cse.chalmers.se/~uffe/vfc_bbox.pdf
Front-to-back sorting: AABBs sorted by camera distance for optimal draw order
Instanced rendering: Each visible AABB triggers a glDrawElementsInstanced call rendering hundreds of objects with transformation matrices
While threeJS offers a simple and easy to understand interface, simple things like rendering THREE.InstancedMesh geometry to depth buffer with a custom shader seem hard to do.
But maybe I am just doing something wrong.
Thank you in advance,
Andor
Solution
As an 3D/OpenGL experienced but ThreeJS newbie I though something like this should work:
- Setting a shader/material
- Setting a render Target (Depth Buffer)
- Render Indexed geometry with the global shader to render target.
// Sort AABBs front-to-back by distance - only once for all passes
this.engine.sortNodesFrontToBack(this.camera.position);
this.engine.applyFrustumCulling(this.camera);
// Z-PrePass (depth-only, front-to-back)
if (this.passManager.isEnabled('zPrePass')) {
this.passManager.startPass('zPrePass');
// Override all materials with depth-only material - should set vertex and fragment shaders
this.scene.overrideMaterial = this.depthOnlyMaterial;
// Configure depth-only rendering state
gl.colorMask(false, false, false, false); // Disable color writes
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Standard depth comparison (or gl.LEQUAL)
gl.depthMask(true); // Enable depth buffer writes
this.renderer.setRenderTraget(DepthBuffer);
// Traverse sorted AABBs and draw instanced meshes front to back, if visible
this.engine.nodes.forEach(aabb => {
const indexedMesh = this.engine.getMeshForNode(aabb);
if (indexedMesh && indexedMesh.visible) {
// Single glDrawElementsInstanced call for all meshes in this node
this.renderer.render(indexedMesh, this.camera);
// low level but not available method would be
// this.renderer.drawIndexedMesh(indexedMesh);
}
});
// Restore rendering state
gl.colorMask(true, true, true, true); // Re-enable color writes
gl.depthMask(false); // Disable depth writes
// Restore normal materials
this.scene.overrideMaterial = null;
this.passManager.endPass('zPrePass');
}
// Some other passes like SSAO
if (this.passManager.isEnabled('ssao')) {
....
}
// Simple Shading pass would be e.g.
gl.depthFunc(gl.EQUAL);
this.renderer.setRenderTraget(FrameBuffer);
// Traverse sorted AABBs and draw instanced meshes front to back, if visible
this.engine.nodes.forEach(aabb => {
const indexedMesh = this.engine.getMeshForNode(aabb);
if (indexedMesh && indexedMesh.visible) {
// This one works already
this.renderer.render(indexedMesh, this.camera);
}
});
Alternatives
I followed several issues like the following:
#8676
#20673
And especially:
Implementing Depth Pre-Pass Optimization for Three.JS
https://cprimozic.net/blog/threejs-depth-pre-pass-optimization/
I also tried extending the renderer which basically works, but I stumbled across some RenderState errors (which could be fixed somehow of course).
Additional context
No response