Deferred Shading and attenuation

2020-07-23 08:40发布

问题:

Recently I added deferred shading support in my engine; however I ran into some attenuation issues:

As you can see, when I'm rendering the light volume (sphere), it doesn't blend nicely with the ambient part of the image !

Here is how I declare my point light:

PointLight pointlight;
pointlight.SetPosition(glm::vec3(0.0, 6.0, 0.0));
pointlight.SetIntensity(glm::vec3(1.0f, 1.0f, 1.0f));

Here is how I compute the light sphere radius:

Attenuation attenuation = pointLights[i].GetAttenuation();
    float lightMax = std::fmaxf(std::fmax(pointLights[i].GetIntensity().r, pointLights[i].GetIntensity().g),
        pointLights[i].GetIntensity().b);

    float pointLightRadius = (-attenuation.linear +
        std::sqrtf(std::pow(attenuation.linear, 2.0f) - 4.0f * attenuation.exponential *
            (attenuation.constant - (256.0f / 5.0f) * lightMax))) / (2.0f * attenuation.exponential);

And finally, here is my PointLightPass fragment shader:

#version 450 core

struct BaseLight
{
    vec3 intensities;//a.k.a color of light
    float ambientCoeff;
};

struct Attenuation
{
    float constant;
    float linear;
    float exponential;
};

struct PointLight
{
    BaseLight base;
    Attenuation attenuation;
    vec3 position;
};

struct Material
{
    float shininess;
    vec3  specularColor;
    float ambientCoeff;
};


layout (std140) uniform Viewport
{
    uniform mat4 Projection;
    uniform mat4 View;
    uniform mat4 ViewProjection;
    uniform vec2 scrResolution;
};

layout(binding = 0) uniform sampler2D gPositionMap;
layout(binding = 1) uniform sampler2D gAlbedoMap;
layout(binding = 2) uniform sampler2D gNormalMap;
layout(binding = 3) uniform sampler2D gSpecularMap;

uniform vec3 cameraPosition;
uniform PointLight pointLight;

out vec4 fragmentColor;

vec2 FetchTexCoord()
{
      return gl_FragCoord.xy / scrResolution;
}

void main()
{
      vec2 texCoord = FetchTexCoord();
      vec3 gPosition =      texture(gPositionMap, texCoord).xyz;
      vec3 gSurfaceColor =  texture(gAlbedoMap, texCoord).xyz;
      vec3 gNormal =        texture(gNormalMap, texCoord).xyz;
    vec3 gSpecColor =   texture(gSpecularMap, texCoord).xyz;
    float gSpecPower =  texture(gSpecularMap, texCoord).a;

    vec3 totalLight = gSurfaceColor * 0.1; //TODO remove hardcoded ambient light
    vec3 viewDir = normalize(cameraPosition - gPosition);

    vec3 lightDir = normalize(pointLight.position - gPosition);
    vec3 diffuse = max(dot(gNormal, lightDir), 0.0f) * gSurfaceColor *
                    pointLight.base.intensities;

    vec3 halfWayDir = normalize(lightDir + viewDir);
    float spec = pow(max(dot(gNormal, halfWayDir), 0.0f), 1.0f);
    vec3 specular = pointLight.base.intensities * spec /** gSpecColor*/;

    float distance = length(pointLight.position - gPosition);
    float attenuation = 1.0f / (1.0f + pointLight.attenuation.linear * distance
                    + pointLight.attenuation.exponential * distance * distance +
                     pointLight.attenuation.constant);



    diffuse *= attenuation;
    specular *= attenuation;
    totalLight += diffuse + specular;

    fragmentColor = vec4(totalLight, 1.0f);
}

So what can you suggest to deal with this issue ?

EDIT : Here are more details :

For deferred shading,

  • I populate my GBuffer;
  • I make an ambient light pass where I render a fullscreen quad with the ambient colors :

    #version 420 core

    layout (std140) uniform Viewport
    {
        uniform mat4 Projection;
        uniform mat4 View;
        uniform mat4 ViewProjection;
        uniform vec2 scrResolution;
    };
    
    layout(binding = 1) uniform sampler2D gAlbedoMap;
    out vec4 fragmentColor;
    
    vec2 FetchTexCoord()
    {
          return gl_FragCoord.xy / scrResolution;
    }
    
    void main()
    {
        vec2 texCoord = FetchTexCoord();
        vec3 gSurfaceColor =    texture(gAlbedoMap, texCoord).xyz;
    
        vec3 totalLight = gSurfaceColor * 1.2; //TODO remove hardcoded ambient light
        fragmentColor = vec4(totalLight, 1.0f);
    }
    

Then I pass my point lights (see code above);

回答1:

The reason you're having this problem is that you're using a "light volume" (a fact that you didn't make entirely clear in this question, but was brought up in your other question).

You are using the normal light attenuation equation. Well, you'll notice that this equation does not magically stop at some arbitrary radius. It is defined for all distances from 0 to infinity.

The purpose of your light volume is to prevent lighting contributions beyond a certain distance. Well, if your light attenuation doesn't go to zero at that distance, then you're going to see a discontinuity at the edge of the light volume.

If you're going to use a light volume, you need to use a light attenuation equation that actually is guaranteed to reach zero at the edge of the volume. Or failing that, you should pick a radius for your volume such that the attenuated strength of the light is nearly zero. And your radius is too small for that.

Keep making your radius bigger until you can't tell it's there.