Camera-space Normals from depth texture

I want to use a stored (non-linear) depth texture from 1st pass to produce screen-space normals. In 2nd pass I can render out depth, diffuse, ID etc., but I can’t seem to get normals from depth working.

Current understanding of getting normals from depth:

[ol]
[li]texture() to get the current reconstructed positions for: p at the current tex coord, p + (1, 0) = p1, and p + (0, 1) = p2.[/li][li]Get v1 vector p1 - p: vector between current position and it’s screen/texture-space x neighbour[/li][li]Get v2 vector p2 - p: vector between current position and it’s screen/texture-space y neighbour[/li][li]cross product these two and normalize() to get the surface normal at that texel.[/li][/ol]

Shaders (2nd pass! to fullscreen quad)

Vertex:

#version 330 core
    
    layout (location = 0) in vec2 position;
    layout (location = 2) in vec2 texcoord;
    
    out vec2 texcoordFrag;
    
    void main()
    {
        gl_Position = vec4(position.x, position.y, 0, 1);
        texcoordFrag = texcoord;
    }

Fragment:

#version 330 core
    
    uniform sampler2D tex;
    uniform mat4 vpInv;
    
    in vec2 texcoordFrag;
    layout(location = 0) out vec4 fragmentColor;
    
    
    float near = 0.1;
    float far = 100.0;
        
    float linearizeDepth(float depth)
    {
        float nearToFarDistance = far - near;
        return (2.0 * near) / (far + near - depth * nearToFarDistance);
        //http://www.ozone3d.net/blogs/lab/20090206/how-to-linearize-the-depth-value/ 
        //http://www.geeks3d.com/20091216/geexlab-how-to-visualize-the-depth-buffer-in-glsl/
    } 
    
    vec3 worldSpacePositionFromDepth(in float depth)
    {
        vec4 clipSpacePos;
        clipSpacePos.xy = texcoordFrag * 2.0 - 1.0;
        clipSpacePos.z = texture(tex, texcoordFrag).r * 2.0 - 1.0;
        clipSpacePos.w = 1.0;
        vec4 homogenousPos = vpInv * clipSpacePos;
        return homogenousPos.xyz / homogenousPos.w;
    }
    
    vec3 viewSpacePositionFromDepth(in float depth) //PositionFromDepth_DarkPhoton(): https://www.opengl.org/discussion_boards/showthread.php/176040-Render-depth-to-texture-issue
    {
        vec2 ndc;             // Reconstructed NDC-space position
        vec3 eye;             // Reconstructed EYE-space position
    
        float top = 0.05463024898; //per 1 radianm FoV & distance of 0.1
        float bottom = -top;
        
        float right = top * 1024.0 / 768.0;
        float left = -right;
        
        float width = 1024.0;
        float height = 768.0;
        float widthInv = 1.0 / width;
        float heightInv = 1.0 / height;
        
        ndc.x = (texcoordFrag.x - 0.5) * 2.0;
        ndc.y = (texcoordFrag.y - 0.5) * 2.0;
    
        eye.z = linearizeDepth(depth); //eye.z = near * far / ((depth * (far - near)) - far); //original
        eye.x = (-ndc.x * eye.z) * right/near;
        eye.y = (-ndc.y * eye.z) * top/near;
    
        return eye;
    }
    
    vec3 getNormal(vec3 p1, vec3 p2)
    {
        vec3 normal = cross(p2, p1);
        normal.z = -normal.z;
        return normalize(normal) * 0.5 + 0.5;
    }    
    
    void main()
    {
        float depth  = texture(tex, texcoordFrag.st).r;
        float depth1 = texture(tex, texcoordFrag.st + vec2(1.0/1024.0, 0)).r;
        float depth2 = texture(tex, texcoordFrag.st + vec2(1.0/768.0 , 0)).r;
        float depthLinear = linearizeDepth(depth);
        
        if (depthLinear > 1.0) discard;
        //fragmentColor = vec4(depth, depth, depth, 1.0f);
        fragmentColor = vec4(depthLinear, depthLinear, depthLinear, 1.0);
        
        //vec3 w = worldSpacePositionFromDepth(depth);
        //fragmentColor = vec4(w, 1.0);
        
        vec3 p  = viewSpacePositionFromDepth(depth);
        vec3 p1 = viewSpacePositionFromDepth(depth1);
        vec3 p2 = viewSpacePositionFromDepth(depth2);
        
        vec3 v1 = (p1-p);
        vec3 v2 = (p2-p);
        vec3 normal = getNormal(v1, v2);
        fragmentColor = vec4(normal, 1.0);
    }

Result

[ATTACH=CONFIG]928[/ATTACH]

What am I doing wrong? Explain it like you would to a five year old. :slight_smile:

P.S. This will be used as a basis for SSAO and directional lighting.
P.P.S. As far as I know, dFdx and dFdy are not the answer as I am working with texels, not fragments.

As far as I know, dFdx and dFdy are not the answer as I am working with texels, not fragments.

But that is how it’s done (assuming you’re not using deferred rendering and thus don’t have a convenient normal lying around).

You are working with fragments. Since you’re in the fragment shader. Which works with fragments. What dFdx/y do is ask the neighboring fragment what value it computed for the variable in question, and takes the difference between that one and yours.

Since you’re doing SSAO, you’re almost certainly rendering a full-screen quad. So the neighboring fragment will be for the neighboring pixel. Therefore, the neighboring fragment will have computed the position based on the neighboring texel. So doing a dFdx on that position will compute the difference between your position and the position directly to the left/right of you.

[QUOTE=Alfonse Reinheart;1264714]But that is how it’s done (assuming you’re not using deferred rendering and thus don’t have a convenient normal lying around).

You are working with fragments. Since you’re in the fragment shader. Which works with fragments. What dFdx/y do is ask the neighboring fragment what value it computed for the variable in question, and takes the difference between that one and yours.

Since you’re doing SSAO, you’re almost certainly rendering a full-screen quad. So the neighboring fragment will be for the neighboring pixel. Therefore, the neighboring fragment will have computed the position based on the neighboring texel. So doing a dFdx on that position will compute the difference between your position and the position directly to the left/right of you.[/QUOTE]

Many thanks @Alfonse, for correcting my thinking.

What you said made sense. Besides this, there was a problem in that I was sampling the same fragment every time in viewSpacePositionFromDepth(). Once that was solved, I tried what you said and indeed, I now have it rendering the same normal colours using dFd*() as when doing the diffs explicitly.

SOLVED.