透视投影的反转几何gBuffer位置,正交投影可以吗?

3
我有一个延迟渲染器,看起来工作正常,深度、颜色和阴影都正确。然而,当使用透视投影时,位置缓冲区对于正交投影是正常的,但几何体似乎“反转”(或者深度被禁用)。
对于正交投影,我得到以下缓冲输出。

depth

normals

positions

目前最终的“阴影”图像看起来正确。

enter image description here

然而,当我使用透视投影时,会得到以下输出缓冲区...

enter image description here

enter image description here

enter image description here

最后的图片很好,尽管目前我没有整合任何位置缓冲区信息(注:目前仅进行“车头灯”着色)。

enter image description here

虽然最终图像看起来正确,但是我的位置缓冲似乎忽略了深度缓冲区...(代码中没有 glDisable(GL_DEPTH_TEST)。深度和法线缓冲区看起来没问题,只有“位置”缓冲区似乎忽略了深度?正交和透视的渲染管道完全相同,唯一的区别是投影矩阵。我使用 glm::ortho 和 glm::perspective,并根据场景 AABB 实时计算近/远裁剪距离。对于正交投影,我的近/远分别为 1 和 11.4734,对于透视投影,它们分别为 11.0875 和 22.5609... 宽度和高度值相同,透视投影的 fov 为 45。在绘制任何几何图形之前,我确实调用了这些函数...
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

我将其用于合成渲染管道的不同层。

我有做错什么吗?或者我有什么误解吗?

这是我的着色器...... gBuffer 的顶点着色器...

#version 430 core

layout (std140) uniform MatrixPV
{
    mat4 P;
    mat4 V;
};

layout(location = 0) in vec3 InPoint;
layout(location = 1) in vec3 InNormal;
layout(location = 2) in vec2 InUV;

uniform mat4 M;

out vec4 Position;
out vec3 Normal;
out vec2 UV;

void main()
{
    mat4 VM = V * M;
    gl_Position = P * VM * vec4(InPoint, 1.0);
    Position = P * VM * vec4(InPoint, 1.0);
    Normal = mat3(M) * InNormal;
    UV = InUV;
}
#version 430 core

layout(location = 0) out vec4 gBufferPicker;
layout(location = 1) out vec4 gBufferPosition;
layout(location = 2) out vec4 gBufferNormal;
layout(location = 3) out vec4 gBufferDiffuse;

in vec3 Normal;
in vec4 Position;

vec4 Diffuse();
uniform vec4 PickerColour;

void main()
{
    gBufferPosition = Position;
    gBufferNormal = vec4(Normal.xyz, 1.0);
    gBufferPicker = PickerColour;
    gBufferDiffuse = Diffuse();
}

这是“第二遍”着色器,用于可视化位置缓冲区...

#version 430 core

uniform sampler2D debugBufferPosition;

in vec2 UV;
out vec4 frag;

void main()
{
    vec3 val = texture(debugBufferPosition, UV).xyz;
    frag = vec4(val.xyz, 1.0);
}

我还没有使用位置缓冲数据,但我知道我可以在不必将它们存储在另一个缓冲区中的情况下重建它们,然而,对于其他原因,这些位置对我很有用,我想知道为什么它们在透视图中是这样的?

1个回答

1
你实际上在位置缓冲区中编写的是剪裁空间坐标。
Position = P * VM * vec4(InPoint, 1.0);

剪辑空间坐标是一种齐次坐标,通过透视除法将其转换为笛卡尔坐标系的归一化设备坐标。

ndc = gl_Position.xyz / gl_Position.w;

在正交投影中,w分量为1,但在透视投影中,w分量包含一个值,该值取决于视图空间坐标系(笛卡尔)的z分量(深度)。
建议将规范化设备坐标存储到位置缓冲区,而不是剪裁空间坐标。例如:
gBufferPosition = vec4(Position.xyz / Position.w, 1.0);

谢谢。位置缓冲区的可视化现在看起来和我预期的一样 :). 如果可以,我还有一个问题?您建议将位置存储为ndc,为什么?我希望拥有一个位置缓冲区的主要原因之一是能够通过使用glReadPixels与位置图像进行查询而不必在CPU上执行转换(尽管我现在有些犹豫,因为我有很多空闲的GPU周期),但也可能有助于SSAO。对于此,直接将位置存储在视图/眼睛(剪辑)空间中是否更好?非常感谢。 - lfgtm
@lfgtm 我的意思是,与存储剪辑空间坐标(你已经存储了剪辑空间坐标)相比,存储NDC坐标更好。当然,如果需要视图空间或世界空间坐标,则将其存储到缓冲区中。 - Rabbid76
1
好的,我会尝试一下。是的,我现在认为最好使用GPU从深度重建这个。非常感谢您的时间和帮助,非常感激 :) - lfgtm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接