Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

On one of the slides from "DirectX 11 Rendering in Battlefield 3" PowerPoint I noticed the folowing code:

struct Light {
    float3 pos; float sqrRadius;
    float3 color; float invSqrRadius;
}

I don't understand why would they store the squared radius and even the inverse squared (which I believe is simply 1-squared radius) instead of simply storing the radius? How are they using this data in their computations? Moreover, what about cone and line lights? This struct must be only for the point lights, I can't see it working for other types - there is not enought data. Still I would love to know how they use that square and invSquare.

UPDATE: Ok I finally got it.

Here is the classic light attenuation equation, easily found on the net:

float3 lightVector = lightPosition - surfacePosition;

float attenuation = saturate(1 - length(lightVector)/lightRadius);

It is relatively costly as length(lightVector) is actually doing this:

length(lightVector) = sqrt(dot(lightVector, lightVector);

moreover division operation (/lightRadius) is also quite costly.

Instead of computing light attenuation this way, you can compute it the following way, which would be much faster:

attenuation = saturate(1 - dot(lightVector, lightVector)*invRadiusSqr);

where invRadiusSqr can be pre-computed at CPU level and passed as a shader constant.

Moreover, you get a quadratic light attenuation as a result (instead of linear in the former case), which is even better, as IRL light has shown to have quadratic falloff.

Thanks everyone for your help!

share|improve this question
13  
Padding. Shaders are 16bytes aligned. And if you take a look at how the code is formated, you will see that it will get packed into two float4. Why not store something usefull when you will get it from free from the cache? –  Tordin Apr 16 at 7:04

3 Answers 3

up vote 23 down vote accepted

This is simply a kind of optimization given that invSqrRadius = 1/SqrRadius, instead of calculating the inverse squared radius for each light every time they simply cache it, the reason is that division is usually a "slow" operation at least when compared to multiplication.

This optimization is relevant especially:

  • when the operation is done a huge amount of times for each light, so caching the value will free extra CPU/GPU cycles
  • And assuming the memory access time for reading the value is actually faster than recalculating it.

Regarding how it is used, I am not sure about their specific implementation, but regarding 1/sqrRadius, this is simply used for light attenuation, falloff and culling. It's also relevant for directional and spotlight, the only difference in case of spotlight is that you need to calculate the spotlight factor after applying the attenuation. Regarding directional lights like sun, it usually doesn't have any attenuation or falloff, so I would guess it will be ignored.

[EDIT] Just to elaborate more, it is not irrelevant data. The light Irradiance can be calculated using the following equation:

E = Phi / 4*pi* rSqr;

Where

E is the area density of flux.

Phi is radiance flux.

4*pi*rSqr is the surface area of a sphere.

This equation explains why the amount of energy received falls off with the squared distance.

Another point is you need to calculate distance between the vertex and the light to calculate the light contribution on a specific vertex (this value is unlikely to be cached), the vertex can be in or out the light range which brings us to the next point where Radius Square is useful for culling.

If you want a practical example for calculating light falloff and culling, this is especially useful in tiled based deferred renderers, here is one example.

share|improve this answer
    
Curses, you beat me by 7 seconds! ;) (and with a more comprehensive answer, too!) –  Trevor Powell Apr 16 at 7:18
    
Thanks for the detailed comment, esp for the link! From what I understood from the latter link, Battlefield 3 stores not the radius but the actual distance between the light source and the light receiver, right? That's the "d" value they use in the article. –  cubrman Apr 16 at 8:39
    
@cubrman it's hard to speculate without seeing code. My guess is that it is the inverse radiusSqr. And the equations they use might greatly vary from the article. –  concept3d Apr 16 at 12:11
    
But tell me, how can you use a bare inverted light radius squared in a lighting calculation? Every source I found on the net tells me that I need to find the DISTANCE between the receiving surface and the light source, divide the latter by the original light radius AND THEN square the result. Where would you ever use squared radius or invSqrRadius? That seems like completely irrelevant data to me. –  cubrman Apr 16 at 16:51
1  
@cubrman updated the answer. –  concept3d Apr 16 at 17:30

invSqrRadius isn't 1 - sqrRadius; it's 1 / sqrRadius.

It means that you can multiply by invSqrRadius, instead of dividing by sqrRadius (as division is typically much more expensive than multiplication)

share|improve this answer

The other answers here dealt with the inverse square radius, but I'm going to look at the squared radius instead (which concept3d touched on, but I believe it merits further discussion).

What squares are useful for is distance comparisons. We know that calculating the distance between two points involves a square root, and square roots are expensive to calculate, but if all we want to do is compare distances (in order to find which is less or greater, and do something interesting based on the result) we can throw out the square root.

If sqrt(x) > sqrt(y) then it's also the case that x > y.

For a light, the squared radius is the same as the distance between the light's center and it's maximum extent - squared, of course.

For lighting computations this can be used for an early-out case. If the distance between the point you're lighting and the light's center (squared) is greater than the squared radius, the point recieves no light and you don't need to run the rest of your computations. This is therefore just an optimization (a fairly common one) - we can use squared radius to do the distance comparison without expensive square roots, and at a cost of just a subtraction and dot product.

I, of course, don't know if this is exactly what BF3 is using it for, but I would expect that I'm not too far off the mark.

share|improve this answer
    
So if I understood you correctly, the code will be: if (dot((lightPos - surfacePos), (lightPos - surfacePos)) > lightRadiusSqr) don't do lighting, right? –  cubrman Apr 17 at 5:46

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.