【问题标题】:How to bind an array of textures to a WebGL shader uniform?如何将一组纹理绑定到 WebGL 着色器制服?
【发布时间】:2013-10-25 14:41:24
【问题描述】:

我需要处理许多只共享少量纹理的对象。手动一一定义和加载纹理(如another post on SO 中所述)感觉不对……更是如此,因为WebGL 中没有switch (index) {case:...} 语句。

所以我想将用于顶点的纹理作为顶点属性传递,并将这个数字用作片段着色器中某些“纹理数组”的索引。但是OpenGL wiki on Samplers(对于 WebGL 来说并不是完美的参考,但我找到的那个)说:

采样器的变量只能以两种方式之一定义。可以定义为函数参数,也可以定义为统一变量。

uniform sampler2D texture1;

对我来说,这听起来像是我没有采样器阵列。我已经阅读了几页关于纹理单元的内容,但直到现在,这对我来说仍然是个谜。

在上面引用的 SO 帖子中,Toji 暗示了一个解决方案,但想要一个单独的问题 - 瞧!

谢谢,诺比

PS:我知道使用“纹理图集”的另一种可能性 - 如果这更有效或更简单 - 我很乐意听到经验!

【问题讨论】:

  • 属性标记是 IMO 的一个好主意。我知道这违背了一个人更好的直觉,但是手动加载和 if(aId == 1)c = texture2D(sampler1,p); else if(aID == 2)c = texture2D(sampler2... 至少可以工作。:-/ 纹理单元就像纹理的“便笺簿寄存器”。一些 GL 操作,包括绘图,需要将 1 个纹理与 1 关联纹理单元,我们可以根据需要分配它们。

标签: textures webgl


【解决方案1】:

你必须用常量值索引采样器数组,这样你才能做这样的事情

#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));
}

你还需要告诉它使用哪些纹理单元

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

为了简单起见,上面的示例使用了一个恒定的纹理坐标,当然您也可以使用任何纹理坐标。

这是一个示例:

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>

【讨论】:

  • 非常感谢,这看起来很有希望。我只是想知道为什么你需要写if (ndx==0) {...textures[0]... 而不仅仅是写textures[ndx]?并完全摆脱功能getSampleFromArray?我一放假回来就试试这个——
  • 因为基于 OpenGL ES 2.0 的 WebGL 不允许使用除常量索引表达式之外的任何内容来索引采样器数组。请参阅 GLSL 1.0.17 附录 A 第 5 节 [khronos.org/registry/gles/specs/2.0/…,这意味着您可以使用常量或基于常量的循环索引进行索引,但仅此而已。
  • 您可以执行其他操作,例如循环遍历所有纹理并乘以颜色。我不知道那是更快还是更慢。换句话说uniform vec4 u_colors[4]; ... vec4 color = vec4(0,0,0,0); for (int i = 0; i &lt; 4; ++i) { color += texture2D(u_samplers[i], uv) * u_colors[i]; }; gl_FragColor = color; ... 然后你可以通过设置 u_color 来关闭单个纹理。在你的情况下,我想你也可以做if (i == (int)(v_textureIndex)) { color = texture2D(...) }
  • 就像一个魅力。我花了一段时间才弄清楚gl.uniform1iv(textureLoc, [0, 1, 2, 3]); 是通过u_textures[3] 设置u_textures[0](至少-我认为它正在这样做)。并且任何时候都限制使用 32 个纹理(因此,纹理单元),对吗?非常感谢
  • 是的,gl.uniform1iv 正在设置 u_textures[0] -&gt; [3]。纹理单元的限制是特定于 GPU 的。 WebGL 至少需要 8 个。您可以致电 gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) 了解您在用户的机器上获得了多少。顶点着色器有自己的限制gl.getParameter(gl.VERTEX_MAX_TEXTURE_IMAGE_UNITS),可以为 0。
猜你喜欢
  • 2013-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-27
  • 2013-11-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多