copyTexImage2D — GL ERROR :GL_INVALID_OPERATION : glCopyTexImage2D:

copyTexImage2D — GL ERROR :GL_INVALID_OPERATION : glCopyTexImage2D:

我正在尝试弄清楚如何使用 gl.copyTexImage2D 函数。 我有以下代码(虽然笨拙):

//--------- SETUP (not important) ---------------

//I use budo package to easily run browserify
var createContext = require('webgl-context');
var createShader = require('gl-shader');

//↓ here is webgl setup usual routine, using floats and simple one-triangle vertex shader
var gl = createContext({width: 2, height: 2});
gl.getExtension('OES_texture_float');
gl.getExtension('OES_texture_float_linear');

var shader = createShader(gl, `
precision mediump float;
attribute vec2 position;
varying vec2 uv;
void main (void) {
 gl_Position = vec4(position, 0, 1);
 uv = vec2(position.x * 0.5 + 0.5, position.y * 0.5 + 0.5);
}
`, `
precision mediump float;
uniform sampler2D image;
varying vec2 uv;
void main (void) {
 gl_FragColor = texture2D(image, uv);
}
`);

//fullscreen triangle
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 3, 3, -1]), gl.STATIC_DRAW);
shader.attributes.position.pointer();

//textures
var outTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, outTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.FLOAT, new Float32Array([1,1,1,1, 0,0,0,1, 0,0,0,1, 0,0,0,1]));

var sourceTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
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);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.FLOAT, new Float32Array([0,0,0,1, 1,1,1,1, 0,0,0,1, 0,0,0,1]));


//--------------- MAIN PART (important) ---------------

//then I setup custom framebuffer ↓
var framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, outTexture, 0);

gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.drawArrays(gl.TRIANGLES, 0, 3);

//here ↓ I am expecting to copy framebuffer’s output, which is `outTexture`, to `sourceTexture`
gl.bindTexture(gl.TEXTURE_2D, sourceTexture);
gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, w, h, 0);

//then I try to render shader again, with it’s own output as input
gl.drawArrays(gl.TRIANGLES, 0, 3);

//when I try to read pixels here ↓ I get the error below
gl.readPixels(0, 0, w, h, gl.RGBA, gl.FLOAT, pixels);

错误:[.CommandBufferContext]GL ERROR :GL_INVALID_OPERATION : glCopyTexImage2D:

不知道我做错了什么。

错误来自 gl.copyTexImage2D 而不是 gl.readPixels。在调用 gl.readPixels 之前你看不到它的原因是因为 WebGL 是一种命令驱动的语言。命令只有在出于各种原因必须执行时才会执行。 gl.flush 将强制命令在某个时间点执行。 gl.readPixels 还强制执行命令,因为需要使用命令的结果来读取像素。

至于错误你需要提供更多的代码。代码按原样通过 gl.copyTexImage2D 工作,这意味着您从中得到的错误与您未显示的某些代码有关。要么是你创建的纹理有误,要么是 w 和 h,要么是时髦的值,要么是什么

我自己在下面试了一下它有效但指出了另一个错误。您无法在 WebGL 中使用 gl.readPixels 读取浮点数。切换到 UNSIGNED_BYTE 有效

var gl = document.getElementById("c").getContext("webgl");
var w = 300;
var h = 150;

var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
gl.useProgram(programInfo.program);

var arrays = {
  position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0],
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);

// make a renderable npot texture
function createRenderableTexture(gl, w, h) {
 var tex = gl.createTexture();
 gl.bindTexture(gl.TEXTURE_2D, tex);
 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
 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);
 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
 return tex;
}

var outTexture = createRenderableTexture(gl, w, h);
var framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, outTexture, 0);

// render something to it
gl.clearColor(0,1,0,1);  // green
gl.clear(gl.COLOR_BUFFER_BIT);

// copy the framebuffer to the texture
var sourceTexture = createRenderableTexture(gl, w, h)
gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, w, h, 0);

// draw to canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);

// clear to red
gl.clearColor(1,0,0,1); 
gl.clear(gl.COLOR_BUFFER_BIT);

// Since we cleared to red and the texture is filled with green
// the result should be green
gl.drawArrays(gl.TRIANGLES, 0, 6);

var pixels = new Uint8Array(w * h * 4);
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
log("pixel 0: " 
        + pixels[0] + "," 
        + pixels[1] + "," 
        + pixels[2] + "," 
        + pixels[3]);

function log(msg) {
  var div = document.createElement("pre");
  div.appendChild(document.createTextNode(msg));
  document.body.appendChild(div);
}
<script src="https://twgljs.org/dist/twgl.min.js"></script>
<script id="vs" type="notjs">
attribute vec4 position;
varying vec2 v_uv;

void main() {
  gl_Position = position;
  v_uv = position.xy * 0.5 + 0.5;  
}
</script>
<script id="fs" type="notjs">
precision mediump float;

varying vec2 v_uv;
uniform sampler2D u_texture;

void main() {
  gl_FragColor = texture2D(u_texture, v_uv);
}
  </script>
<canvas id="c"></canvas>

另一方面,FLOAT 纹理应该不起作用。他们对我有用,但据我所知,这是我系统上的错误,不是你的。

特别是 FLOAT WebGL 默认不允许使用纹理。您可以通过启用 OES_texture_float 扩展来打开它们,但是 that extension specifically says

  1. Should CopyTexImage2D, CopyTexSubImage{2D|3D} be supported for textures with half-float and float channels?

    RESOLUTION: No.

I filed a bug for Chrome

您可以尝试通过自己实施 copyTexImage2D 来解决该问题。基本上将要作为源纹理复制的纹理附加到某些着色器和四边形。将要复制到的纹理附加到帧缓冲区对象。由于它是 FLOAT 纹理,您需要调用 checkFramebufferStatus 并检查它 returns FRAMEBUFFER_COMPLETE 以确保您的 GPU/Driver 实际上支持浮点纹理作为附件。然后用一个简单的着色器绘制你的四边形,有效地将你的纹理从 src 复制到 dst。