Opengl 如何将gl_FragCoord转换为片段着色器中的世界空间点?

Opengl 如何将gl_FragCoord转换为片段着色器中的世界空间点?,opengl,glsl,Opengl,Glsl,我的理解是,如果有视图投影矩阵、屏幕宽度和屏幕高度的倒数,可以将gl_FragCoord转换为片段着色器中的世界坐标点。首先,通过分别除以宽度和高度,将gl_FragCoord.x和gl_FragCoord.y从屏幕空间转换为标准化设备坐标,然后将其缩放和偏移到范围[-1,1]。接下来,通过逆视图投影矩阵进行变换,以获得仅当除以w分量时才能使用的世界空间点 下面是我的片段着色器代码,它不起作用。注inverse_proj实际上设置为逆视图投影矩阵: #version 450 uniform m

我的理解是,如果有视图投影矩阵、屏幕宽度和屏幕高度的倒数,可以将gl_FragCoord转换为片段着色器中的世界坐标点。首先,通过分别除以宽度和高度,将
gl_FragCoord.x
gl_FragCoord.y
从屏幕空间转换为标准化设备坐标,然后将其缩放和偏移到范围[-1,1]。接下来,通过逆视图投影矩阵进行变换,以获得仅当除以w分量时才能使用的世界空间点

下面是我的片段着色器代码,它不起作用。注
inverse_proj
实际上设置为逆视图投影矩阵:

#version 450

uniform mat4 inverse_proj;
uniform float screen_width;
uniform float screen_height;

out vec4 fragment;

void main()
{
    // Convert screen coordinates to normalized device coordinates (NDC)
    vec4 ndc = vec4(
        (gl_FragCoord.x / screen_width - 0.5) * 2,
        (gl_FragCoord.y / screen_height - 0.5) * 2,
        0,
        1);

    // Convert NDC throuch inverse clip coordinates to view coordinates
    vec4 clip = inverse_proj * ndc;
    vec3 view = (1 / ndc.w * clip).xyz;

    // ...
}
首先,将gl_FragCoord.x和gl_FragCoord.y从屏幕空间转换为标准化设备坐标

同时忽略了NDC空间是三维的这一事实(就像窗口空间一样)。您还忘记了从剪辑空间到NDC空间的转换涉及一个分割,您没有撤消该分割。好吧,你确实试着撤销它,但是在通过逆剪辑变换变换之后

取消使用
gl_FragCoord
的所有四个组件(尽管您可以只使用3个组件)。第一步是撤消视口变换,这需要访问给定给
gldeptrange
的参数

这就给出了NDC坐标。然后,您必须撤消透视分割
gl_FragCoord.w
的值为1/clipW。clipW是这个运算的除数。所以你除以
gl_FragCoord.w
回到剪辑空间

从那里,可以乘以投影矩阵的逆。但是,如果需要世界空间,则反转的投影矩阵必须是世界到投影,而不仅仅是纯投影(通常是摄影机到投影)

代码:

vec4 ndcPos;
ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1;
ndcPos.z = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) /
    (gl_DepthRange.far - gl_DepthRange.near);
ndcPos.w = 1.0;

vec4 clipPos = ndcPos / gl_FragCoord.w;
vec4 eyePos = invPersMatrix * clipPos;

其中,
viewport
是一个统一体,包含由指定的四个参数,其顺序与给定给该函数的顺序相同。

我解决了代码中的问题。首先,正如Nicol所指出的,
glFragCoord.z
(深度)需要从屏幕坐标进行移动。另外,我在原始代码中编写了
1/ndc.w*clip
而不是
clip/clip.w
,这是一个错误

但是,正如BDL所指出的,将世界位置作为一个变量传递给片段着色器会更有效。但是,下面的代码是完全通过片段着色器实现所需结果的一种简单方法(例如,对于屏幕空间程序,没有每个片段的世界位置,您需要每个片段的视图向量)


通过一个不同于顶点着色器的变量传递世界位置会更容易(也更有效)。嗨,尼科尔,请你解释一下什么是
视口
变量以及它是如何定义的?@travnik:Done。
#version 450

uniform mat4 inverse_view_proj;
uniform float screen_width;
uniform float screen_height;

out vec4 fragment;

void main()
{
    // Convert screen coordinates to normalized device coordinates (NDC)
    vec4 ndc = vec4(
        (gl_FragCoord.x / screen_width - 0.5) * 2.0,
        (gl_FragCoord.y / screen_height - 0.5) * 2.0,
        (gl_FragCoord.z - 0.5) * 2.0,
        1.0);

    // Convert NDC throuch inverse clip coordinates to view coordinates
    vec4 clip = inverse_view_proj * ndc;
    vec3 vertex = (clip / clip.w).xyz;

    // ...
}