RenderTargetTexture renders instanced meshes multiple times?

I’m not fully sure if this is a bug, but I haven’t been able to figure out another explanation for this when looking around in the source code:

I was implementing shader based picking that also separates picked object between instances. This included messing around with customized Mesh.prototype._processInstancedBuffers call in order to map gl_InstanceID index back to the InstancedMesh using the visibleInstances array.

All this worked fine, but I noticed something weird with my debug prints:

  • visbileInstances array during a render target render has more objects than the whole scene has.
  • Frustum culling doesn’t seem to work for the render target render, as it adds all instanced meshes to the visbileInstances call

Here is a Playground test case to replicate the issue: Babylon.js Playground

It has an instanced gltf model with 1000 cubes (loader loads it as 1 base cube that has 999 instances)

When you click the button in the middle to render to the RenderTargetTexture it prints out the visibleInstances array size in console and after that same visibleInstances size for the next normal render call.

What ends up happening is that:

  • The RenderTargetTexture renders with 1313 instances, that is more than the whole scene has objects, so some of them must render twice. The number seems to be 999 + the previous instances count from the last render loop.
  • And as it adds full 999 instances to the visibleInstances it indicates that frustrum culling also isn’t working for the render target render. Although not sure if this is by design?

This is by design. If you set a mesh list to RTT.renderList, the system assumes you want these meshes to be displayed in the texture. If you want to use the list of meshes culled by the camera used to render into the RTT, set RTT.renderList = null.

Regarding the number of instances in visibleInstances which is too high, there may be a bug indeed: the meshes added during the regular pass are still there when the RTT pass is running.

However, the RTTs are normally processed by Babylon.js at the start of the frame loop and the frameRenderId (scene._renderId actually) are updated correctly to avoid this bug. In your PG, you are calling RTT.render() yourself and in an event handler: it means it is called at the very end of the frame loop and the frameRenderId retains its last value and is not updated. You can fix your problem by simply calling scene.incrementRenderId() before calling RTT.render(), that’s why I’m not sure it is a bug… @Deltakosh / @sebavan what do you think?

Here’s a fixed PG:

Not a bug for me as this is the expected behavior. Maybe we could drop a note about it in the doc?

1 Like

Ah. So if I wanted to “filter out” meshes (only include meshes that are pickable) rather than forcing all of them to be rendered regardless of frustrum culling I should be maybe using renderListPredicate instead? Or is there different way for that?

Yes, if you set a renderListPredicate, all meshes from the scene will be run against this predicate and only the meshes fulfilling it will be added to the render list.

I now switched to renderListPredicate but frustrum culling is still disabled. So I guess that isn’t any different than setting renderList manually.

But at least scene.incrementRenderId() got rid of the duplicated renders. :+1:

1 Like

Yes, you should add a test against the frustum yourself in the predicate if you need it.

1 Like