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.

Rectangular area light illuminance

Rectangular area light illuminance

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.

enter image description here enter image description here

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?


2 Answers 2


It might be your display honestly. Some displays use less color depth than 32 bpp which can cause banding or flickering. You might consider dithering if you end up with no better option than addressing the banding.


Looking at the problem further, it just seems to be an issue in darker areas. Adding just a low level of ambient light, such as in the attached image, resolves the issue. Maybe someone else can elaborate on the cause of the problem? I consider the issue resolved for the moment, as I always have some level of ambient lighting present, but further explanation would be much appreciated.

enter image description here


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .