如何将一组纹理绑定到 WebGL 着色器统一? [英] How to bind an array of textures to a WebGL shader uniform?

查看:22
本文介绍了如何将一组纹理绑定到 WebGL 着色器统一?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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... even more so since there's no switch (index) {case:...} statement in WebGL.

所以我想传递用于顶点的纹理作为顶点属性,并使用这个数字作为片段着色器中某些纹理数组"的索引.但是 采样器上的 OpenGL wiki(不是 WebGL 的完美参考,但是我找到的一个)说:

So I wanted to pass the texture to use for a vertex as a vertex attribute, and use this number as an index into some "array of textures" in the fragement shader. But the OpenGL wiki on Samplers (not quite the perfect reference for WebGL, but the one I found) says:

采样器的变量只能以两种方式之一定义.它可以定义为函数参数或统一变量.

A variable of sampler can only be defined in one of two ways. It can be defined as a function parameter or as a uniform variable.

uniform sampler2D texture1;

uniform sampler2D texture1;

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

That to me sounds like I can have no array of samplers. I've read a few pages on texture units, but until now, that remains a mystery to me.

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

In the SO post cited above, Toji hinted at a solution, but wanted a separate question - voila!

谢谢,诺比

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

PS: I know the other possibility of using a "texture atlas" - if this is more efficient or less complicated - I'd be happy to hear experiences!

推荐答案

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

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.

这是一个示例:

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>

这篇关于如何将一组纹理绑定到 WebGL 着色器统一?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