Improved light attenuation

February 10, 2011

In my previous post, I talked about the attenuation curve for a spherical light source:
f_{att} = \frac{1}{(\frac{d}{r} + 1)^2}

I had suggested applying a scale and bias to the result in order to limit a light’s influence, which is a serviceable solution, but far from ideal. Unfortunately, applying such a bias causes the gradient of the curve to become non-zero at limit of the light’s influence.

Here’s the attenuation curve for a light of radius 1.0:

And after applying a scale and bias (shown in red):

You can see that the gradient at the zero-crossing is close to, but not quite zero. This is problematic because the human eye is irritatingly sensitive to discontinuities in illumination gradients and we might easily end up with Mach bands.

I was discussing this problem with a colleague of mine, Jerome Scholler, and he came up with an excellent suggestion – to transform d in the attenuation equation by some function whose value tends to infinity as its input reaches our desired maximum distance of influence. My first thought was of using tan:
d' = 2.1tan(\frac{\pi d}{2d_{max}})
f_{att} = \frac{1}{(\frac{d'}{r} + 1)^2}

That worked well, the resulting curve has roughly the same shape as the original, while also having both a gradient and value of zero at the desired maximum distance. It does have the disadvantage of using a trig function, which isn’t so hot, so we went looking for something else. After a few minutes playing around we came up with the following rational function:
d' = \frac{d}{1-(\frac{d}{d_{max}})^2}
f_{att} = \frac{1}{(\frac{d'}{r} + 1)^2}

It’s very similar to the tan version, but may run faster, depending on your hardware.

Below are some examples of the different methods, using a light with high intensity and small influence. On the left of each is the original image, on the right is the result of a levels adjustment, which emphasizes the tail of the attenuation curve.

Disclaimer: The parameters for the analytic functions were chosen to highlight their different characteristics, not to look good.

Original ray traced reference:

Analytic scale and bias:

Analytic tan:

Analytic rational:

The graphs today were brought to you courtesy of the awesome


3 Responses to “Improved light attenuation”

  1. Matt Enright Says:

    You encouraged me to take another look at my attenuation function this morning. Subtracting off the tail was indeed causing some banding.

    My solution is a bit different, and would gently end any attenuation function regardless of the inner math because it is just multiplied through.

    term = 1 – smoothstep(saturate(d / dmax))^4
    light *= term * att

    where smoothstep(x) = x*x*(3 – 2*x)

    It tends to preserve the brightness of the light until about half way out.

  2. bongju kim Says:

    wow excellent~
    i’m wondering how implement attenuation equation in opengl es 2.0.
    thanks your post.

  3. Jesús Says:

    This post comes a bit late but I just wanted to share my attenuation function, which is simpler and (I think) has all the benefits exposed above:
    f_att = (1 – min(d / dmax, 1))^2;
    Thanks for the great posts in your website 🙂

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: