textImage2D 加载 1 个组件纹理而不复制

textImage2D load 1 component texture without copy

我正在尝试从包含具有 1 个组件的图像的 ArrayBuffer 创建 2D 纹理。我想在不将其复制到 4 分量图像的情况下执行此操作。 我想知道 webglwebgl2 是否可行(对 webgl2 抱有更多希望)。

webgl 1

我试过这个

gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, pixelData);

我试过 gl.LUMINANCEgl.LUMINANCE_ALPHA,正如文档所说

Each color component is a ...

而不是 gl.ALPHA 例如它说的是:

Discards the red, green and blue components and reads the alpha component

由此看来很明显 gl.ALPHA 需要 4 个分量的图像,而 gl.LUMINANCEgl.LUMINANCE_ALPHA 只需要 1 个分量。
但是当我执行以下操作时,我收到一条错误消息,告诉我缓冲区太小。如果我传入一个大小为 4 倍的缓冲区,则不会出现错误,所以看起来它毕竟还需要一个 4 分量图像。

我也尝试过将宽度和高度除以 4,认为我可以在我的着色器上使用纹理坐标,但得出的结论是这可能不是插值的好主意,否则我将不得不自己做。

webgl 2

所以我对 webgl 1 没有抱太大期望,但我很确定使用 webgl2 会很容易。但不幸的是以下没有工作:

gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, this.image.width, this.image.height, 0, gl.RED, gl.UNSIGNED_BYTE, pixelData);

它正在做同样的事情,抱怨缓冲区太小,并且不会在缓冲区大 4 倍时抛出任何错误。

那么是否可以使用任何(或两个)版本,或者我注定要创建包含 4 个组件的临时图像?

您正在阅读哪种文档?您提供的 None 引述 ArrayBuffers 是正确的,其中 none 可以在 WebGL(1) 规范中找到。 ALPHALUMINANCE 都是单通道格式,每个像素只需要一个值,它们之间唯一有效的区别是通过哪些组件您可以在着色器中访问它们。然而,LUMINANCE_ALHPA 是一种 通道格式(第一个通道映射到 rgb,第二个通道映射到 a)。

现在回答您的实际问题,WebGL 1 和 2 都允许通过 texImage2D 提交单个组件数据,但是两者都默认为 UNPACK_ALIGNMENT 4(字节)。

Specifies the alignment requirements for the start of each pixel row in memory. The allowable values are 1 (byte-alignment), 2 (rows aligned to even-numbered bytes), 4 (word-alignment), and 8 (rows start on double-word boundaries).

OpenGL ES 3.0 Manpage

通过 pixelStorei 方法将 UNPACK_ALIGNMENT 设置为 1(字节)可解决问题。

ctx.pixelStorei(ctx.UNPACK_ALIGNMENT,1);

注意:这是一个 全局 设置(未绑定到活动纹理)并且可能导致纹理数据的性能损失,这些数据可以通过更好的对齐方式解包。因此,您应该确定每个纹理情况下的最佳解包对齐方式(考虑格式和数据类型)并相应地设置对齐方式。

var ctx=canvas.getContext('webgl');
// Create alpha texture
var tex = ctx.createTexture();
ctx.bindTexture(ctx.TEXTURE_2D, tex);
ctx.pixelStorei(ctx.UNPACK_ALIGNMENT,1);
ctx.texImage2D(ctx.TEXTURE_2D,0,ctx.ALPHA,2,2,0,ctx.ALPHA,ctx.UNSIGNED_BYTE,new Uint8Array([0,128,128,0]));
ctx.texParameteri(ctx.TEXTURE_2D,ctx.TEXTURE_MIN_FILTER,ctx.NEAREST);
ctx.texParameteri(ctx.TEXTURE_2D,ctx.TEXTURE_MAG_FILTER,ctx.NEAREST);

// Create screenspace shader program
var
  vshader=ctx.createShader(ctx.VERTEX_SHADER),
  fshader=ctx.createShader(ctx.FRAGMENT_SHADER),
  program=ctx.createProgram()
;
ctx.shaderSource(vshader,`
  attribute vec2 vPosition;
  void main() {
    gl_Position=vec4(vPosition,0,1);
  }
`);
ctx.compileShader(vshader);
ctx.attachShader(program,vshader);
ctx.shaderSource(fshader,`
  precision mediump float;
  uniform sampler2D tex;
  void main(){
    gl_FragColor=vec4(texture2D(tex,gl_FragCoord.xy*.05));
  }
`);
ctx.compileShader(fshader);
ctx.attachShader(program,fshader);
ctx.linkProgram(program);

// Create and bind screenspace geometry
ctx.bindBuffer(ctx.ARRAY_BUFFER, ctx.createBuffer());
ctx.bufferData(ctx.ARRAY_BUFFER, new Float32Array([
  -1,1, 1,1, 1,-1, -1,-1,
  -1,1, -1,-1, 1,-1, 1,1
]), ctx.STATIC_DRAW);
ctx.enableVertexAttribArray(0);
ctx.vertexAttribPointer(0,2,ctx.FLOAT,false,0,0);

// Render
ctx.useProgram(program);
ctx.drawArrays(ctx.TRIANGLE_FAN,0,4);
canvas {outline:1px solid red;position:absolute;left:50%;transform:translateX(-50%);}
html{background:#aaa;}
<canvas id="canvas"></canvas>