OpenGL 多渲染目标 - 黑色输出
OpenGL Multiple Render Target - Black output
我目前正在为我的学校开发一个游戏项目,使用 C++、SFML(用于 OpenGL 上下文创建,核心 3.3)和用于渲染部分的 OpenGL。
最近,我决定实现运动模糊。为此,我需要为帧缓冲区对象中渲染的每个像素提供一个运动矢量。所以在我的 FBO 中,我有两个纹理目标:
- 一个用于颜色输出
- 一个用于运动向量
颜色输出正确填充,但运动矢量始终为黑色。我使用 BuGLe 检查我的 FBO 纹理的内容。
我知道我的问题可能来自我的纹理创建,因为我不太了解内部纹理格式等等。
这是我创建的帧缓冲区:
void Framebuffer::create(int sizeX, int sizeY, bool depthBuffer, int repeatMode)
{
_texture = new GLuint[2];
std::memset(_texture, 0, sizeof(GLuint) * 2);
// Create OpenGL framebuffer
glGenFramebuffersEXT(1, &_fbo);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, _fbo);
// Create texture
glGenTextures(textNbr, _texture);
for (unsigned int i = 0; i < 2; ++i)
{
glBindTexture(GL_TEXTURE_2D, _texture[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeatMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeatMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Color - 4 float components
if (i == 0)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, sizeX, sizeY, 0, GL_RGBA, GL_FLOAT, NULL);
// Motion vector - 2 float components
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, sizeX, sizeY, 0, GL_RG, GL_FLOAT, NULL);
// Even if I use "glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, sizeX, sizeY, 0, GL_RGBA, GL_FLOAT, NULL);" for both texture, my motion texture is black
glBindTexture(GL_TEXTURE_2D, 0);
// getColorAttachment only return GL_COLOR_ATTACHMENTi_EXT
glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, getColorAttachment(i), GL_TEXTURE_2D, _texture[i], 0);
}
// Generate depth buffer
if (depthBuffer == true)
{
glGenRenderbuffersEXT(1, &_depth);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depth);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, sizeX, sizeY);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glFramebufferRenderbufferEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, _depth);
}
// Check completion
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
ogl::error("Can't complete framebuffer creation");
// Clean
unbind();
}
帧缓冲区绑定:
void Framebuffer::bind(void)
{
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, _fbo);
GLuint *attachment = new GLuint[2];
for (unsigned int i = 0; i < 2; ++i)
attachment[i] = getColorAttachment(i);
glDrawBuffers(2, attachment);
delete[] attachment;
}
然后我的片段着色器应该同时写入两个纹理目标:
#version 330
in vec4 Color0;
layout(location = 0) out vec4 FragmentColor;
layout(location = 1) out vec2 MotionVector;
void main(void)
{
FragmentColor = Color0;
MotionVector = vec2(1);
}
结果:
在评论中,您提到启用 GL_BLEND
会导致问题。这是可以预料的。片段着色器输出的颜色值始终是 4 分量 RGBA 向量,无论您的帧缓冲区采用何种格式。 GL 的以下阶段仍然适用于这 4 个组件。他们可能会在写入实际帧缓冲区时丢弃它们。如果你只是将它们声明为 vec2,其他组件只是 undefined。扩展到 (0,0,0,1) 形式的 vec4 是 not 在这种情况下应用(只有当你 read 来自输入属性或纹理,因此在这种情况下,您在评论中提到的 GL 文档的引用是无关紧要的)。因此,混合仅适用于未定义的 alpha 值,该值也可能意外地始终为 0。
但是,由于您只想混合真实的颜色缓冲区,我建议您使用 glEnablei()
而不是 glEnable()
来只为第一个绘制缓冲区启用混合。
另一种选择是使用 out vec4
并明确地将 1 写入 alpha - 在这种情况下您仍然可以使用 2 通道帧缓冲区。由不需要的缓冲区混合触发的额外帧缓冲区读取仍然是性能损失,所以我更喜欢第一个选项。
我目前正在为我的学校开发一个游戏项目,使用 C++、SFML(用于 OpenGL 上下文创建,核心 3.3)和用于渲染部分的 OpenGL。
最近,我决定实现运动模糊。为此,我需要为帧缓冲区对象中渲染的每个像素提供一个运动矢量。所以在我的 FBO 中,我有两个纹理目标:
- 一个用于颜色输出
- 一个用于运动向量
颜色输出正确填充,但运动矢量始终为黑色。我使用 BuGLe 检查我的 FBO 纹理的内容。
我知道我的问题可能来自我的纹理创建,因为我不太了解内部纹理格式等等。
这是我创建的帧缓冲区:
void Framebuffer::create(int sizeX, int sizeY, bool depthBuffer, int repeatMode)
{
_texture = new GLuint[2];
std::memset(_texture, 0, sizeof(GLuint) * 2);
// Create OpenGL framebuffer
glGenFramebuffersEXT(1, &_fbo);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, _fbo);
// Create texture
glGenTextures(textNbr, _texture);
for (unsigned int i = 0; i < 2; ++i)
{
glBindTexture(GL_TEXTURE_2D, _texture[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, repeatMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, repeatMode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Color - 4 float components
if (i == 0)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, sizeX, sizeY, 0, GL_RGBA, GL_FLOAT, NULL);
// Motion vector - 2 float components
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RG32F, sizeX, sizeY, 0, GL_RG, GL_FLOAT, NULL);
// Even if I use "glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, sizeX, sizeY, 0, GL_RGBA, GL_FLOAT, NULL);" for both texture, my motion texture is black
glBindTexture(GL_TEXTURE_2D, 0);
// getColorAttachment only return GL_COLOR_ATTACHMENTi_EXT
glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, getColorAttachment(i), GL_TEXTURE_2D, _texture[i], 0);
}
// Generate depth buffer
if (depthBuffer == true)
{
glGenRenderbuffersEXT(1, &_depth);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, _depth);
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, sizeX, sizeY);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
glFramebufferRenderbufferEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, _depth);
}
// Check completion
GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
ogl::error("Can't complete framebuffer creation");
// Clean
unbind();
}
帧缓冲区绑定:
void Framebuffer::bind(void)
{
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, _fbo);
GLuint *attachment = new GLuint[2];
for (unsigned int i = 0; i < 2; ++i)
attachment[i] = getColorAttachment(i);
glDrawBuffers(2, attachment);
delete[] attachment;
}
然后我的片段着色器应该同时写入两个纹理目标:
#version 330
in vec4 Color0;
layout(location = 0) out vec4 FragmentColor;
layout(location = 1) out vec2 MotionVector;
void main(void)
{
FragmentColor = Color0;
MotionVector = vec2(1);
}
结果:
在评论中,您提到启用 GL_BLEND
会导致问题。这是可以预料的。片段着色器输出的颜色值始终是 4 分量 RGBA 向量,无论您的帧缓冲区采用何种格式。 GL 的以下阶段仍然适用于这 4 个组件。他们可能会在写入实际帧缓冲区时丢弃它们。如果你只是将它们声明为 vec2,其他组件只是 undefined。扩展到 (0,0,0,1) 形式的 vec4 是 not 在这种情况下应用(只有当你 read 来自输入属性或纹理,因此在这种情况下,您在评论中提到的 GL 文档的引用是无关紧要的)。因此,混合仅适用于未定义的 alpha 值,该值也可能意外地始终为 0。
但是,由于您只想混合真实的颜色缓冲区,我建议您使用 glEnablei()
而不是 glEnable()
来只为第一个绘制缓冲区启用混合。
另一种选择是使用 out vec4
并明确地将 1 写入 alpha - 在这种情况下您仍然可以使用 2 通道帧缓冲区。由不需要的缓冲区混合触发的额外帧缓冲区读取仍然是性能损失,所以我更喜欢第一个选项。