View Full Version : Doom3 like attenuation ?

05-24-2005, 10:25 PM
I need to obtain an attenuation like the doom3`s.
Especially the effect when the angle between the light and the surface is large the light contribution is smaller.
But NOT using bump mapping.

Also in doom3 when the light is closer to the surface the light contribution gets smaller instead of overlighting the surface.

05-25-2005, 01:39 AM
If you have a copy of doom3, you can just look how the attenuation's done there. Just open the pak file with a zip program.

05-25-2005, 05:18 AM
dotproduct = dot(normal, lightvector);
//and use dotproduct as is


dotproduct = dot(normal, lightvector);
newdotproduct = texture1D(texture, dotproduct);

where the texture contains your custom values.
The first code is standard fixed function, but the second requires a shader.

05-25-2005, 06:31 AM
ok..so what do I do with this dot product ? :)

In the d3 shaders they calculate atten as usual, how texcoords are calculated is the thingy

05-25-2005, 08:01 AM
Read that again.

V-man's second line is a lookup in a texture.

Guess where you can change the attenuation.

05-25-2005, 06:43 PM
Originally posted by Undertaker:
ok..so what do I do with this dot product ? :)

In the d3 shaders they calculate atten as usual, how texcoords are calculated is the thingyLook in the red book about the lighting equation GL uses. It goes in the diffuse ligthing part.

I don't know about Doom3. Post the shader here and we will all have a look.

05-27-2005, 12:17 PM
If you're wanting to do an attenuation, I don't think you need to use the dot product. That'll give you the effect of the surfaces which are facing the light most directly being lit more intensely, which you mentioned above, I think - but attenuation (like what doom 3 does) is a function of distance from the light source. If you're unclear on what to do with the dot product and the attenuation, consider that they are both measures of the amount of light energy that is reaching a surface (so you're going to multiply them with the color of your fragment from a texture or whatnot). The dot product will give you an approximation of how light bounces diffusely when it hits the surface, and the attenuation factor will tell you how intense the light is when it hits the surface (because light gets weaker as it's energy is spread over a greater distance).

attenuation might look something like...

atten = A / d
atten = A / d^B

where d is the distance from the fragment to the light source you're evaluating. You can play around with the variables 'A' and 'B' to get an effect that appeals to you. 'A' sort of controls when the light starts to get really bright (when d is less than A) - essentially the light's strength, and 'B' essentially controls the rate at which attentuaion occurs - so B = 2 will probably give you something realistic looking.

So take atten and multiply it by your fragment color, and multiply by the dot product, too. If you use both of these values (the dot and the atten), you'll start to get something doom3-ish. This is pretty fast, too - so you can definitley use these functions in a scene with 8 lights or whatever.

05-27-2005, 05:07 PM
inline float DOTPRODUCT( const VEC3& v1, const VEC3& v2 )
return ( v1.x * v2.x + v1.y * v2.y + v1.z * v2.z );

inline float LENGTHOFVECTOR( const VEC3 &v )
return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);

inline float SQUAREDLENGTHOFVECTOR( const VEC3 &v )
return (v.x * v.x + v.y * v.y + v.z * v.z);

dotproduct gives the squared distance of a vector, thus u can measure how far away something is
personally though WRT to calculating attenuation i prefer to use something linear like
float attenuation = (end-dist)/(end-start)
which gives an exact cutoff point, with the standard method even if youre a million miles away from the lightsource will never have 0 just a really small number which feels messy (even though it might be more inline with whatever reality does)

05-27-2005, 07:33 PM
To Undertaker - if you use the dot product as a measure of distance though(as above), make sure you don't normalize the lightVector

where lightVector is used in dot(normal, lightVector)

if you read in the openGL books, you will probably see them normalizing the lightVector because they don't use it to measure distance.

06-02-2005, 02:43 AM
OK ,I used the dot prod like in :
(1-(d/r)^2) * dot , and it works...but not correctly because I calculate the light vector from a vertex to the light.
How can I interpolate the light vector across the surface for correct result ?

Jens Scheddin
06-02-2005, 03:25 AM
Originally posted by Undertaker:
OK ,I used the dot prod like in :
(1-(d/r)^2) * dot , and it works...but not correctly because I calculate the light vector from a vertex to the light.
How can I interpolate the light vector across the surface for correct result ?Pass the light vector as a texture coordinate. This will interpolate the vector across the polygon.

06-02-2005, 03:53 AM
It still looks wrong in places where 2 or more polygons are part of the same plane and the light lights them both.

06-02-2005, 09:49 AM
I got it working but only with fragment shaders.
I don`t know how to do it without them, that`s what I needed in the first place.

06-02-2005, 03:11 PM
Try this: get the light vector in the vertex program, don't normalize it, but instead scale it by the inverse of the light radius. This will let it interpolate correctly and also give you your squared attenuation by doing a dot product of this vector with itself in the fragment program. The squared attenuation will be from 0 to greater than 1, where >= 1 would be outside the light radius. So then you could do (1 - dot(lightvec, lightvec)) and clamp the result to [0, 1] to get your attenuation factor. As for the usual dot(normal, lightvec) part, you will still need the normalized light vector, which you can get either by passing the light vector (scaled or not, doesn't matter) through a normalization cubemap or by normalizing it in the fragment program. Doing the normalization in the fragment program is faster and much better looking than using a cube map on my radeon 9800 pro, but cubemaps are probably better for geforce fx and older cards. Using this approach I was able to get nice bump mapping with specular and attenuation in a single pass on a geforce 3 using register combiners, or the equivalent on ATI.

06-02-2005, 11:07 PM
What you said was tell me what formula I`m currently using , (1-(d/r)^2) * dot :)

I need to do the (...* dot ) part without pixel shaders, using combiners or something.

06-02-2005, 11:21 PM
Have alook at http://www.ronfrazier.net/apparition/index.asp?appmain=research/per_pixel_lighting.html

for low end per pixel attenuation using 1-D^2