I'm currently working on an implementation of rectangular area lights but I am having some issues with the illuminance calculation, which gives me serious colour banding across the entire lit area. I'm using the paper published by DICE, "Moving Frostbite to Physically Based Rendering" (http://www.frostbite.com/2014/11/moving-frostbite-to-pbr/) as my base for implementation (Listing 12).
This is the relevant code used:
Solid angle of the rectangle
#define M_PI 3.14159265358979323846
float RectangleSolidAngle(vec3 worldPos, vec3 p0, vec3 p1, vec3 p2, vec3 p3)
{
vec3 v0 = p0 - worldPos;
vec3 v1 = p1 - worldPos;
vec3 v2 = p2 - worldPos;
vec3 v3 = p3 - worldPos;
vec3 n0 = normalize(cross(v0, v1));
vec3 n1 = normalize(cross(v1, v2));
vec3 n2 = normalize(cross(v2, v3));
vec3 n3 = normalize(cross(v3, v0));
float g0 = acos(dot(-n0, n1));
float g1 = acos(dot(-n1, n2));
float g2 = acos(dot(-n2, n3));
float g3 = acos(dot(-n3, n0));
return g0 + g1 + g2 + g3 - 2.0f * M_PI;
}
Illuminance calculation
float RectangleIlluminance(vec3 worldPos, vec3 worldNormal, AreaLightData lightSource)
{
float result = 0.0f;
if (dot((worldPos - lightSource.position), lightSource.normal) > 0.0f)
{
float hWidth = lightSource.halfWidth;
float hHeight = lightSource.halfHeight;
vec3 p0 = lightSource.position + lightSource.right * -hWidth + lightSource.up * hHeight;
vec3 p1 = lightSource.position + lightSource.right * -hWidth + lightSource.up * -hHeight;
vec3 p2 = lightSource.position + lightSource.right * hWidth + lightSource.up * -hHeight;
vec3 p3 = lightSource.position + lightSource.right * hWidth + lightSource.up * hHeight;
float solidAngle = RectangleSolidAngle(worldPos, p0, p1, p2, p3);
float illuminance = solidAngle * 0.2f * (
Saturate(dot(normalize(p0 - worldPos), worldNormal)) +
Saturate(dot(normalize(p1 - worldPos), worldNormal)) +
Saturate(dot(normalize(p2 - worldPos), worldNormal)) +
Saturate(dot(normalize(p3 - worldPos), worldNormal)) +
Saturate(dot(normalize(lightSource.position - worldPos), worldNormal)));
result = illuminance;
}
return result;
}
The paper mentions some level of banding but it should not be on the level which I am currently getting, which can be seen in motion in this video: https://youtu.be/gmkOOxIYc9s . These images show the illuminance without shadows or anything.
From what I can gather from the paper, the code is correct. The current solution is deferred, reconstructing position using the depth stencil texture and sampling normals from a RGBA32F texture (trying to eliminate precision issues). A directional light also seems to produce some level of banding, although not as visible as with the area light, which can be seen in the following images.
So maybe it's still just a precision issue? The code for handling my depth and position reconstruction looks like this:
uniform sampler2D gDepthStencil;
vec4 ReconstructWSPosition(float z, vec2 uv_f)
{
vec4 sPos = vec4(uv_f * 2.0f - 1.0f, z, 1.0f);
sPos = gInvProjView * sPos;
return vec4((sPos.xyz / sPos.w ), sPos.w);
}
void main()
{
ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
float depth = texelFetch(gDepthStencil, pixel, 0).r * 2.0f - 1.0f;
vec4 wPosition = ReconstructWSPosition(depth, uv);
}
Which to me looks correct, but I might be missing something. Any other ideas or suggestions? Maybe a matter of tonemapping/HDR problems?