Hi All,
I am a seasoned DX Developer and having scoured the web for decent deferred shader approaches I am trying to adapt samples from the web to the forsaken tongue
Deferred Rendering Tutorials I have seen:
- http://ogldev.atspace.co.uk/www/tutorial35/tutorial35.html - Uses Extra Depth Texture & Inefficient Stencil
- http://www.catalinzima.com/xna/tutorials/deferred-rendering-in-xna/creating-the-g-buffer/ - Uses Extra Depth Texture
- http://www.openglsuperbible.com/example-code/ (Deferred Rendering Demo) - Uses Extra Depth Texture
- http://mynameismjp.wordpress.com/2010/09/05/position-from-depth-3/ - Only snippets, limited success with the frag_depth "vertex / w" approach (with perspective screen space distortions), fell flat otherwise.
- https://github.com/Circular-Studios/Dash - Looks great but I was unable to inspect using dev tools debugger did not play nice with D.
- http://www.humus.name/index.php?page=3D&&start=0 - No excess position buffers, no stencil buffers, advanced depth inspection to filter lights efficiently, its beautiful!
In my attempt to port the last sample (based on the Second-Depth Anti-Alias sample) I get world space wrapped inside the light. Clearly the primary fault is that the inverse view projection is invalid (this was the worst number of artifacts I could generate rotating around a cube).
I intentionally have a very well rounded light sphere to make the distortions clear.
Provided I can get the basics like a world space position I can write beautiful shaders, I am struggling to get this point of reference and without it I feel about 2cm tall.
Because I want this to be available for everyone (I hate the lack of GL sample code!) here is what I have so far (apologies that there is no sample app):
GeomVertShader:
#version 330
in vec3 inPos;
in vec2 inUV;
in vec3 inNormal;
in vec3 inBinormal;
in vec3 inTangent;
uniform mat4 wvpMatrix;
out vec4 mWVPPosition;
out vec3 pNormal;
out vec3 pBinormal;
out vec3 pTangent;
out vec2 texCoord;
void main(void) {
mWVPPosition = wvpMatrix * vec4(inPos, 1.0f);
gl_Position = mWVPPosition;
pNormal = inNormal;
pBinormal = inBinormal;
pTangent = inTangent;
texCoord = inUV;
}
GeomFragShader
#version 330
in vec4 mWVPPosition;
in vec3 pNormal;
in vec3 pBinormal;
in vec3 pTangent;
in vec2 texCoord;
uniform mat4 wvpMatrix;
uniform sampler2D diffuseTexture;
uniform sampler2D normalTexture;
uniform sampler2D heightTexture;
uniform sampler2D specularTexture;
layout (location = 0) out vec4 colourOut;
layout (location = 1) out vec4 normalOut;
void main(void) {
vec3 bump = 2 * texture(normalTexture, texCoord).xyz -1;
vec3 normal = pTangent * bump.x + pBinormal * bump.y + pNormal * bump.z;
normal = normalize(normal);
colourOut = texture( diffuseTexture, texCoord );
// specular intensity
vec3 specularSample = texture( specularTexture, texCoord ).xyz;
colourOut.w = ( specularSample.x + specularSample.y + specularSample.z ) / 3;
normalOut.xyz = normal;
normalOut.w = 1;
}
PointLightVertShader
#version 330
in vec3 inPos;
in vec2 inUV;
uniform mat4 wvpMatrix;
uniform mat4 ivpMatrix;
uniform vec2 zBounds;
uniform vec3 camPos;
uniform float invRadius;
uniform vec3 lightPos;
uniform vec3 lightColour;
uniform float lightRadius;
uniform float lightFalloff;
out vec4 mWVPPosition;
void main(void) {
vec3 position = inPos;
position *= lightRadius;
position += lightPos;
mWVPPosition = wvpMatrix * vec4(position, 1.0f);
gl_Position = mWVPPosition;
}
PointLightFragShader
#version 330
in vec4 mWVPPosition;
uniform mat4 wvpMatrix;
uniform mat4 ivpMatrix;
uniform vec2 zBounds;
uniform vec3 camPos;
uniform float invRadius;
uniform vec3 lightPos;
uniform vec3 lightColour;
uniform float lightRadius;
uniform float lightFalloff;
uniform sampler2D diffuseTexture;
uniform sampler2D normalTexture;
uniform sampler2D depthTexture;
layout (location = 0) out vec4 colourOut;
void main(void) {
vec2 UV = mWVPPosition.xy;
float depth = texture(diffuseTexture, UV).x;
vec3 addedLight = vec3(0,0,0);
//if (depth >= zBounds.x && depth <= zBounds.y)
{
vec4 diffuseTex = texture(diffuseTexture, UV);
vec4 normal = texture(normalTexture, UV);
// Screen-space position
vec4 cPos = vec4(UV, depth, 1);
// World-space position
vec4 wPos = ivpMatrix * cPos;
vec3 pos = wPos.xyz / wPos.w;
// Lighting vectors
vec3 lVec = (lightPos - pos) * invRadius;
vec3 lightVec = normalize(lVec);
vec3 viewVec = normalize(camPos - pos);
// Attenuation that falls off to zero at light radius
float atten = clamp(1.0f - dot(lVec, lVec), 0.0, 1.0);
atten *= atten;
// Lighting
float colDiffuse = clamp(dot(lightVec, normal.xyz), 0, 1);
float specular_intensity = diffuseTex.w * 0.4f;
float specular = specular_intensity * pow(clamp(dot(reflect(-viewVec, normal.xyz), lightVec), 0.0, 1.0), 10.0f);
addedLight = atten * (colDiffuse * diffuseTex.xyz + specular);
}
colourOut = vec4(addedLight.xyz, 1);
}
Note that for the moment I am totally ignoring the optimisation of "if (depth >= zBounds.x && depth <= zBounds.y)" because I want to crack the basic reconstruction before experimenting with this.
Shader Binding:
Matrix4f inverseViewProjection = new Matrix4f();
Vector3f camPos = cameraController.getActiveCameraPos();
GL20.glUniform3f(shader.getLocCamPos(), camPos.x, camPos.y, camPos.z);
inverseViewProjection = cameraController.getActiveVPMatrixInverse();
//inverseViewProjection = inverseViewProjection.translate(new Vector3f(-1f, 1f, 0));
//inverseViewProjection = inverseViewProjection.scale(new Vector3f(2, -2, 1));
inverseViewProjection = inverseViewProjection.scale(new Vector3f(1f/engineParams.getDisplayWidth(), 1f/engineParams.getDisplayHeight(), 1));
GL20.glUniformMatrix4(shader.getLocmIVPMatrix(), false, OpenGLHelper.getMatrix4ScratchBuffer(inverseViewProjection));
float nearTest = 0, farTest = 0;
Matrix4f projection = new Matrix4f(cameraController.getCoreCameraProjection());
GL20.glUniformMatrix4(shader.getLocmWVP(), false, OpenGLHelper.getMatrix4ScratchBuffer(cameraController.getActiveViewProjectionMatrix()));
Vector2f zw = new Vector2f(projection.m22, projection.m23);
//Vector4f testLightViewSpace = new Vector4f(lightPos.getX(), lightPos.getY(), lightPos.getZ(), 1);
//testLightViewSpace = OpenGLHelper.columnVectorMultiplyMatrixVector((Matrix4f)cameraController.getActiveCameraView(), testLightViewSpace);
// Compute z-bounds
Vector4f lPos = OpenGLHelper.columnVectorMultiplyMatrixVector(cameraController.getActiveCameraView(), new Vector4f(lightPos.x, lightPos.y, lightPos.z, 1.0f));
float z1 = lPos.z + lightRadius;
//if (z1 > NEAR_DEPTH)
{
float z0 = Math.max(lPos.z - lightRadius, NEAR_DEPTH);
nearTest = (zw.x + zw.y / z0);
farTest = (zw.x + zw.y / z1);
if (nearTest > 1) {
nearTest = 1;
} else if (nearTest < 0) {
nearTest = 0;
}
if (farTest > 1) {
farTest = 1;
} else if (farTest < 0) {
farTest = 0;
}
GL20.glUniform3f(shader.getLocLightPos(), lightPos.getX(), lightPos.getY(), lightPos.getZ());
GL20.glUniform3f(shader.getLocLightColour(), lightColour.getX(), lightColour.getY(), lightColour.getZ());
GL20.glUniform1f(shader.getLocLightRadius(), lightRadius);
GL20.glUniform1f(shader.getLocInvRadius(), 1f/lightRadius);
GL20.glUniform1f(shader.getLocLightFalloff(), lightFalloff);
GL20.glUniform2f(shader.getLocZBounds(), nearTest, farTest);
}
The line "inverseViewProjection = cameraController.getActiveVPMatrixInverse();" depends on the multiplied result of the inverse of these two:
View Matrix
public void updateViewMatrix(Matrix4f coreViewMatrix) {
Matrix4f.setIdentity(coreViewMatrix);
if (lookAtVector.length() != 0) {
lookAtVector.normalise();
}
Vector3f.cross(up, lookAtVector, right);
if (right.length() != 0) {
right.normalise();
}
Vector3f.cross(lookAtVector, right, up);
if (up.length() != 0) {
up.normalise();
}
coreViewMatrix.m00 = right.x;
coreViewMatrix.m01 = up.x;
coreViewMatrix.m02 = lookAtVector.x;
coreViewMatrix.m03 = 0;
coreViewMatrix.m10 = right.y;
coreViewMatrix.m11 = up.y;
coreViewMatrix.m12 = lookAtVector.y;
coreViewMatrix.m13 = 0;
coreViewMatrix.m20 = right.z;
coreViewMatrix.m21 = up.z;
coreViewMatrix.m22 = lookAtVector.z;
coreViewMatrix.m23 = 0;
//Inverse dot from eye position
coreViewMatrix.m30 = -Vector3f.dot(eyePosition, right);
coreViewMatrix.m31 = -Vector3f.dot(eyePosition, up);
coreViewMatrix.m32 = -Vector3f.dot(eyePosition, lookAtVector);
coreViewMatrix.m33 = 1;
}
Projection Matrix:
public static void createProjection(Matrix4f projectionMatrix, float fov, float aspect, float znear, float zfar) {
float scale = (float) Math.tan((Math.toRadians(fov)) * 0.5f) * znear;
float r = aspect * scale;
float l = -r;
float t = scale;
float b = -t;
projectionMatrix.m00 = 2 * znear / (r-l);
projectionMatrix.m01 = 0;
projectionMatrix.m02 = 0;
projectionMatrix.m03 = 0;
projectionMatrix.m10 = 0;
projectionMatrix.m11 = 2 * znear / (t-b);
projectionMatrix.m12 = 0;
projectionMatrix.m13 = 0;
projectionMatrix.m20 = (r + l) / (r-l);
projectionMatrix.m21 = (t+b)/(t-b);
projectionMatrix.m22 = -(zfar + znear) / (zfar-znear);
projectionMatrix.m23 = -1;
projectionMatrix.m30 = 0;
projectionMatrix.m31 = 0;
projectionMatrix.m32 = -2 * zfar * znear / (zfar - znear);
projectionMatrix.m33 = 0;
}
TLDR:
Please help me diagnose what is wrong with the lighting from the picture/code above, my holy grail is a working sample of true depth reconstruction in OpenGL preferably to world space.