Screenshots using custom #ifdef block within a PBRCustomMaterial

Hi together,

this may be a bit specific but I try my best to generalize my issue/question.

Qustion / Issue

I ran into a problem with CreateScreenshotUsingRenderTargetAsync() using a custom PBR shader that has its own #ifdef block.

If I update the material (custom defines habe to be updated) shortly before taking the screenshot the target object is invisible within the screenshot. If I repeat the action it works as intended.

I event tried calling CreateScreenshotUsingRenderTargetAsync within scene.onAfterRenderObservable without any luck. Another idea was to wait until material.isReady() is set to true. However as far as I can tell it is never set to true.

Is there a way to ensure the shader is fully rebuild before the screenshot is taken? Or did I handle the implementation of custom #ifdev blocks wrong and should use another approach?

Playground

https://playground.babylonjs.com/#VMKSIU#12

  • Basic custom material / shader with a useCustomDefine property
  • Shader code contains a custom #ifdef block that changes the color if set
  • Material overrides the Builder() function to update the shader defines depending on the state of useCustomDefine property
  • If useCustomDefine is enabled the shader will draw each pixel in green
  • The button updates the useCustomDefine and takes a screenshot

Desired Behavior: On the press of a button the material is updated and the screenshot contains the image of the updated sphere.

Observed Behavior: After pressing the button for the first time the screenshot does not contain any sphere. On the next clicks the screenshot works as expected.

Video:

Sadly this is not reproducible 100% of the time! Sometimes it will happen on every first click after I recompile the playground solution. Sometimes it works perfectly fine…

It looks like you would need for the shader to be compiled before using it as it is async.

You could force it to be synchronous with scene.getEngine().getCaps().parallelShaderCompile = undefined;

1 Like

Also, you can trigger a recompilation of the shader by calling sphere.resetDrawCache(), then use scene.executeWhenReady to call the screenshot function only when the shader compilation is finished:

2 Likes

You are awesome thanks!

I went with the resetDrawCache() option and it seems to be working great in my situation. :+1:. Only have to hide the flickering of my objects but that should not be a problem.

Setting paralellShaderCompile to undefined did work as well. But in my application I had to do as a global setting not right before the screenshot was taken because the structure is not as simplistic as in the playground. But nevertheless great suggestion.

1 Like