COLOR_ATTACHMENT's - 如何将多个纹理渲染为帧缓冲区对象内的颜色附件?
Posted
技术标签:
【中文标题】COLOR_ATTACHMENT\'s - 如何将多个纹理渲染为帧缓冲区对象内的颜色附件?【英文标题】:COLOR_ATTACHMENT's - How to render to multiple textures as color attachments inside a Framebuffer Object?COLOR_ATTACHMENT's - 如何将多个纹理渲染为帧缓冲区对象内的颜色附件? 【发布时间】:2015-05-27 13:54:55 【问题描述】:我试图以COLOR_ATTACHMENT
s 渲染多个纹理,但没有成功。我从显示它们中得到的只是一个黑屏(带有红色透明填充),这意味着我的纹理已被读取但是“空的”。
我的伪代码是:将 3 个纹理附加到 FBO,纹理索引分别为 1、2 和 3,颜色附件分别为 0、1 和 2。作为一个测试用例,我尝试将我的场景渲染为 3 种颜色的附件,因此它们应该包含相同的确切数据。然后在着色器通道 2(使用 2Dsampler)读取这些纹理中的任何一个并将它们显示在四边形上。
我对这 2 个额外颜色附件的最初意图是使用 GPU 乒乓技术将它们用作随机数据缓冲区。到目前为止,我只是将它们用作纹理克隆以进行测试。
当尝试从 GL_TEXTURE1
(COLOR_ATTACHMENT0
) 读取时,一切正常,但从其他 2 个(黑屏)读取时却不行。
代码:
// Texture indices - inside a 'myGlut' struct
GLenum skyboxTextureIndex = GL_TEXTURE0;
GLenum colorTextureIndex = GL_TEXTURE1;
unsigned int colorTextureIndexInt = 1;
GLenum depthTexture1Index = GL_TEXTURE2;
unsigned int depthTexture1IndexInt = 2;
GLenum depthTexture2Index = GL_TEXTURE3;
unsigned int depthTexture2IndexInt = 3;
//** Below is inside 'main()' **//
// Create frame buffer
myGlut.frameBuffer = glutils::createFrameBuffer();
// Create texture to hold color buffer
glActiveTexture(myGlut.colorTextureIndex);
glBindTexture(GL_TEXTURE_2D, myGlut.colorTexture);
myGlut.colorTexture = glutils::createTextureAttachment(myGlut.camera -> getRenderResizedWidthPx(), myGlut.camera -> getRenderResizedHeightPx());
glutils::bindTextureAttachment(GL_COLOR_ATTACHMENT0, myGlut.colorTexture);
// Create 1st texture to hold depth buffer wannabe :>
glActiveTexture(myGlut.depthTexture1Index);
glBindTexture(GL_TEXTURE_2D, myGlut.depthTexture1);
myGlut.depthTexture1 = glutils::createTextureAttachment(myGlut.camera -> getRenderResizedWidthPx(), myGlut.camera -> getRenderResizedHeightPx());
glutils::bindTextureAttachment(GL_COLOR_ATTACHMENT1, myGlut.depthTexture1);
// Create 2nd texture to hold depth buffer wannabe :>
glActiveTexture(myGlut.depthTexture2Index);
glBindTexture(GL_TEXTURE_2D, myGlut.depthTexture2);
myGlut.depthTexture2 = glutils::createTextureAttachment(myGlut.camera -> getRenderResizedWidthPx(), myGlut.camera -> getRenderResizedHeightPx());
glutils::bindTextureAttachment(GL_COLOR_ATTACHMENT2, myGlut.depthTexture2);
// Check FBO
if (!glutils::checkFBOStatus()) return 0;
带有glutils::
函数
// Clear screen
void glutils::clearScreen (float r, float g, float b, float a)
glClearColor(r, g, b, a);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Bind select framebuffer
void glutils::bindFrameBuffer(int frameBuffer, int width, int height)
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glViewport(0, 0, width, height);
// Create frame buffer
GLuint glutils::createFrameBuffer()
GLuint frameBuffer;
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
return frameBuffer;
// Create a texture attachment
GLuint glutils::createTextureAttachment(int width, int height)
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
return texture;
// Bind a texture attachment to select framebuffer
void glutils::bindTextureAttachment (GLenum colorAttachment, GLuint texture)
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachment, GL_TEXTURE_2D, texture, 0);
// Check current frame buffer status
bool glutils::checkFBOStatus ()
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cerr << "##### ERROR : Frambuffer not complete... #####" << std::endl;
return false;
else return true;
然后是过剩显示函数:
// Clear screen
glutils::clearScreen(1.f, 0.f, 0.f, 1.f);
// Bind to custom framebuffer
glutils::bindFrameBuffer(myGlut.frameBuffer, myGlut.camera -> getScreenWidthPx(), myGlut.camera -> getScreenHeightPx());
// Set draw context
GLuint drawBuffers[2];
if (myGlut.depthTextureSwitch) drawBuffers[0] = GL_COLOR_ATTACHMENT0;
drawBuffers[1] = GL_COLOR_ATTACHMENT2;
else drawBuffers[0] = GL_COLOR_ATTACHMENT0;
drawBuffers[1] = GL_COLOR_ATTACHMENT1;
glDrawBuffers(2, drawBuffers);
// Use main program and bind uniforms
glUseProgram(myGlut.theProgram);
myGlut.refreshUniformsPass_1();
// Draw quad to sample
glutils::drawQuad();
// Unbind custom framebuffer -> use default (screen)
glutils::unbindCurrentFrameBuffer(myGlut.camera -> getScreenWidthPx(), myGlut.camera -> getScreenHeightPx());
// Use secondary program and bind uniforms
glUseProgram(myGlut.theProgram2);
myGlut.refreshUniformsPass_2();
// Draw quad to apply texture to
glutils::drawQuad();
// Switch
myGlut.depthTextureSwitch = !myGlut.depthTextureSwitch;
// Display & loop
glutSwapBuffers();
glutPostRedisplay();
相关的统一绑定->通过1
glUniform1i(glGetUniformLocation(myGlut.theProgram, "depthTexture"), !myGlut.depthTextureSwitch ? myGlut.depthTexture2IndexInt : myGlut.depthTexture1IndexInt);
相关着色器代码 -> Pass 1
layout (location = 0) out vec4 outputColor;
layout (location = 1) out vec4 outputDepth1;
layout (location = 2) out vec4 outputDepth2;
uniform sampler2D depthTexture;
void main()
// ...
outputColor = someColor;
outputDepth1 = someColor;
outputDepth2 = someColor;
相关的统一绑定 -> pass 2
glUniform1i(glGetUniformLocation(myGlut.theProgram2, "texFramebuffer"), myGlut.depthTextureSwitch ? myGlut.depthTexture1IndexInt : myGlut.depthTexture2IndexInt);
使用相关的着色器代码 -> 通过 2
uniform sampler2D texFramebuffer;
out vec4 outputColor;
// ...
void main()
outputColor = texture(texFramebuffer, vec2(gl_FragCoord.x / screenWidthPx * resRatio, gl_FragCoord.y / screenHeightPx * resRatio));
简而言之:我的GL_TEXTURE0
持有场景,而GL_TEXTURE1
和GL_TEXTURE2
是黑色的。为什么?
【问题讨论】:
基于glDrawBuffers()
调用,您只渲染到 2 个纹理。看起来对于第 2 步,您使用的是您未渲染到的纹理。
确实,我正在读取我没有渲染到的纹理,但这是(预期的)并且因为“乒乓”渲染技术。所以这意味着我应该只在第一帧得到黑屏。因为然后在第 2 帧我渲染到纹理 2(打开)并从纹理 1 读取 - 我在第 1 帧渲染到等等......真正的问题是我的渲染一直是黑色的,好像GL_TEXTURE1
和 @ 987654337@从不渲染到。为什么 ?如果我尝试不断地从GL_TEXTURE1
或GL_TEXTURE2
阅读,我会得到一个持续的黑屏。
只有一个纹理采样(texFramebuffer),你不需要不同的ActiveTexture(tex槽),这很混乱......
是的,你是对的。但我需要在着色器通道 1 中读取 depthTexture1
或 depthTexture2
。因此使用了 3 个纹理槽。
您需要与#textureNames 一样多的槽,换句话说,#slot = #textures 在单遍中采样。但是如果你真的想绑定 3 个纹理以避免在 pass 之间调用 bindTexture,你可以,但是你必须声明 3 个不同的制服
【参考方案1】:
我终于找到了罪魁祸首。因为我在循环的display()
函数中绑定了帧缓冲区,所以在绑定FBO 之后我还需要绑定纹理附件。改为
// Bind to custom framebuffer
glutils::bindFrameBuffer(myGlut.frameBuffer, myGlut.camera -> getScreenWidthPx(), myGlut.camera -> getScreenHeightPx());
// Bind to select attachments
glutils::bindTextureAttachment(GL_COLOR_ATTACHMENT0, myGlut.colorTexture);
if (!myGlut.depthTextureSwitch) glutils::bindTextureAttachment(GL_COLOR_ATTACHMENT1, myGlut.depthTexture1);
else glutils::bindTextureAttachment(GL_COLOR_ATTACHMENT1, myGlut.depthTexture2);
允许我渲染所有需要的颜色附件。
【讨论】:
实际上,FBO 附件保持不变,无需重新设置。至少,如果您不自己将它们分离到某个地方。从目前给出的代码片段很难判断到底发生了什么。 我正在实现一个 GPU 乒乓球 (pixelnerve.com/v/2010/07/20/pingpong-technique),所以我需要每帧切换附件。例如:从 0 和 1 读取 + 写入 2 @ 帧 1,从 0 和 2 读取 + 写入 1 @ 帧到等等......问题是:我无法读取和写入纹理中的纹理单程因此是乒乓解决方法。 无需重新附加。您所需要的只是切换绘图缓冲区。顺便说一句,根据您实际执行的操作,甚至可以在同一通道中读取和写入相同的纹理。 GL 在某些特定条件下确实允许这样做。 切换绘图缓冲区正是我开始做的,但由于某种原因它对我不起作用...... I.E.绑定main()
内的所有内容 - 纹理和颜色附件 - 并切换 display()
内的绘图缓冲区。没用。以上是关于COLOR_ATTACHMENT's - 如何将多个纹理渲染为帧缓冲区对象内的颜色附件?的主要内容,如果未能解决你的问题,请参考以下文章