【问题标题】:Weird behaviour of Z-BufferZ-Buffer 的奇怪行为
【发布时间】:2015-07-05 16:06:29
【问题描述】:

我正在使用延迟着色方法来渲染场景,但由于 Z-Buffer 的奇怪行为,我遇到了 Skybox 技术的问题。我创建了额外的帧缓冲区并附加了 5 个纹理,其中一个用作深度缓冲区。第一个通道是几何通道,它用所需的数据填充纹理,第二个通道渲染最终对象,并将纹理和闪电应用于主帧缓冲区,最后一个通道渲染天空盒。问题是 Skybox 被忽略(Z-Buffer 检查失败,所以它是不可见的)。

在渲染天空盒时,我将辅助帧缓冲区映射为 GL_READ_FRAMEBUFFER,将主帧缓冲区(屏幕)映射为 GL_DRAW_FRAMEBUFFER,因此可以使用辅助帧缓冲区的深度缓冲区,然后我将深度函数设置为 GL_LEQUAL。只有当我将深度函数更改为 GL_GREATER(或 GL_ALWAYS)时,才能看到天空盒,但它是在对象之上渲染的。

顶点着色器:

#version 330

layout (location = 0) in vec3 Position;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;

out vec3 TexCoord0;

void main()
{
    vec4 MVP_Pos = P * V * M * vec4(Position, 1.0);
    gl_Position = MVP_Pos.xyww;
    TexCoord0 = Position;
}

模型矩阵是天空盒到相机坐标的转换结果。

glDepthFunction 设置为 GL_LEQUAL:

glDepthFunction 设置为 GL_GREATER:

Z-Buffer 的内容:

更新 1:我尝试过像这样使用 glBlitFramebuffer:

glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo); //Additional framebuffer
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDepthMask(GL_TRUE);
glBlitFramebuffer(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, GL_DEPTH_BUFFER_BIT, GL_NEAREST);

但它不起作用。主深度缓冲区包含黑色像素数组。

更新 2:

//FBO initialization
glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo);

// Create gbuffer textures
glGenTextures(ARRAY_SIZE_IN_ELEMENTS(m_textures), m_textures);
glGenTextures(1, &m_depthTexture);

for (unsigned int i = 0; i < ARRAY_SIZE_IN_ELEMENTS(m_textures); i++) {
    glBindTexture(GL_TEXTURE_2D, m_textures[i]);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, WindowWidth, WindowHeight, 0, GL_RGB, GL_FLOAT, NULL);

    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, m_textures[i], 0);
    }

// depth buffer
glBindTexture(GL_TEXTURE_2D, m_depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
        NULL);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexture, 0);

GLenum DrawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3 };
glDrawBuffers(ARRAY_SIZE_IN_ELEMENTS(DrawBuffers), DrawBuffers);

GLenum Status = glCheckFramebufferStatus(GL_FRAMEBUFFER);

if (Status != GL_FRAMEBUFFER_COMPLETE) {
    fprintf(stderr, "FB error, status: 0x%x\n", Status);
    return false;
}

天空盒缓冲区:

GLfloat cube_vertices[] = {
        // front
        -1.0, -1.0, 1.0,
        1.0, -1.0, 1.0,
        1.0, 1.0, 1.0,
        -1.0, 1.0, 1.0,
        // back
        -1.0, -1.0, -1.0,
        1.0, -1.0, -1.0,
        1.0, 1.0, -1.0,
        -1.0, 1.0, -1.0,
    };

    GLushort cube_elements[] = {
        // front
        0, 1, 2,
        2, 3, 0,
        // top
        3, 2, 6,
        6, 7, 3,
        // back
        7, 6, 5,
        5, 4, 7,
        // bottom
        4, 5, 1,
        1, 0, 4,
        // left
        4, 0, 3,
        3, 7, 4,
        // right
        1, 5, 6,
        6, 2, 1,
    };

【问题讨论】:

  • 最后一次渲染 Skybox 为什么?你不能先渲染天空盒,然后在上面混合其他东西吗? (可能是清除深度,或者禁用深度测试等)
  • @peppe 如果天空盒是在第一遍绘制时,着色器将需要设置屏幕上的每个像素,但在最后一遍早期深度测试可以提供巨大的速度提升,因为通常只有一小部分天空盒是可见的。
  • 我从没想过绘制天空盒会限制填充率...
  • @peppe:你认为天空盒渲染还会受到什么限制?它主要使用填充率。这绝对是图形供应商的推荐。 “做会被扔掉的工作毫无意义”。你为什么要先做这项工作?
  • 当然,但同时它在大多数时候都是微不足道的。无论如何,也许你可以把它印出来。

标签: c++ opengl glsl zbuffer skybox


【解决方案1】:

渲染天空盒时,我将辅助帧缓冲区映射为 GL_READ_FRAMEBUFFER 和主帧缓冲区(屏幕)作为 GL_DRAW_FRAMEBUFFER,所以是二级framebuffer的深度缓冲区 可以用,

事情不是这样运作的。 GL_READ_FRAMEBUFFER 与深度测试无关。它用作glReadPixelsglBlitFramebuffer 等操作的源。但是对于任何类型的渲染,只有GL_DRAW_FRAMEBUFFER 是相关的。这包括深度测试。

不幸的是,您不能将用户定义的渲染目标(如渲染缓冲区或纹理)与窗口系统提供的缓冲区混合在一起,因此您在使用特定深度测试时无法渲染到最终屏幕。因此,您要么必须使用自定义深度缓冲区对 FBO 进行额外的渲染传递,然后将结果 blit 到屏幕上,要么您可以将自定义深度缓冲区的内容 blit 到提供的窗口系统并使用它进行渲染。

【讨论】:

  • 使用 glBlitFramebuffer 是我尝试做的第一件事,但是它不起作用。
  • @romu4:嗯,还有很多事情还不清楚,例如,你试图在哪个深度绘制天空盒,你如何设置你的 FBO 和深度缓冲区/纹理等等。这个答案确实集中在原始问题描述的方法中绝对错误的事情上。
  • 感谢您的回答。我在主帖中添加了 FBO 初始化代码和 Skybox 缓冲区内容。纹理缓冲区似乎工作得很好,唯一的问题是 Z-Buffer,glBlitFramebuffer 不起作用。
【解决方案2】:

我找到了解决方案。额外的帧缓冲区有 32 位/像素,但主帧缓冲区只有 24,这阻止了 glBlitFramebuffer 工作。所以正确的帧缓冲初始化应该是这样的:

glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
        NULL);

【讨论】:

    猜你喜欢
    • 2019-03-27
    • 1970-01-01
    • 2021-07-13
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多