DaveH355 / clustered-shading

An OpenGL tutorial on clustered shading. A technique for efficiently rendering thousands of dyanmic lights in games.
MIT License
48 stars 2 forks source link

Issues getting light binning to work #4

Closed Ravbug closed 3 months ago

Ravbug commented 3 months ago

Thanks for this guide! I'm trying to adapt it for my own engine.

I noticed that when viewing the scene from oblique angles, the lights do not appear to bin properly. The issue is shown in this video:

https://github.com/user-attachments/assets/98f44506-c14e-468a-8db0-ae30e5e8644f

Do you know of any common mistakes that can cause this? I'm using reverse-Z and my depth range is [0,1] rather than [-1,1], so I changed the screenToView function to this:

vec3 screenToView(vec2 screenCoord)
{
    // normalize screenCoord to [0, 1] and
    // set the NDC depth of the coordinate to be on the near plane. 
    vec4 ndc = vec4(screenCoord / ubo.screenDimensions, 0, 1.0);

    vec4 viewCoord = ubo.inverseProjection * ndc;
    viewCoord /= viewCoord.w;
    return viewCoord.xyz;
}

Everything else is a direct port of the code shown on the guide.

DaveH355 commented 3 months ago

Hi, the screenToView function should still normalize xy to [-1, 1]. Reverse z should only have changed the range of the z coordinate in ndc

vec3 screenToView(vec2 screenCoord)
{
    // normalize screenCoord to [-1, 1] 
    // z = 0.0 (near plane in reverse z)
    // w = 1.0
    vec4 ndc = vec4(screenCoord / screenDimensions * 2.0 - 1.0, 0.0, 1.0);

    vec4 viewCoord = inverseProjection * ndc;
    viewCoord /= viewCoord.w;
    return viewCoord.xyz;
}
Ravbug commented 3 months ago

Thanks for the quick response! The change to screenToView seemed to have helped a little bit, but it's still not fully working:

https://github.com/user-attachments/assets/efcdeec5-336c-4e24-aa80-4fbbb61af004

The froxels themselves look fine, so I don't think it has to do with consumption in the fragment shader (here I'm outputting the computed froxel coordinates):

image

Here are my exact grid build and light assignment shaders: cluster_build_grid.csh cluster_assign_lights.csh cluster_shared.glsl

Are there other common mistakes that could cause this?

DaveH355 commented 3 months ago

Well one common problem is the light attenuation affecting fragments beyond the light's defined radius. It can cause those lighting artifacts. This happens when light's radius encompasses only part of a cluster. The light is assigned to the cluster, but not all the fragments in the cluster should be lighted.

So if you're using an attenuation like 1.0 / (dist * dist), you should add a range check.

        if (distanceToFragment >= lightRadius)
        {
            attenuation = 0.0;
        }
Ravbug commented 3 months ago
 if (distanceToFragment >= lightRadius)
{
     attenuation = 0.0;
}

I tried adding this, and it had no effect.

To me, it seems like it's undershading rather than overshading. For example the chunk missing from the red light on the right side of the triangle:

image

This looks like it was erroneously not included in that cluster when it should have been. I'm currently setting the light's radius to well beyond the effective attenuation range, so to me it seems like something else would be causing it.

From my debugging, the radius of the point light was slightly too small to be included in that froxel:

image

Could it be that there's an error in the bounds calculations for the froxels?

Ravbug commented 3 months ago

I found the source of the problem - the C++ code was calculating the wrong radius for the lights (the values were too small). Fixing this fixed the issue. Thanks for your help!

DaveH355 commented 3 months ago

Glad to hear!