Nick Wiggill

03-04-2015, 01:32 PM

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:

texture() to get the current reconstructed positions for: p at the current tex coord, p + (1, 0) = p1, and p + (0, 1) = p2.

Get v1 vector p1 - p: vector between current position and it's screen/texture-space x neighbour

Get v2 vector p2 - p: vector between current position and it's screen/texture-space y neighbour

cross product these two and normalize() to get the surface normal at that texel.

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

1658

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

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.

Current understanding of getting normals from depth:

texture() to get the current reconstructed positions for: p at the current tex coord, p + (1, 0) = p1, and p + (0, 1) = p2.

Get v1 vector p1 - p: vector between current position and it's screen/texture-space x neighbour

Get v2 vector p2 - p: vector between current position and it's screen/texture-space y neighbour

cross product these two and normalize() to get the surface normal at that texel.

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

1658

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

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.