Possible issue with screenshots (RTT) and instanced meshes

Hi BJS team,

I faced a strange behavior of CreateScreenshotUsingRenderTarget when a scene contains instanced meshes. Under some conditions, the resulting screenshot misses meshes that have instanced “children”.

Details:
If the scene contains the “source” mesh (MeshA, for example) and InstancedMesh (MeshB) and this MeshB was never rendered (hidden by default), then MeshA will be missing on a screenshot.
But if MeshB will be rendered at least once, then MeshA will appear on screenshots despite the current visibility of the MeshB.

The problem happens only for screenshots that use RenderTargetTexture.

But the strange thing is that “missing” meshes are visible on RTT manually added to the scene (see demo PG).
So something happens only for screenshots’ RTT.

Affected BJS versions:
Can be reproduced in 6.49 and 7.12+.
v5.x seems not affected and MeshA appears on screenshots independently of MeshB visibility.

Demo PG:

After the scene is loaded, click on the “Take screenshot” button and there will be no “tabletop” (Table-Seated-Top in the Inspector, it’s a “source” mesh) on the screenshot (see attached image).
Next, click on the “Toggle Instanced Mesh” (“Table-Standing-Top” in the inspector) button so it will be rendered too. After that, the missing “Table-Seated-Top” starts appearing on screenshots normally.
Even if the “instanced” mesh is invisible, it won’t affect the “source” mesh anymore.

Note:
The issue may somehow be related to the scene itself.
In the demo PG, the “full” scene is loaded by default. And screenshots are incomplete.
But on line #80 it is possible to load the “mini” scene (which is a copy of the “full” scene but contains only two problematic meshes “Table-Seated-Top” and “Table-Standing-Top”).
Strangely, the issue doesn’t appear in the “mini” scene.

Please, can you help me to figure out why some meshes are not rendered on screenshots if they have invisible (and never rendered) instanced copies?

Maybe I forgot to reset some caches, mark something “as dirty” or anything else after I hid instanced meshes.

Thanks!

1 Like

Hello @dgorbash!

First of all, this is the best bug report ever :slight_smile:

The problem seems to be in the babylon file format or more in it’s loader. Once the scene was exported to glb and loaded back from the glb file everything works as expected:

I always recommend to use glb instead of anything else.


EDIT:
But if you want to stick to the babylon file format this doesn’t solve your problem. So let’s try to load the scene into the sandbox. A few moments later… It works… You can screenshot via RTT and the prefab mesh is visible on the screenshot. Let’s someone more experienced with the babylon format have a look at the issue. Maybe @Deltakosh ?

1 Like

Hi @roland ,

Thanks for your quick reply.

Thanks :smiley: I was just trying to make the issue is reproducible right away so you don’t have to spend time guessing where and what is not working for me.

Yes, but this happens because both “problematic” meshes are exported as independent Meshes and not as Mesh + InstancedMesh pair.

Currently, I use a different workaround: before taking a screenshot I do a single scene.render() call with all meshes visible. Next, I restore the meshes’ original visibility and render a screenshot as usual and it helps.

But still, I want to try to find the root cause of this issue and commit a PR if needed.

Is that possible to modify the scene before the render loop has started in the Sandbox? Probably not and this is why it also works.

The trick here is to make sure that InstancedMesh (“Table-Standing-Top” in the “full” demo scene) will be hidden by default and won’t be rendered even a single time.
After InstancedMesh is rendered for the very first time, something changes in the “source” mesh and it starts working too.
Probably some internal cache/state/buffer gets properly initialized which fixes the problem.

I was able to tack down the render workflow down to the ThinEngine._gl.drawElements() call for the missing mesh and every top-level check was successful.
I.e. subMeshe’s are ready, material is ready, a shader is compiled, everything is visible, isEnabled, etc.
So nothing should stop the engine from rendering that mesh. But somehow it is not rendered.

That’s not the case I believe. I checked the babylon file and there are instances of the affected meshes:

However I can see this in the console:

But this error is due to incorrectly setup RTT and after removing the RTT preview plane from your PG the error disappears but the issue with the screenshot remains.

I did the same. :slight_smile:


I’ve created a PG which eliminates the possibility of an error in the loader. It creates a mesh and an instance of it programatically. It fails to screenshot the meshes.

http://localhost:1338/#2UWGBA#3 ← change the base url

Let’s dig deeper :slight_smile:

If I call this before reading the pixels it works as expected.

I believe this line must not be removed:

@Evgeni_Popov Could you please check it?

Thanks!

I am collecting PRs so let me do the fix if you agree with my suggestion to put that one line back thx! :teddy_bear:

The WebGL errors are there because you are adding the rttPreview plane to the list of meshes to render into the RTT (hence the error :slight_smile: ) - Bug with screenshots and instanced meshes | Babylon.js Playground

Still trying to figure out what is going on with the table in the second example.

Following up on this - @roland’s place in code seems to be correct, but the cleaner solution should be to move the texture.render call to this position solves the issue:

2 Likes

Yes, there are instances in the babylon file. But when the scene is exported to glb (via Inspector) instances seem to be replaced by normal Meshes.
At least in your PG #1 (https://playground.babylonjs.com/#2UWGBA#1) I can see that both tabletops are reported as Mesh by the Inspector.

Anyway, I think this is not important anymore as you already crafted very minimal PG with just 2 meshes demonstrating the same bug.

Thank you so much for all your help. I appreciate that.

1 Like

Oooops :smiley:

That’s why I’ve removed the preview plane :smiley:

Hi @RaananW ,

Thanks for pointing out. Initially, I was adding only 2 meshes to the RTT but later decided to preview the entire scene and forgot to exclude the RTT itself.

Rookie mistake :upside_down_face:

@roland already pointed out a possible workaround with calling texture.render() but I have another one.
It can’t be considered a fix but maybe it will give you a clue why mesh is rendered correctly again.

In PG #6 (https://playground.babylonjs.com/#2UWGBA#6) if you add material.needDepthPrePass = true to line #17 screenshots will be correct.

1 Like

I assume it is not this flag but checkReadyOnEveryCall set to true on the material (which happens when you set the depth prepass flag)

1 Like

Yep, you’re absolutely right. I think I’ll use the checkReadyOnEveryCall as a temporary workaround instead of pre-rendering the entire scene with all instanced meshes.

Thanks!

2 Likes

Exactly that’s what I wrote :see_no_evil:

Yes, I agree it should be fixed this way!