如何在 WebGL 中使用数据纹理

ocl*_*yke 4 shader textures webgl

我一直在学习像webglfundamentals这样的 WebGL 教程,但遇到了一个绊脚石 - 我相信我需要使用我创建的纹理将信息直接传递到片段着色器,但我似乎无法正确索引纹理。

目标是传递有关光源(位置和颜色)的信息,这些信息将被纳入片段颜色中。理想情况下,该信息的值和长度都是动态的。

再生产

我在这个小提琴中创建了问题的简化版本:WebGL - 数据纹理测试

这是一些代码。

一次性设置中,我们创建一个纹理,用数据填充它,并在该纹理上应用似乎最简单的设置(没有 mips,没有字节打包问题[?])

  // lookup uniforms
  var textureLocation = gl.getUniformLocation(program, "u_texture");

  // Create a texture.
  var texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);

  // fill texture with 1x3 pixels
  const level = 0;
  const internalFormat = gl.RGBA; // I've also tried to work with gl.LUMINANCE
  //   but it feels harder to debug
  const width = 1;
  const height = 3;
  const border = 0;
  const type = gl.UNSIGNED_BYTE;
  const data = new Uint8Array([
    // R,   G,   B, A (unused)    // : 'texel' index (?)
    64, 0, 0, 0, // : 0
    0, 128, 0, 0, // : 1
    0, 0, 255, 0, // : 2
  ]);
  const alignment = 1; // should be uneccessary for this texture, but 
  gl.pixelStorei(gl.UNPACK_ALIGNMENT, alignment); //   I don't think this is hurting
  gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, width, height, border,
    internalFormat, type, data);

  // set the filtering so we don't need mips and it's not filtered
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
Run Code Online (Sandbox Code Playgroud)

绘制序列中(只发生一次,但可以想象可以重复),我们强调程序应该使用我们的纹理

    // Tell the shader to use texture unit 0 for u_texture
    gl.activeTexture(gl.TEXTURE0);                  // added this and following line to be extra sure which texture is being used...
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.uniform1i(textureLocation, 0);
Run Code Online (Sandbox Code Playgroud)

最后,在片段着色器中,我们尝试可靠地使用一个“纹理元素”作为传达信息的一种方式。我似乎不知道如何可靠地检索我存储在纹理中的值。

precision mediump float;

// The texture.
uniform sampler2D u_texture;

void main() {

    vec4 sample_00 = texture2D(u_texture, vec2(0, 0)); 
    // This sample generally appears to be correct. 
    // Changing the provided data for the B channel of texel
    //   index 0 seems to add blue as expected

    vec4 sample_01 = texture2D(u_texture, vec2(0, 1));
    vec4 sample_02 = texture2D(u_texture, vec2(0, 2));
    // These samples are how I expected this to work since 
    //   the width of the texture is set to 1
    // For some reason 01 and 02 both show the same color

    vec4 sample_10 = texture2D(u_texture, vec2(1, 0));
    vec4 sample_20 = texture2D(u_texture, vec2(2, 0));
    // These samples are just there for testing - I don't think
    //   that they should work

    // choose which sample to display
    vec4 sample = sample_00;
    gl_FragColor = vec4(sample.x, sample.y, sample.z, 1);
}
Run Code Online (Sandbox Code Playgroud)

问题)

使用纹理是实现此目的的最佳方法吗?我也听说过传递向量数组的能力,但纹理似乎更常见。

