Attenuation based on a light radius

Started by
2 comments, last by jollyjeffers 16 years, 3 months ago
Been converting some old OpenGL code across to Direct3D and I came across this light attenuation code I wrote some time ago. Fixed function Direct3D calculates attenuation using an attenuation factor based on three terms: constant, linear, and quadratic attenuation constants. A more intuitive approach would be to base attenuation on a light radius. Where the light is brightest the center of the sphere defined by the light radius. At the edge of this sphere there would be no lighting. My old OpenGL version was based on an article titled Per-Pixel Point Lights and Spot Lights. My updated Direct3D version based on HLSL shaders can be found here.
http://www.dhpoware.com
Advertisement
The argument for the reciprocal-quadratic model is realism. In vacuo, light attenuates according to an inverse-square relationship on distance. The linear and quadratic terms are included as an afterthought to approximate logarithmic attenuation for bright areas (a sort of pseudo-HDR) and to counteract the realistic but usually undesirable sharp approach to the central singularity. Considering that the piecewise-linear model is completely unrealistic, I think it's almost forgiveable that the FFP doesn't support it.

On the other, personal, hand; I totally agree with you. No matter how much you play about with the three parameters (four, including range) it's very difficult to model a light that isn't painfully bright near its source without attenuation giving it very limited range. And for this reason, I almost always find the the 'smoothstep' light you describe preferable.
Ring3 Circus - Diary of a programmer, journal of a hacker.
I experienced the same impression that you had when using inverse quadratic attenuation. Although physically correct it looks way too dark for most of the affected area and too bright close to the light. The tale was cleared up when I learned of "gamma-correct lighting". The idea behind is simple: the graphics card employs a brightness correction when converting the framebuffer values to analog intensities. The human eye senses brightness non-linearly, so the graphics card uses a non-linear correction function to create a linear brightness gradient for the user. The usual function employed is pow( value, 2.2f).

Of course this spoils the lighting calculations of your renderer. It is usually solved by pre-correcting the framebuffer values, for example along with the tone mapping operator. The inverse correction function is then pow( value, 1/2.2f), but a sqrt() will suffice for the moment. For a simple test, just calculate the effect of a single lightsource like you'd do normally, using ndotl and attenuation. Then apply the correction function to that value, then use the corrected value to calculate the final colour from diffuse, light colour, ambient light and whatnot. You'll realise that the light's brightness is now much smoother, with distant areas being lighted better and the sharp gradient close to the light dampened.

Beware though: this is only for testing. To achieve a real correct result, you'd have to calculate everything in physic-correct space and apply the pre-correction at the end. This will also result in overlapping lights looking more natural, specular and diffuse blending in a subtle distinct way, water reflection/refraction mixing looking more natural and so on. It also trashes your textures because they are in fact pre-corrected to show up on screen like they appeared in nature. If you apply the pre-correction globally to the final result, the texture colours used in the process will now be corrected twice. You need to pre-treat the textures to avoid this, preferrably in an offline setup process. There are also colour setups available for photoshop, for example, that allow your artists to use this pre-corrected linear colour system when creating the textures. Precision is better when doing this.

Google for gamma-correct lighting. There are others who wrote about the topic, mostly using much better wording than I'm able to. But I hope it was good enough to get the point across.

Bye, Thomas
----------
Gonna try that "Indie" stuff I keep hearing about. Let's start with Splatter.
I included a couple of paragraphs in my section of our D3D10 book to discuss this point. My general conclusion (iirc) was that the quadratic form was accurate but for purely asthetic reasons a simple linear/smoothstep interpolation yielded better results.

ISTR reading somewhere that the fact realtime graphics tends to use 5-10 lights at most the effect of quadratic attenuation is even more pronounced, so in a way having a linear interpolation tries to approximate and make up for an overly simple model anyway.

One other thing, if you're interested, they can be hard to find but some engineering texts and lighting manufacturers contain distribution graphs in lumens for a given light source. It's quite possible to encode this distribution in a look-up buffer and get some quite impressive results.

Jack

PS - ever tried arbitrary f(x) functions? You can have a laugh with sine wave attenuation for example... neat little special effect [wink]

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

This topic is closed to new replies.

Advertisement