Question about preloading and compile background exr before use to prevent freeze

Hi, I’m experiencing jank/freeze on background exr enabled in my r3f ts app, and don’t know how to preload and compile it, wish someone can give me some advice on this issue. Thanks in advance.

I have two mode for an indoor scene: orbit and first person. When entering a scene it is the default orbit mode. And in firstPerson mode, I would like to use a very large exr image for background, while in orbit mode, I want to simply use color as background .

Yet if I load and compile the exr after entering first person mode, there will be a very significant jank/freeze, with my chrome devtool performance recorded trace, there is a long blocking shader compile caused by that texture.

As can be seen from the screenshot of the performance trace, there are lots of setProgram → WebGLProgram.getUniforms → onFirstUse following the getBackground → fromEquictangularTexture(I’m setting the texture as background in my custom FirstPersonControl component which is conditionally rendered depending on mode state)

// In FirstPersonControl.tsx, the component itself is conditionally rendered depending on mode state
<Suspense>
    {firstPersonModeOn && cameraInitialized && texture && <Environment
       background={true} // can be true, false or "only" (which only sets the background) (default: false)
       backgroundIntensity={0.5}
       map={texture}
       environmentIntensity={0}
      />
     }
</Suspense>

So I wrote a custom hook to load and compile the texture.

export function usePreLoadFpsEnvTexture () {
    const texture = useEnvironment({
        files: envTextureUrl,
    });

    const { gl, scene, camera } = useThree();
    useEffect(() => {
        if (!texture) return;
        texture.mapping = EquirectangularReflectionMapping;
        const tempScene = scene;
        // const tempScene = new Scene();
        tempScene.environment = texture;
        tempScene.environmentIntensity = 0.01;
        // tempScene.background = texture;
        gl.compile(tempScene, camera);
        // tempScene.environment = null;
        // tempScene.background = null;
    }, [texture, gl, camera]);
    return texture;
}

It works to preload the texture and compile it, now I have no jank/freeze after switching to firstperson mode. As can be seen from the trace, there is only one setProgram and onFirstUse after getBackground.

Yet, now the envmap is using this texture and the reflection of models in the scene is displaying the texture which is not desired. Yet if in this preload hook, use the texture to set background instead of the environment, the background will show up for a blink of moment and overwritten by my custom orbit mode background. Then, if switching to first person mode, it still jank for compiling the shader. I’ve also tried making a WebGLRenderTarget and only render result to it to pre compile the shader while not make the texture visible before entering firstPersonMode, yet it still jank on entering first person mode.

How may I simply preload and compile this texture to make it available in gpu before entering first person mode to use it as background, at the same time use another envmap for orbit mode.


And there is another issue:
For many of the models to be used in the scene, if using light only, even though I set light intensity to large number, the model still looks dark and not rendered with texture details.


Yet if using scene.environment(I’m using pmremGenerator.fromScene(new RoomEnvironment()).texture for the screenshot case as in threejs editor), the detail is displayed.

I wonder why the some of the details only react to environment? And in my in door scene, I’m trying to use environment(with low environmentIntensity) as well as lights( with lower intensity) to simulate lights and shadow as well as getting the details rendered. I wonder whether it is the best practice for rendering scene with these kind of models.

And at the same time, introducing environment also creates some articrafts on each frame, which makes kind of blink of noise shape in the render result. I wonder is there a way to get rid of it.


Any advice will be appreciated, thank you so much.

Load all stuffs, if all loadeed, run init_end. Then can show for user.

function meshes_frustum_visible(item,mode){


if(mode=="start"){
item.traverse(function(child){
if(child.frustumCulled!=undefined){
child.last_visible=child.visible;
child.visible=true;
child.last_frustumCulled=child.frustumCulled;
child.frustumCulled=false;
}
});
}
else{
item.traverse(function(child){
if(child.last_visible!=undefined){
child.visible=child.last_visible;
child.frustumCulled=child.last_frustumCulled;
delete child.last_visible;
delete child.last_frustumCulled;
}
});
}


}


function init_end(){


meshes_frustum_visible(scene,"start");
if(typeof scene_2!=="undefined"){ meshes_frustum_visible(scene_2,"start"); }


if(typeof composer!=="undefined"){
composer.render();
if(typeof scene_2!=="undefined"){
pass["render"].scene=scene_2;
composer.render();
pass["render"].scene=scene;
}
}
else{
renderer.render(scene,camera);
}


meshes_frustum_visible(scene,"end");
if(typeof scene_2!=="undefined"){ meshes_frustum_visible(scene_2,"end"); }


}

Sorry, I don’t really get it, what is the init_end?

I think my issue is more with the pre-compiling of the background texture to prevent jank when it should be rendered. For your code, I think it is something to do with controlling the visibility of the meshes.

init_end is just name of mine function. If you want compile only background texture. Then when texture will be loaded then render scene once and after it run your loop rendering code.

My usePreLoadFpsEnvTexture is doing the loading and compile before the whole scene is loaded. The following code does precompile and prevent jank.

export function usePreLoadFpsEnvTexture () {
    const texture = useEnvironment({
        files: envTextureUrl,
    });

    const { gl, scene, camera } = useThree();
    useEffect(() => {
        if (!texture) return;
        texture.mapping = EquirectangularReflectionMapping;
        const tempScene = scene;
        // const tempScene = new Scene();
        tempScene.environment = texture;
        tempScene.environmentIntensity = 0.01;
        // tempScene.background = texture;
        gl.compile(tempScene, camera);
        // tempScene.environment = null;
        // tempScene.background = null;
    }, [texture, gl, camera]);
    return texture;
}

Yet this texture is to be used in firstPersonMode after entering the scene and doing some interactions. For default mode, I don’t want it to be displayed as background or environment. So I modify my hook to use the texture and render it to renderTarget, it compiles. But after entering firstPersonMode, the jank is still there

Maybe jank because not compiled models.
Also try use
renderer.render(tempScene,camera);
instead
gl.compile(tempScene, camera);
And try delete all models to see why jank

I’m very sure it is the background texture:

I made a scene that in first person mode, user need to move to certain position to view the background via window, the jank will only happen at the moment of about to view the background.

And by removing the with that texture background, the jank is gone.

And from the performance trace, it can be seen there is long method calling from getBackground and fromEquirectangularTexture, where the only thing related to that in my scene is the background texture in firstPerson mode.

And I’m not using renderer.render(tempScene,camera); because I don’t want to render that texture in default mode, so I try to preload and compile it to make it ready before firstPerson mode, but not visible to use

Try use real scene instead tempScene.
gl.compile(tempScene, camera);

I’m using the real scene

actually I’m doing const tempScene = scene; as can be seen from the first post in this thread. it is named tempScene since I was switching between a new scene and the actual scene

Maybe this can help: three.js docs
I have no problem with freezing. All textures, materials, geometries compiled before user see it.