你应该如何创建纹理?(特别是当我指定“宽度”和“高度”时,我应该指的是生成的纹理元素尺寸还是我将用来构造纹理的 gl.UNSIGNED_BYTE 元素的数量??texImage2D 文档

当不使用“变化”类型时,您应该如何索引片段着色器中的纹理?(即我只想要一个或多个特定纹理像素的值 - 没有插值[与顶点几乎没有关系])

其他资源

我暂时已经尽可能多地阅读了这方面的内容。这是一个非详尽的列表:

  • JMI Madison 表示他们已经解决了这个问题,但解决方案被埋藏在堆积如山的项目特定代码中
  • webglfundamentals教程非常接近 - 使用3x2 数据纹理-但它使用插值,并且似乎不能很好地转化为我的用例
  • 有人在谈论尝试使用 vec3 数组
  • 当然,我一直在尝试将我的工作与一些OpenGL文档(texImage2Dtexture2d)进行比较

编辑这是我刚刚找到的另一个资源:Hassles with array access in WebGL, and a Couple of workarounds。让我充满希望。

这真的很困扰我。

提前致谢!

gma*_*man 6

在 WebGL1 中寻址纹理中的各个像素使用以下公式

vec2 pixelCoord = vec2(x, y);
vec2 textureDimension = vec2(textureWidth, textureHeight)
vec2 texcoord = (pixelCoord + 0.5) / textureDimensions;
vec4 pixelValue = texture2D(someSamplerUniform, texcoord);
Run Code Online (Sandbox Code Playgroud)

因为纹理坐标是按边缘计算的。如果您有 2x1 纹理

1.0+-------+-------+
   |       |       |
   |   A   |   B   |
   |       |       |
0.0+-------+-------+
  0.0     0.5     1.0
Run Code Online (Sandbox Code Playgroud)

像素 A 中心的纹理坐标 = 0.25, 0.5。像素B中心的纹理坐标为0.75,0.5

如果您不遵循上面的论坛,而仅使用pixelCoord/textureDimensions,那么您将指向像素之间,并且数学错误将为您带来一个像素或另一个像素。

当然,如果您使用纹理作为数据,您可能还需要设置gl.NEAREST过滤。

在WebGL2中你可以使用texelFetch

ivec2 pixelCoord = ivec2(x, y);
int mipLevel = 0;
vec4 pixelValue = texelFetch(someSamplerUniform, texcoord, mipLevel);
Run Code Online (Sandbox Code Playgroud)

使用数据纹理的工作示例在这里

使用纹理是实现此目的的最佳方法吗?我也听说过传递向量数组的能力,但纹理似乎更常见。

做什么?目前尚不清楚你想做什么。每个像素都会有不同的光源吗?

你应该如何创建纹理?(特别是当我指定“宽度”和“高度”时,我应该指的是生成的纹理元素尺寸还是我将用来构造纹理的 gl.UNSIGNED_BYTE 元素的数量??texImage2D 文档)

做最简单或最需要的事情。例如,如果您想要提取的每项数据有 5 条数据,我可能会将每条数据放在纹理中的单独行上。然后我可以做

vec4 datum1 = texture2D(dataTexture, vec2(indexTexCoordX, rowTexCoordY0);
vec4 datum2 = texture2D(dataTexture, vec2(indexTexCoordX, rowTexCoordY1);
vec4 datum3 = texture2D(dataTexture, vec2(indexTexCoordX, rowTexCoordY2);
vec4 datum4 = texture2D(dataTexture, vec2(indexTexCoordX, rowTexCoordY3);
Run Code Online (Sandbox Code Playgroud)

其中indexTexCoordX和rowTexCoordY0-3是根据上面的forumla计算出来的。rowTexCoordY0-3 甚至可能是常量。

不过,纹理在维度上有限制,因此,如果您有更多数据,那么将适合一维,那么您必须将数据打包得更紧,并进行更多数学运算才能将其拉出来。

请注意,纹理具有缓存,因此理想情况下,您希望提取的数据接近之前提取的数据。如果你每次都跳过纹理来获取下一个值,你的性能就会下降。(当然,它可能仍然比替代解决方案更快,具体取决于您正在做什么)

当不使用“变化”类型时,您应该如何索引片段着色器中的纹理?(即我只想要一个或多个特定纹理像素的值 - 没有插值[与顶点几乎没有关系])

片段着色器唯一变化的输入是变量gl_FragCoord(写入的坐标像素)和gl_PointCoord,仅在绘制时可用POINTS。因此,您必须使用其中之一,否则所有其他值对于所有像素都是恒定的。