2
votes

When I render 3000 instances of my model and keep them in the view area i get 55FPS, But If i have 5000 instances of my model but keep 2000 of them Way outside the view area, I still get 40 FPS.

camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.01, 10000);

Is it possible it is culling correctly but just the fact it has to determine if its being culled whats slowing it down? I mostly pulled from https://github.com/mrdoob/three.js/blob/master/examples/webgl_buffergeometry_instancing_dynamic.html

Edit* I know i can use geometry.maxInstancedCount = trackCount; this is helpful but its not quite the same thing as culling, as I have tracks out of the current view port and it still hurts the performence

Any Suggestions? or thoughts?

1

1 Answers

5
votes

For instanced geometries three.js can not do any frustum culling as there is no information about where the objects will end up. This will be computed in the vertex-shader and three.js doesn't have any knowledge about what happens there.

The earliest possible time for any culling to happen is after running the vertex-shader for every vertex of every instance. This already explains the drop in framerate you're seeing. Although the same number of objects/triangles are visible, the number of vertex-shader invocations goes up to 166%.

If you want to implement culling yourself, you could try to rearrange the instances in the attribute-buffers for every frame (skip invisible instances) and adjust the max-instance count to the number of visible instances. That might be a bit counter-intuitive, but recomputing all instance-attribute buffers on every frame can actually give a better performance here.

To do the visibility-test, the simplest way would probably be to use the THREE.Frustum() class and frustum.containsPoint(). That would look something like this

const frustum = new Frustum();
const projScreenMatrix = new Matrix();

// assume instances is an array of objects containing all the 
// relevant information for all instances
const instances = [
  // {position: ...}, {position: ...}, ...
];

// for every frame
projScreenMatrix.multiplyMatrices(
  camera.projectionMatrix, 
  camera.matrixWorldInverse 
);
frustum.setFromMatrix(projScreenMatrix);

let visibleInstanceCount = 0;

for (let i = 0; i < instanceCount; i++) {
  const pos = instances[i].position;

  if (!frustum.containsPoint(pos)) {
    continue;
  }

  // add instance to instance-attribute buffers
  pos.toArray(instancePositionBuffer, visibleInstanceCount * 3);
  visibleInstanceCount++;
}

geometry.maxInstanceCount = visibleInstanceCount;