延迟渲染不显示 GBuffer 纹理

问题描述

我正在尝试在我作为个人学习而开发的引擎中实现延迟渲染,但在渲染 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,GL_RGBA8,width,height,GL_RGBA,GL_UNSIGNED_BYTE,nullptr);
     else
       glTexImage2D(GL_TEXTURE_2D,GL_RGBA16F,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,GL_DEPTH24_STENCIL8,height);

  glTexParameteri(GL_TEXTURE_2D,GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D,GL_LINEAR);
  glFramebufferTexture2D(GL_FRAMEBUFFER,GL_DEPTH_STENCIL_ATTACHMENT,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 处)完美运行,如下图所示:

color texture

但是在渲染法线和位置纹理(索引 2 和 3)时,我没有结果:

pos/norm texture

有人看到我做错了什么吗?因为我已经用了好几个小时了,我看不到它。我在 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 通道的 仅在您启用混合并使用某种方式依赖于的混合函数时才重要源 alpha。

如果您将自定义片段着色器输出声明为 float,vec2,vec3,其余组件保持未定义(未定义的值,而非未定义的行为)。这不会造成问题,除非您执行的某些其他操作依赖于这些值。

我们这里还有一个 GL_RGBA16F 输出格式(这是正确的选择,因为规范不需要任何 3 分量 RGB 格式都可呈现颜色)。

这里可能发生的是:

  • 在渲染到 g 缓冲区期间已启用 Alpha 混合。片段着色器的 alpha 输出恰好为零,因此它显示为 100% 透明并且纹理的内容没有改变。
  • 在渲染到 g 缓冲区期间不使用 Alpha 混合,因此正确的内容最终出现在纹理中,而 Alpha 通道恰好以全零结束。现在,纹理可以通过 alpha 混合进行可视化,最终呈现 100% 透明的视图。

如果是第一个选项,在渲染到 g-buffer 时关闭混合。无论如何,它不适用于延迟着色。那么你可能仍然会遇到第二个选项。

如果这是第二个选项,则完全没有问题 - 随后的光照通道将读取它们需要的数据(最终,您将希望将有用的信息放入 alpha 通道中以免浪费它并能够以减少附件数量)。只是您的可视化(我认为仅用于调试目的)是错误的。您可以尝试修复可视化。

附带说明:将世界空间位置存储在 G-Buffer 中是对带宽的巨大浪费。能够重建世界空间位置所需要的只是深度值以及视图和投影矩阵的倒数。如果您将相机从世界空间原点移开,将世界空间位置存储在 GL_RGB16F 中也很容易遇到精度问题。