【问题标题】:How do I convert gl_FragCoord to a world space point in a fragment shader?如何将 gl_FragCoord 转换为片段着色器中的世界空间点?
【发布时间】:2016-08-14 01:50:56
【问题描述】:

我的理解是,如果您有视图投影矩阵、屏幕宽度和屏幕高度的倒数,您可以将 gl_FragCoord 转换为片段着色器中的世界坐标中的一个点。首先,通过分别除以宽度和高度,将gl_FragCoord.xgl_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;

    // ...
}

【问题讨论】:

  • 通过顶点着色器的变化传递世界位置会更容易(也更有效)。

标签: opengl glsl


【解决方案1】:

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

同时忽略 NDC 空间是 三维 的事实(就像窗口空间一样)。您还忘记了从剪辑空间到 NDC 空间的转换涉及一个除法,您没有撤消该除法。好吧,您确实尝试过撤消它,但是在通过反向剪辑变换进行了变换之后。

撤消vertex post-processing transformations 使用gl_FragCoord 的所有四个组件(尽管您可以只使用3 个组件)。第一步是撤消视口变换,这需要访问glDepthRange 的参数。

这为您提供了 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 是一个uniform,包含the glViewport function 指定的四个参数,其顺序与给定该函数的顺序相同。

【讨论】:

  • 嗨 Nicol,您能解释一下viewport 变量是什么以及它是如何定义的吗?
  • @travnik:完成。
【解决方案2】:

我发现我的代码存在问题。首先,正如 Nicol 指出的,glFragCoord.z(深度)需要从屏幕坐标偏移。另外,我写1 / ndc.w * clip而不是clip / clip.w的原始代码有一个错误。

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

#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;

    // ...
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-04
    • 2018-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-07
    • 1970-01-01
    • 2015-03-26
    相关资源
    最近更新 更多