延迟渲染不显示 GBuffer 纹理
Deferred Rendering not Displaying the GBuffer Textures
我正在尝试在个人学习开发的引擎中实现延迟渲染,但在渲染 GBuffer 中的所有纹理以进行检查时,我无法理解我做错了什么如果执行没问题。
问题是我目前有一个带有 3 种颜色附件的帧缓冲区,用于 GBuffer 的不同纹理(颜色、法线和位置),我初始化如下:
glCreateFramebuffers(1, &id);
glBindFramebuffer(GL_FRAMEBUFFER, id);
std::vector<uint> textures;
textures.resize(3);
glCreateTextures(GL_TEXTURE_2D, 3, textures.data());
for(size_t i = 0; i < 3; ++i)
{
glBindTexture(GL_TEXTURE_2D, textures[i]);
if(i == 0) // For Color Buffer
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i], 0);
}
GLenum color_buffers[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers((GLsizei)textures.size(), color_buffers);
uint depth_texture;
glCreateTextures(GL_TEXTURE_2D, 1, &depth_texture);
glBindTexture(GL_TEXTURE_2D, depth_texture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, width, height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0);
bool fbo_status = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
ASSERT(fbo_status, "Framebuffer Incompleted!");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
这没有报告任何错误,而且它似乎可以正常工作,因为前向渲染器的帧缓冲区可以正确渲染。然后,在渲染时,我 运行 绑定帧缓冲区并清除颜色和深度缓冲区后的下一个代码:
camera_buffer->Bind();
camera_buffer->SetData("ViewProjection", glm::value_ptr(viewproj_mat));
camera_buffer->SetData("CamPosition", glm::value_ptr(glm::vec4(view_position, 0.0f)));
camera_buffer->Unbind();
for(Entity& entity : scene_entities)
{
shader->Bind();
Texture* texture = entity.GetTexture();
BindTexture(0, texture);
shader->SetUniformMat4("u_Model", entity.transform);
shader->SetUniformInt("u_Albedo", 0);
shader->SetUniformVec4("u_Material.AlbedoColor", entity->AlbedoColor);
shader->SetUniformFloat("u_Material.Smoothness", entity->Smoothness);
glBindVertexArray(entity.VertexArray);
glDrawElements(GL_TRIANGLES, entity.VertexArray.index_buffer.count, GL_UNSIGNED_INT, nullptr);
// Shader, VArray and Textures Unbindings
}
因此,使用此代码,我设法渲染使用 ImGui::Image 函数创建的 3 个纹理,方法是在 0、1 或 2 之间切换纹理索引,如下所示:
ImGui::Image((ImTextureID)(fbo->textures[0]), viewport_size, ImVec2(0, 1), ImVec2(1, 0));
现在,颜色纹理(在索引 0 处)完美运行,如下图所示:
但是在渲染法线和位置纹理(索引 2 和 3)时,我没有结果:
有人看到我做错了什么吗?因为我已经花了几个小时来研究它,但我看不到它。我在 RenderDoc 上 运行 看不出有什么不对,RenderDoc 中显示的纹理与引擎中显示的纹理相同。
我在渲染实体时使用的顶点着色器是下一个:
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
layout(location = 2) in vec3 a_Normal;
out IBlock
{
vec2 TexCoord;
vec3 FragPos;
vec3 Normal;
} v_VertexData;
layout(std140, binding = 0) uniform ub_CameraData
{
mat4 ViewProjection;
vec3 CamPosition;
};
uniform mat4 u_ViewProjection = mat4(1.0);
uniform mat4 u_Model = mat4(1.0);
void main()
{
vec4 world_pos = u_Model * vec4(a_Position, 1.0);
v_VertexData.TexCoord = a_TexCoord;
v_VertexData.FragPos = world_pos.xyz;
v_VertexData.Normal = transpose(inverse(mat3(u_Model))) * a_Normal;
gl_Position = ViewProjection * u_Model * vec4(a_Position, 1.0);
}
下一个是片段,它们都很简单:
layout(location = 0) out vec4 gBuff_Color;
layout(location = 1) out vec3 gBuff_Normal;
layout(location = 2) out vec3 gBuff_Position;
in IBlock
{
vec2 TexCoord;
vec3 FragPos;
vec3 Normal;
} v_VertexData;
struct Material
{
float Smoothness;
vec4 AlbedoColor;
};
uniform Material u_Material = Material(1.0, vec4(1.0));
uniform sampler2D u_Albedo, u_Normal;
void main()
{
gBuff_Color = texture(u_Albedo, v_VertexData.TexCoord) * u_Material.AlbedoColor;
gBuff_Normal = normalize(v_VertexData.Normal);
gBuff_Position = v_VertexData.FragPos;
}
从问题中不清楚这里到底发生了什么,因为许多 GL 状态 - 在渲染到 gbuffer 时,以及在渲染 gbuffer 纹理以进行可视化时 - 都是未知的.但是,根据问题中给出的图像,可以不能得出附件 1 和 2 的实际颜色输出不工作的结论。
想到的一个问题是 alpha 混合。顶点着色器之后由每个片段操作处理的颜色值始终使用 RGBA 值 - 尽管 A 通道的 value 仅在启用混合并使用混合函数时才重要某种程度上取决于源 alpha。
如果您将自定义片段着色器输出声明为 float
、vec2
、vec3
,其余组件将保持未定义状态(未定义的值,而不是未定义的行为)。这不会造成问题,除非您执行的某些其他操作依赖于这些值。
我们这里还有一个 GL_RGBA16F
输出格式(这是正确的选择,因为 3 分量 RGB 格式中的 none 是规范要求的可渲染颜色)。
这里可能发生的是:
- Alpha 混合在渲染到 g 缓冲区的过程中已经打开。片段着色器的 alpha 输出恰好为零,因此它显示为 100% 透明并且纹理的内容没有改变。
- 在渲染到 g 缓冲区期间不使用 Alpha 混合,因此正确的内容最终出现在纹理中,Alpha 通道恰好以全零结束。现在可以通过启用 alpha 混合来可视化纹理,最终呈现 100% 透明视图。
如果是第一个选项,则在将 渲染到 g 缓冲区时关闭混合。它无论如何都不适用于延迟着色。你可能仍然 运行 到第二个选项。
如果这是第二个选项,则完全没有问题 - 随后的光照通道将读取它们需要的数据(最终,您将希望将有用的信息放入 alpha 通道中,以免浪费它并且能够减少附件的数量)。这只是您的可视化(我假设仅用于调试目的)是错误的。您可以尝试修复可视化。
附带说明:在 G-Buffer 中存储世界 space 位置是对带宽的巨大浪费。重建世界 space 位置所需的全部是深度值以及视图和投影矩阵的倒数。如果您将相机从世界 space 原点移开,那么在 GL_RGB16F
中存储世界 space 位置将很容易 运行 出现精度问题。
我正在尝试在个人学习开发的引擎中实现延迟渲染,但在渲染 GBuffer 中的所有纹理以进行检查时,我无法理解我做错了什么如果执行没问题。
问题是我目前有一个带有 3 种颜色附件的帧缓冲区,用于 GBuffer 的不同纹理(颜色、法线和位置),我初始化如下:
glCreateFramebuffers(1, &id);
glBindFramebuffer(GL_FRAMEBUFFER, id);
std::vector<uint> textures;
textures.resize(3);
glCreateTextures(GL_TEXTURE_2D, 3, textures.data());
for(size_t i = 0; i < 3; ++i)
{
glBindTexture(GL_TEXTURE_2D, textures[i]);
if(i == 0) // For Color Buffer
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, textures[i], 0);
}
GLenum color_buffers[3] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
glDrawBuffers((GLsizei)textures.size(), color_buffers);
uint depth_texture;
glCreateTextures(GL_TEXTURE_2D, 1, &depth_texture);
glBindTexture(GL_TEXTURE_2D, depth_texture);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, width, height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0);
bool fbo_status = glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
ASSERT(fbo_status, "Framebuffer Incompleted!");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
这没有报告任何错误,而且它似乎可以正常工作,因为前向渲染器的帧缓冲区可以正确渲染。然后,在渲染时,我 运行 绑定帧缓冲区并清除颜色和深度缓冲区后的下一个代码:
camera_buffer->Bind();
camera_buffer->SetData("ViewProjection", glm::value_ptr(viewproj_mat));
camera_buffer->SetData("CamPosition", glm::value_ptr(glm::vec4(view_position, 0.0f)));
camera_buffer->Unbind();
for(Entity& entity : scene_entities)
{
shader->Bind();
Texture* texture = entity.GetTexture();
BindTexture(0, texture);
shader->SetUniformMat4("u_Model", entity.transform);
shader->SetUniformInt("u_Albedo", 0);
shader->SetUniformVec4("u_Material.AlbedoColor", entity->AlbedoColor);
shader->SetUniformFloat("u_Material.Smoothness", entity->Smoothness);
glBindVertexArray(entity.VertexArray);
glDrawElements(GL_TRIANGLES, entity.VertexArray.index_buffer.count, GL_UNSIGNED_INT, nullptr);
// Shader, VArray and Textures Unbindings
}
因此,使用此代码,我设法渲染使用 ImGui::Image 函数创建的 3 个纹理,方法是在 0、1 或 2 之间切换纹理索引,如下所示:
ImGui::Image((ImTextureID)(fbo->textures[0]), viewport_size, ImVec2(0, 1), ImVec2(1, 0));
现在,颜色纹理(在索引 0 处)完美运行,如下图所示:
但是在渲染法线和位置纹理(索引 2 和 3)时,我没有结果:
有人看到我做错了什么吗?因为我已经花了几个小时来研究它,但我看不到它。我在 RenderDoc 上 运行 看不出有什么不对,RenderDoc 中显示的纹理与引擎中显示的纹理相同。
我在渲染实体时使用的顶点着色器是下一个:
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
layout(location = 2) in vec3 a_Normal;
out IBlock
{
vec2 TexCoord;
vec3 FragPos;
vec3 Normal;
} v_VertexData;
layout(std140, binding = 0) uniform ub_CameraData
{
mat4 ViewProjection;
vec3 CamPosition;
};
uniform mat4 u_ViewProjection = mat4(1.0);
uniform mat4 u_Model = mat4(1.0);
void main()
{
vec4 world_pos = u_Model * vec4(a_Position, 1.0);
v_VertexData.TexCoord = a_TexCoord;
v_VertexData.FragPos = world_pos.xyz;
v_VertexData.Normal = transpose(inverse(mat3(u_Model))) * a_Normal;
gl_Position = ViewProjection * u_Model * vec4(a_Position, 1.0);
}
下一个是片段,它们都很简单:
layout(location = 0) out vec4 gBuff_Color;
layout(location = 1) out vec3 gBuff_Normal;
layout(location = 2) out vec3 gBuff_Position;
in IBlock
{
vec2 TexCoord;
vec3 FragPos;
vec3 Normal;
} v_VertexData;
struct Material
{
float Smoothness;
vec4 AlbedoColor;
};
uniform Material u_Material = Material(1.0, vec4(1.0));
uniform sampler2D u_Albedo, u_Normal;
void main()
{
gBuff_Color = texture(u_Albedo, v_VertexData.TexCoord) * u_Material.AlbedoColor;
gBuff_Normal = normalize(v_VertexData.Normal);
gBuff_Position = v_VertexData.FragPos;
}
从问题中不清楚这里到底发生了什么,因为许多 GL 状态 - 在渲染到 gbuffer 时,以及在渲染 gbuffer 纹理以进行可视化时 - 都是未知的.但是,根据问题中给出的图像,可以不能得出附件 1 和 2 的实际颜色输出不工作的结论。
想到的一个问题是 alpha 混合。顶点着色器之后由每个片段操作处理的颜色值始终使用 RGBA 值 - 尽管 A 通道的 value 仅在启用混合并使用混合函数时才重要某种程度上取决于源 alpha。
如果您将自定义片段着色器输出声明为 float
、vec2
、vec3
,其余组件将保持未定义状态(未定义的值,而不是未定义的行为)。这不会造成问题,除非您执行的某些其他操作依赖于这些值。
我们这里还有一个 GL_RGBA16F
输出格式(这是正确的选择,因为 3 分量 RGB 格式中的 none 是规范要求的可渲染颜色)。
这里可能发生的是:
- Alpha 混合在渲染到 g 缓冲区的过程中已经打开。片段着色器的 alpha 输出恰好为零,因此它显示为 100% 透明并且纹理的内容没有改变。
- 在渲染到 g 缓冲区期间不使用 Alpha 混合,因此正确的内容最终出现在纹理中,Alpha 通道恰好以全零结束。现在可以通过启用 alpha 混合来可视化纹理,最终呈现 100% 透明视图。
如果是第一个选项,则在将 渲染到 g 缓冲区时关闭混合。它无论如何都不适用于延迟着色。你可能仍然 运行 到第二个选项。
如果这是第二个选项,则完全没有问题 - 随后的光照通道将读取它们需要的数据(最终,您将希望将有用的信息放入 alpha 通道中,以免浪费它并且能够减少附件的数量)。这只是您的可视化(我假设仅用于调试目的)是错误的。您可以尝试修复可视化。
附带说明:在 G-Buffer 中存储世界 space 位置是对带宽的巨大浪费。重建世界 space 位置所需的全部是深度值以及视图和投影矩阵的倒数。如果您将相机从世界 space 原点移开,那么在 GL_RGB16F
中存储世界 space 位置将很容易 运行 出现精度问题。