Z-Buffer 的奇怪行为
Weird behaviour of Z-Buffer
我正在使用延迟着色方法来渲染场景,但由于 Z-Buffer 的奇怪行为,我在使用天空盒技术时遇到了问题。我创建了额外的帧缓冲区并附加了 5 个纹理,其中一个用作深度缓冲区。第一个通道是一个几何通道,它用所需的数据填充纹理,第二个通道渲染最终对象,并将纹理和闪电应用到主帧缓冲区,最后一个通道渲染天空盒。问题是Skybox被忽略了(Z-Buffer检查失败,所以它是不可见的)。
在渲染Skybox时,我将secondary framebuffer映射为GL_READ_FRAMEBUFFER,将main framebuffer(screen)映射为GL_DRAW_FRAMEBUFFER,这样就可以使用secondary 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 缓冲区的内容:
更新 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,
};
When rendering a Skybox, I map the secondary framebuffer as a
GL_READ_FRAMEBUFFER
and main framebuffer (screen) as a
GL_DRAW_FRAMEBUFFER
, so the depth buffer of the secondary framebuffer
can be used,
事情不是这样的。 GL_READ_FRAMEBUFFER
与深度测试无关。它用作 glReadPixels
或 glBlitFramebuffer
等操作的来源。但是对于任何类型的渲染,只有 GL_DRAW_FRAMEBUFFER
是相关的。这包括深度测试。
不幸的是,您不能将用户定义的渲染目标(如渲染缓冲区或纹理)与 window-系统提供的缓冲区混合,因此您无法在使用特定深度测试时渲染到最终屏幕.因此,您要么必须使用自定义深度缓冲区对 FBO 进行额外的渲染传递,并将结果 blit 到屏幕,要么您可以将自定义深度缓冲区的内容 blit 到 window-system provided one and用那个渲染。
我找到了解决办法。附加帧缓冲区有 32 bits/pixel,但主帧缓冲区只有 24,这导致 glBlitFramebuffer
无法工作。所以正确的帧缓冲区初始化应该是这样的:
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
NULL);
我正在使用延迟着色方法来渲染场景,但由于 Z-Buffer 的奇怪行为,我在使用天空盒技术时遇到了问题。我创建了额外的帧缓冲区并附加了 5 个纹理,其中一个用作深度缓冲区。第一个通道是一个几何通道,它用所需的数据填充纹理,第二个通道渲染最终对象,并将纹理和闪电应用到主帧缓冲区,最后一个通道渲染天空盒。问题是Skybox被忽略了(Z-Buffer检查失败,所以它是不可见的)。
在渲染Skybox时,我将secondary framebuffer映射为GL_READ_FRAMEBUFFER,将main framebuffer(screen)映射为GL_DRAW_FRAMEBUFFER,这样就可以使用secondary 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 缓冲区的内容:
更新 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,
};
When rendering a Skybox, I map the secondary framebuffer as a
GL_READ_FRAMEBUFFER
and main framebuffer (screen) as aGL_DRAW_FRAMEBUFFER
, so the depth buffer of the secondary framebuffer can be used,
事情不是这样的。 GL_READ_FRAMEBUFFER
与深度测试无关。它用作 glReadPixels
或 glBlitFramebuffer
等操作的来源。但是对于任何类型的渲染,只有 GL_DRAW_FRAMEBUFFER
是相关的。这包括深度测试。
不幸的是,您不能将用户定义的渲染目标(如渲染缓冲区或纹理)与 window-系统提供的缓冲区混合,因此您无法在使用特定深度测试时渲染到最终屏幕.因此,您要么必须使用自定义深度缓冲区对 FBO 进行额外的渲染传递,并将结果 blit 到屏幕,要么您可以将自定义深度缓冲区的内容 blit 到 window-system provided one and用那个渲染。
我找到了解决办法。附加帧缓冲区有 32 bits/pixel,但主帧缓冲区只有 24,这导致 glBlitFramebuffer
无法工作。所以正确的帧缓冲区初始化应该是这样的:
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, WindowWidth, WindowHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT,
NULL);