Computing volumetric light effects analytically

In my previous question I have asked about an effect on how to visualize light spheres. The question resulted in a very nice formulated answer which described pretty well how to achieve this by adding some fog to the scene and using some factor to weight different light samples on the rays from the camera to the scene.

However, since performance was too bad when testing it on the Xbox when taking shadows in account (texCUBElod lookups seem to be expensive) in split-screen multiplayer, I wanted to solve the resulting integral (which is currently approximated by a Riemann's sum) analytically without taking shadows into account.

I could reduce the pixel shader code to some formula, but was unable to solve it. So, I've put up another question on the mathematics stackexchange. It was answered fast, but the responder could not come up with any way I could use to solve this in an efficient way.

The question remains, how the performance could be increased without losing awesomeness of the lighting effect. (It does not have to be physically correct, so, if there are methods to increase performance but take away realism, I would like to hear about them.)

Current pixel shader code

-
I'm sorry that my original answer leaded you into dead end ... Hope you did not end waste too much time with that ... –  Notabene Apr 26 '11 at 10:18
Your original answer was very nice and showed us the goal what we want to achieve :-) We are very thankful for your answer there! The question which remains is how to improve efficiency of the method when shadows can be stripped from the volumetric part. –  Etan Apr 26 '11 at 10:21
add comment

3 Answers

We solved the problem by modifying the integral using some approximations. For example, the 0.99^x part has been approximated by a linear decay function. This produced very similar results to the original solution and was solvable analytically in one step.

float a = dot(lightPos - nearPlane , distance_to_wall);
a /= length_distancetowall;
if (a <= -1/dust_factor) {
float b = length(cameraPos + direction*a - lightPos);
float c = 1;
float d = -0.25/30;
float x = length_distancetowall;
if (x > -1/dust_factor)
x = -1/dust_factor;
float integral = (c+a*d)/b*(atan(a/b)-atan((a-x)/b)) + d/2*(log(a*a+b*b)+log(a*a+b*b-2*a*x+x*x));
float lightIntensity=0.0042 * lightRad;
outColor += float4(integral*(lightColor * lightIntensity), 1);
}


With this method, the framerate could be improved drastically. Thanks to notabene for giving the first input and his help at this point again.

-
add comment

There is term called "opacity correction". It enable changing number of samples without changing final result, but also can be used for solving constant density analyticaly:

\delta x is segment length, \delta x' is new segment length. So you can just define some segment length which is reference and it should provide same results(i hope)

Source: real time volume graphics book.

-
add comment