How to bind an array of textures to a WebGL shader uniform?

前端 未结 1 805
遥遥无期
遥遥无期 2020-12-10 04:08

I need to handle many objects which share only a few textures. Defining and loading textures one by one manually (as described in another post on SO) does not feel right...

相关标签:
1条回答
  • 2020-12-10 04:33

    You have to index sampler arrays with constant values so you can do something like this

    #define numTextures 4
    
    precision mediump float;
    varying float v_textureIndex;
    uniform sampler2D u_textures[numTextures];
    
    vec4 getSampleFromArray(sampler2D textures[4], int ndx, vec2 uv) {
        vec4 color = vec4(0);
        for (int i = 0; i < numTextures; ++i) {
          vec4 c = texture2D(u_textures[i], uv);
          if (i == ndx) {
            color += c;
          }
        }
        return color;
    }
    
    void main() {
        gl_FragColor = getSampleFromArray(u_textures, int(v_textureIndex), vec2(0.5, 0.5));
    }
    

    You also need to tell it which texture units to use

    var textureLoc = gl.getUniformLocation(program, "u_textures");
    // Tell the shader to use texture units 0 to 3
    gl.uniform1iv(textureLoc, [0, 1, 2, 3]);
    

    The sample above uses a constant texture coord just to keep it simple but of course you can use any texture coordinates.

    Here's a sample:

    var canvas = document.getElementById("c");
    var gl = canvas.getContext('webgl');
    
    // Note: createProgramFromScripts will call bindAttribLocation
    // based on the index of the attibute names we pass to it.
    var program = webglUtils.createProgramFromScripts(
        gl, 
        ["vshader", "fshader"], 
        ["a_position", "a_textureIndex"]);
    gl.useProgram(program);
    var textureLoc = gl.getUniformLocation(program, "u_textures[0]");
    // Tell the shader to use texture units 0 to 3
    gl.uniform1iv(textureLoc, [0, 1, 2, 3]);
    
    var positions = [
          1,  1,  
         -1,  1,  
         -1, -1,  
          1,  1,  
         -1, -1,  
          1, -1,  
    ];
        
    var textureIndex = [
        0, 1, 2, 3, 0, 1,
    ];
    
    var vertBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(0);
    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    
    var vertBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(textureIndex), gl.STATIC_DRAW);
    gl.enableVertexAttribArray(1);
    gl.vertexAttribPointer(1, 1, gl.UNSIGNED_BYTE, false, 0, 0);
    
    var colors = [
        [0, 0, 255, 255],
        [0, 255, 0, 255],
        [255, 0, 0, 255],
        [0, 255, 255, 255],
    ];
    
    // make 4 textures
    colors.forEach(function(color, ndx) {
        gl.activeTexture(gl.TEXTURE0 + ndx);
        var tex = gl.createTexture();
       gl.bindTexture(gl.TEXTURE_2D, tex);
       gl.texImage2D(
          gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0,
          gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(color));
    });
    
    
    gl.drawArrays(gl.TRIANGLES, 0, positions.length / 2);
    canvas { border: 1px solid black; }
    <script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
    <script id="vshader" type="whatever">
        attribute vec4 a_position;
        attribute float a_textureIndex;
        varying float v_textureIndex;
        void main() {
          gl_Position = a_position;
          v_textureIndex = a_textureIndex;
        }    
    </script>
    <script id="fshader" type="whatever">
    #define numTextures 4
    precision mediump float;
    varying float v_textureIndex;
    uniform sampler2D u_textures[numTextures];
        
    vec4 getSampleFromArray(sampler2D textures[4], int ndx, vec2 uv) {
        vec4 color = vec4(0);
        for (int i = 0; i < numTextures; ++i) {
          vec4 c = texture2D(u_textures[i], uv);
          if (i == ndx) {
            color += c;
          }
        }
        return color;
    }
        
    void main() {
        gl_FragColor = getSampleFromArray(u_textures, int(v_textureIndex + 0.5), vec2(0.5, 0.5));
    }
    </script>
    <canvas id="c" width="300" height="300"></canvas>

    0 讨论(0)
提交回复
热议问题