简体   繁体   中英

How do I convert gl_FragCoord to a world space point in a fragment shader?

My understanding is that you can convert gl_FragCoord to a point in world coordinates in the fragment shader if you have the inverse of the view projection matrix, the screen width, and the screen height. First, you convert gl_FragCoord.x and gl_FragCoord.y from screen space to normalized device coordinates by dividing by the width and height respectively, then scaling and offsetting them into the range [-1, 1]. Next you transform by the inverse view projection matrix to get a world space point that you can use only if you divide by the w component.

Below is the fragment shader code I have that isn't working. Note inverse_proj is actually set to the inverse view projection matrix:

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

    // ...
}

First, you convert gl_FragCoord.x and gl_FragCoord.y from screen space to normalized device coordinates

While simultaneously ignoring the fact that NDC space is three-dimensional (as is window space). You also forgot that the transformation from clip-space to NDC space involved a division, which you did not undo. Well, you did kinda try to undo it, but after transforming by the inverse clip transformation.

Undoing the vertex post-processing transformations use all four components of gl_FragCoord (though you could make due with just 3). The first step is undoing the viewport transform, which requires getting access to the parameters given to glDepthRange .

That gives you the NDC coordinate. Then you have to undo the perspective divide. gl_FragCoord.w is given the value 1/clipW. And clipW was the divisor in that operation. So you divide by gl_FragCoord.w to get back into clip space.

From there, you can multiply by the inverse of the projection matrix. Though if you want world-space, the projection matrix you invert must be a world-to-projection, rather than just pure projection (which is normally camera-to-projection).

In-code:

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;

Where viewport is a uniform containing the four parameters specified by the glViewport function , in the same order as given to that function.

I figured out the problems with my code. First, as Nicol pointed out, glFragCoord.z (depth) needs to be shifted from screen coordinates. Also, there is a mistake with the original code where I wrote 1 / ndc.w * clip instead of clip / clip.w .

As noted by BDL, however, it would be more efficient to pass the world position as a varying to the fragment shader. However, the code below is a short way to achieve the desired result entirely through the fragment shader (eg for screen-space programs that don't have a world position per fragment and you want the view vector per fragment).

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

    // ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM