Three.js: Avoid Recompiling shaders

Created on 15 May 2017  路  8Comments  路  Source: mrdoob/three.js

So I'm trying to build an app, which requires me to switch between different shaders. Basically there are three different shaders, but textures are random. At the moment, I'm creating a new material each time, which I think webGL will have to recompile the shader program every time. So I'm wondering if there's a way I can save pre-compiled shaders, and each time just load them and switch to a specific texture.

I tried to use the code below, but after I switch the loaded shader is not working. I got a black screen instead of an error.

initMaterialSingleExperiment(initTexture) {
        const THREE = this.props.THREE;

        if (!this.exitingShaderMaterials['fSingle']){
          let texture = initTexture;
          if (!initTexture) {
              texture = this.defaultTexture;
          }

          const uniforms = {
              texture: {type: 't', value: texture},
          };

          this.exitingShaderMaterials['fSingle'] = new THREE.ShaderMaterial({
              uniforms: uniforms,
              vertexShader: Shaders['vPano'].src,
              fragmentShader: Shaders['fSingle'].src
          });
          this.exitingShaderMaterials['fSingle'].side = THREE.DoubleSide;
       }
       const shaderMaterial = this.exitingShaderMaterials['fSingle'];

        console.log('Single shader loaded....');
        return shaderMaterial;
    }
Question

Most helpful comment

At the moment, I'm creating a new material each time, which I think webGL will have to recompile the shader program every time.

Not necessarily. We reuse programs internally.

You can check renderer.info to see the amount of programs that have been created.

All 8 comments

At the moment, I'm creating a new material each time, which I think webGL will have to recompile the shader program every time.

Not necessarily. We reuse programs internally.

You can check renderer.info to see the amount of programs that have been created.

That's great. Does it mean the overhead time is purely from data transfer between GPU and CPU? So there's no way I can avoid that

Why are you creating a new material each time the texture is changed?

var texture = mesh.material.map;
mesh.material.map = texture2;
texture.dispose();

@RileyLee Also very useful: https://threejs.org/docs/index.html#manual/introduction/How-to-update-things (especially the material section)

That's great. Does it mean the overhead time is purely from data transfer between GPU and CPU? So there's no way I can avoid that

What kind of overhead are you getting? The only thing here that might be happening is your new texture being transferred / updated. There are a number of things you can do. One would be making a "dummy" render call that wouldn't write to screen, but would trigger the gpu transfer, and cache internally. I haven't tried this lately, but it should work.

@pailhead Available is renderer.compile( scene, camera ).

See http://threejs.org/examples/webgl_materials_compile.html.

@pailhead @Mugen87 Thanks!! I have another question about GPU textures. As I loaded more and more texture into the GPU, I wish to dispose the old textures to avoid crashes on mobile device. I think textures are transferred by calling renderer.render(scene, camera). Then I could find renderer.info.memory.texture increases by 1. And I guess textures are managed on GPU by three.js, which are just like those shader programs. However, when I call my this.previousTexture.dispose(), this won't release my GPU memory. The renderer.info.memory.texture variable stays the same. Note that my textures are stored in material.uniforms.texture as a type 't'. My question now is how do I find the correct reference like mesh.material.map, so I can correctly dispose my GPU memory wrapped in a shader material?

Please use stackoverflow for help.

Was this page helpful?
0 / 5 - 0 ratings