渲染到纹理时出现黑屏

Posted

技术标签:

【中文标题】渲染到纹理时出现黑屏【英文标题】:Black screen when rendering to a texture 【发布时间】:2021-07-04 19:02:43 【问题描述】:

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

    在第一遍中将场景绘制到我创建的帧缓冲区。 获取附加到创建的帧缓冲区的纹理并将其绘制到平面上,以便它可以显示在屏幕上。 做一些后期处理。

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

目前我无法让第 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);

【问题讨论】:

【参考方案1】:

这是一个常见的错误。 WebGL 1.0 基于 OpenGL ES 2.0。与 mipmap 相同的规则适用于纹理帧缓冲区附件。帧缓冲区纹理的大小必须是 2 的幂。请参阅 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,);

// [...]

【讨论】:

谢谢,这行得通!我在调试上花费的时间比我愿意承认的要长。

以上是关于渲染到纹理时出现黑屏的主要内容,如果未能解决你的问题,请参考以下文章

快速切换到 UITableViewController 时出现黑屏

OpenGL - 在 NVIDIA 卡上渲染到纹理时出现 FBO 黑屏

( Android ) 切换到新活动时出现黑屏

在 iPhone 上启动我的应用程序时出现黑屏

在 Flutter 中使用 Navigator.push() 时出现黑屏

使用 XCode 故事板切换视图控制器时出现黑屏