Search code examples
javascriptthree.jsshaderwebgltextures

webgl: geometry texture missing after gl.bindTexture


there is a textured cube in my scene, and it renders well untill i draw a quad, then cube tuxture is gone, here is the render flow:

  // draw the texture cube
  gl.render(scene, camera);
 
  // draw a quad on screen (for checking purpose), this tex is different from the cube  
  drawQuad(tex);

fake code inside drawQuad:

  setupVertexBuffer();
  gl.activeTexture(...);
  gl.bindTexture(...);
  gl.drawArrays(...);
  gl.bindTexture(null);

I guess the problem is gl.bindTexture will replace TEXTURE0 with null or other texture, so when render the textured cube, its texture will be missing or wrong (correct me if i am wrong) (shouldn't webgl re-bind texture for geometries everytime before rendering?)

so i tried to rebind texture to the textured cube like this right before render the scene:

  cube.map.texture = cubeTexture;
  gl.render(scene, camera);

but it won't work.

then i tried to backup old texture before bind new one in drawQuad like this:

const lastTex = gl.getParameter(gl.TEXTURE_BINDING_2D);

but lastTex is always undefined, so it won't work either.

what's the best way to let them both work correctly?


Solution

  • It turns out that sometimes just looking at the doc. is not enough. After carefully checking the source code of THREE.JS, I found WebGLState, which is responsible for texture binding in THREE.JS, so if I directly modify the texture binding externally will definitely mess up this WebGLState, so we can do this:

      const gl = renderer.getContext();
    
      // rendering with webgl shaders blah
      {
        setupVertexBuffer(...);
    
        gl.activeTexture(gl.TEXTURE0 + 0); // bind webgltexture tex0
        gl.bindTexture(gl.TEXTURE_2D, tex0); 
      
        gl.activeTexture(gl.TEXTURE0 + 1); // bind webgltexture tex1
        gl.bindTexture(gl.TEXTURE_2D, tex1);   
    
        gl.drawArrays(...);
    
        gl.bindTexture(gl.TEXTURE_2D, null); // deactive slot 1
    
        gl.activeTexture(gl.TEXTURE0 + 0);
        gl.bindTexture(gl.TEXTURE_2D, null); // deactive slot 0
      }
    
      // tell THREE.js that no textures are now bound to slot0 and slot1
      renderer.state.bindTexture(gl.TEXTURE_2D, null, 0);
      renderer.state.bindTexture(gl.TEXTURE_2D, null, 1);
    
      // now let THREE.JS render its scene
      renderer.render(scene, camera);
    
    

    The key point here is to tell THREE.js.WebGLState that the texture binding has changed so it can manage the states correctly for its rendering.

      renderer.state.bindTexture(gl.TEXTURE_2D, null, 0);
      renderer.state.bindTexture(gl.TEXTURE_2D, null, 1);
    

    done.