渲染到纹理时黑屏

Black screen when rendering to a texture

我正在尝试执行以下操作:

  1. 在第一遍中将场景绘制到我创建的帧缓冲区。
  2. 将附加到创建的帧缓冲区的纹理绘制到一个平面上,以便它可以显示在屏幕上。
  3. 做一些post-处理。

仅使用默认的帧缓冲区,场景如下所示:

目前我无法让第 1 部分和第 2 部分正常工作。我得到的只是一个黑屏。但是,飞机已正确放置在场景中(通过使用 gl.LINE_STRIP 查看线框来确认)。我不确定这是因为我在代码中犯了错误,还是因为不了解帧缓冲区的工作原理(webgl 对我来说是新手)。

相关代码摘录如下:

// ======== FRAMEBUFFER PHASE ======== //
const framebuffer = gl.createFramebuffer();

gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

const texture = gl.createTexture();

gl.bindTexture(gl.TEXTURE_2D, texture);

gl.texImage2D(
  gl.TEXTURE_2D,
  0,
  gl.RGB,
  canvas.clientWidth,
  canvas.clientHeight,
  0,
  gl.RGB,
  gl.UNSIGNED_BYTE,
  null // don't fill it with pixel data just yet
);

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);

gl.framebufferTexture2D(
  gl.FRAMEBUFFER,
  gl.COLOR_ATTACHMENT0,
  gl.TEXTURE_2D,
  texture,
  0
);

// ======== END FRAMEBUFFER PHASE ======== //

// =========== RENDERBUFFER PHASE ============== //

const renderBuffer = gl.createRenderbuffer();

gl.bindRenderbuffer(gl.RENDERBUFFER, renderBuffer);

gl.renderbufferStorage(
  gl.RENDERBUFFER,
  gl.DEPTH_STENCIL,
  canvas.clientWidth,
  canvas.clientHeight
);

gl.framebufferRenderbuffer(
  gl.FRAMEBUFFER,
  gl.DEPTH_STENCIL_ATTACHMENT,
  gl.RENDERBUFFER,
  renderBuffer
);

// =========== END RENDERBUFFER PHASE ============== //

// =========== CHECK FRAMEBUFFER STATUS ============== //

const framebufferState = gl.checkFramebufferStatus(gl.FRAMEBUFFER);

if (framebufferState !== gl.FRAMEBUFFER_COMPLETE) {
  throw new Error(
    `Framebuffer status is not complete: ${framebufferState}`
  );
}

// =========== END CHECK FRAMEBUFFER STATUS ============== //

// =========== FIRST PASS RENDERING ============ //

gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.clearColor(0.1, 0.1, 0.1, 1.0);
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
gl.enable(gl.DEPTH_TEST);

// this sets up the green quad and draws it to the screen
const objectModel = setupObjectModel({
  position: [100.0, -10.0, 0.0],
  colour: [0.734345265462, 0.89624528765, 0.9868589658, 1.0],
  gl,
  canvas,
});

objectModel.draw({
  shaderProgram: mainShaderProgram,
  camera: updatedCamera,
  currentTime,
  deltaTime,
});

// =========== END FIRST PASS RENDERING ============ //

// =========== SECOND PASS RENDERING ============ //

// back to rendering with the default framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.disable(gl.DEPTH_TEST);

gl.useProgram(frameBufferShaderProgram);

// prettier-ignore
const verts = [
  // positions  // texCoords
  -1.0,  1.0,  0.0, 1.0,
  -1.0, -1.0,  0.0, 0.0,
  1.0, -1.0,  1.0, 0.0,

  -1.0,  1.0,  0.0, 1.0,
  1.0, -1.0,  1.0, 0.0,
  1.0,  1.0,  1.0, 1.0
];

// prettier-ignore-end
const screenQuad = gl.createBuffer();

gl.bindBuffer(gl.ARRAY_BUFFER, screenQuad);

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);

const aPosAttributeLocation = gl.getAttribLocation(
  frameBufferShaderProgram,
  "aPos"
);

gl.enableVertexAttribArray(aPosAttributeLocation);
gl.vertexAttribPointer(
  aPosAttributeLocation,
  2,
  gl.FLOAT,
  false,
  Float32Array.BYTES_PER_ELEMENT * 4,
  0
);

const aTexCoordsAttributeLocation = gl.getAttribLocation(
  frameBufferShaderProgram,
  "aTexCoords"
);

gl.enableVertexAttribArray(aTexCoordsAttributeLocation);
gl.vertexAttribPointer(
  aTexCoordsAttributeLocation,
  2,
  gl.FLOAT,
  false,
  Float32Array.BYTES_PER_ELEMENT * 4,
  Float32Array.BYTES_PER_ELEMENT * 2
);

const screenTexture = gl.getUniformLocation(
  frameBufferShaderProgram,
  "screenTexture"
);

gl.uniform1i(screenTexture, 0);

gl.drawArrays(gl.TRIANGLES, 0, 6);

这里是帧缓冲着色器程序:

// vertex shader 
precision mediump float;

attribute vec2 aPos; 
attribute vec2 aTexCoords; 

varying vec2 TexCoords; 

void main() {
    gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);
    TexCoords = aTexCoords;
}
// fragment shader
precision mediump float;

uniform sampler2D screenTexture; 
varying vec2 TexCoords;

void main() {
    // the texture coordinates are fine here, it's the screen texture that's the issue
    gl_FragColor = texture2D(screenTexture, TexCoords.xy);
}

这是一个常见的错误。 WebGL 1.0 is base on OpenGL ES 2.0. The same rules apply to texture framebuffer attachments as to mipmaps. The size of a framebuffer texture must be a power of 2. See Texture Completeness and Non-Power-Of-Two Textures.

创建一个大小等于 2 的幂的帧缓冲区(例如 1024x1024):

gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);

// [...]

gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1024, 1024, 0, gl.RGBA, gl.UNSIGNED_BYTE,   null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);

// [...]

gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, 1024, 1024);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, renderBuffer);

// [...]

gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.viewport(0, 0, 1024, 1024);

// [...]

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight,);

// [...]