What is the most efficient way to render complex geometry?

I have a program that generates complex geometry on the fly. The geometry is not generated in the render call, however it is regenerated every time the user makes a change. The reason for this is that the geometry can change quite a bit depending on the input.

Right now the algorithm:

  • generates a lot of shapes (polygons)
  • extrudes these shapes into 3d geometry (using ExtrudeGeometry, no bevels)
  • creates buffergeometries out of them.
  • If geometry is very similar (only differs in position) it is instanced using InstancedMesh (however this is only possible in certain cases.)
  • adds wireframe, and merges those into 1 big wireframe

For simple cases this results in about 100-150 render calls. However in some cases it can also climb to 1500-2000 render calls. In this case there is a performance hit that I’d like to address. What would be the best way to do this?

Note that the issue is in the render call, not in the generation of the geometry. Basically the frame rate drops, even though the number of polygons is not actually ridiculously high.

I’ve considered a few options but am unsure what would be best.

  • Merge the polygon shapes into 1 shape, then extrude, then create a mesh
  • Merge all geometry before creating the mesh but after the extrudes

That sounds correct yes.

The usual way of reducing draw calls is using instanced rendering (which you already do in certain cases) or merging/batching meshes. I guess I would start with option two which sounds easier. The merge can then be performed via BufferGeometryUtils.mergeBufferGeometries().

Of course you can also try to utilize special culling algorithm to further decrease the number of draw calls. View frustum culling is done by three.js by default however stuff like occlusion culling or portal culling could introduce noticeable performance improvements in certain scenarios. However, the final benefit always depends on the application and sometimes the additional overhead outweighs the performance gains (meaning too less or no objects get culled).

1 Like

Thank you!

Frustum culling is already happening but not really helping since most geometry needs to be rendered at the same time. But that does remind me, my geometry is rendering with double faces enabled, so it might help to disable that.

Sounds like merging the geometry is a feasible option. I actually already do that for the wireframes.

Am I correct to say that I then need to group the geometry by the material it would need and merge them? You can’t have multiple materials in the same mesh right?

I do have different materials per geometry but not a huge amount, maybe 10 different ones or so.

A mesh does support multiple materials however the renderer creates single render items from such definitions. So the final number of draw calls is identical compared to having independent meshes.

But yes, merged geometries have to share the same material.

1 Like

Thank you!