Real-Time Radiosity

Real-time computer graphics generally uses only direct illumination, which is the simulation of light that is emitted from a light source, bounces off one surface, and then hits the eye/camera. For realistic images, it is essential to simulate not only direct illumination, but light that bounces off two or more surfaces on the way to the camera. This is called global illumination. In most real-time graphics, such as in computer games, the indirect illumination is approximated by a constant term, eg. the ambient term in the Phong lighting equation[1][2]. The problem with this is that it looks very flat.

Radiosity

Radiosity is a special case of the global illumination problem, in which all surfaces are perfectly diffuse reflectors (Lambertian). This means stuff like specular reflection, glossy reflection, transmission etc.. doesn't have to be dealt with.

Real-time direct lighting on modern hardware

Real-time direct lighting, from a point light, with shadowing, on modern 3D-acceleration cards is very doable. A commonly used algorithm is stencil shadow volumes[3]. A simple scene, lit with one point light, and rendered with shadows, can easily be rendered in less than 10ms (i.e. > 100 FPS) on modern hardware. The lighting equation is either evaluated at vertices, and interpolated across triangles, or evaluated for each fragment using fragment shaders.


The scene lit with only direct illumination. Staggered lights are used to achieve soft shadows

Real-time radiosity techniques

There seem to be several techniques for real-time radiosity on graphics hardware. The first is a gathering approach: Illumination incident at a point is evaluated by rendering the scene from the point, usually rendering each hemicube surface into a pbuffer. Weighted pixel colours are then summed to get the irradiance at that point, or spherical harmonic coefficients are computed from the pbuffers, if directional radiance information is needed. Such a technique is described in [4]. Coombe et al. present a progressive refinement technique that uses hardware hemisphere rendering for form factor calculation in [5]. Another technique is instant radiosity:

Instant radiosity

I had the idea of approximating light reflected off surfaces by a series of point lights. Turns out this had been done back in 97, and was called 'instant radiosity'[6] :P
Read the instant radiosity paper for a good technical description of the algorithm. Basically, rays are traced out from the light source into the scene. At the point where the rays hit a surface, a point light is added to approximate the reflected light from that part of the scene. The ray keeps bouncing around the scene, being attenuated by the diffuse reflectivity of the surfaces it bounce off. A Russian roulette scheme is used to terminate the ray after a finite number of bounces, while being un-biased. This is a Monte-Carlo technique because it uses random new directions for the rays when they are reflected from a surface.

N rays are emitted from the light source, where N needs to be at least 100 to get a good image. Depending on the Russian roulette scheme used, something like 2N light sources will be added at ray intersections. Finally, the scene is rendered, using only direct illumination from the primary light source, plus direct illumination from each of the ~2N 'secondary' light sources. Tracing the rays should be a trivial part of the frame computation time. The bulk of the time is used to compute lighting from 200 or so point light sources.


The scene lit with full direct + indirect illumination. Locations of secondary light sources are shown with yellow spheres


The scene shown with only indirect illumination.


The scene shown with lighting from one of the many secondary lights. The intensity is exaggerated to show it clearly. Note that because the reflecting surface is green, only green light is reflected


The scene lit with full direct + indirect illumination. Shadows from secondary lights are enabled, leading to a low framerate.

Gamma correction

Gamma correction is performed so that the monitor intensity of each pixel is proportional to the intensity computed in the scene. This is done by copying the framebuffer to a texture, then rendering the texture back to the framebuffer, raising each colour component to power of 1/2.2 with a GLSL fragment shader program.

Precision issues

When adding the contribution from many lights together into one image, precision issues inevitably arise. I use the accumulation buffer in OpenGL to store the intermediate image as light contributions are progressively added. Whereas the framebuffer on a 3d card usually has 8 bits per colour channel, the accumulation buffer has more precision, and allows floating point operations on it to minimise precision loss. The downside of using the accumulation buffer is that using pixel buffers (PBuffers) as intermediate buffers is more efficient, as they can be bound directly as textures, avoiding a copy operation from framebuffer to pbuffer when doing a final gamma correction pass.

Care must also be taken when rendering the illumination from secondary lights. They are typically so dim, that massive rounding errors occur due to limited framebuffer precision, unless the lights are given a intensity boost, and then a compensating attenuation is made when copying to the accumulation buffer.

Gamma correction also accentuates limited precision for smaller colour values, contributing to banding artifacts in dark regions of the image.

Some of these issues could be avoided by using a fully floating point rendering pipeline, with floating point pbuffers. While such a pipeline is possible today, it requires the use of fragment shaders to output the floating point colour values, and it is in general a lot slower than using the fixed-function pipeline.

Optimisations/Hacks

One hack is to not use shadows when computing illumination from secondary light sources. This allows the frame to be drawn in much less time, because for example 8 lights can be drawn in one pass. When shadows are used, the stencil volume shadow algorithm requires at least one pass per light. Thus not drawing shadows from the secondary lights can result in 8 times less passes, and a corresponding reduction in 'fill-rate' used. The downside to using this hack is that it results in an incorrect image; in particular 'light leakage' occurs, which is when indirect light 'leaks past' an occluder to illuminate the far side of it. To prevent surfaces on the back side of the light-reflecting surface from being illuminated when shadows are disabled, spotlights with a cone angle of 90 degrees are used, so that only surface points on the front side of the surface are illuminated.


(Exaggerated) illumination from one secondary light source, with shadows.


(Exaggerated) illumination from one secondary light source, with no shadows.

Intensity divergence

One of the issues that arises when approximating illumination from a surface with a series of point lights is that very near the point light, as the intensity is proportional to the inverse square of the distance, the intensity goes to infinity. This manifests itself as very bright spots in the image, always found at the concave edge shared between two intersecting surfaces. This can be worked around by setting the constant coefficient in the OpenGL lighting equation to a non-zero value, so that the illumination from a light never exceeds a certain threshold. The intensity then takes the form A/(B + r2) instead of A/r2. This has the side effect of lowering the overall brightness of the scene, but it's worth it in the end :)

Performance

GeForce FX 5200 (really old and crappy card)

FPS for the Cornell box scene shown in the pictures above with about 200 secondary lights, and without secondary shadows: ~5.3 FPS
with secondary shadows: ~1.1 FPS

GeForce 6800gt: (courtesy of Warpath)

FPS for the Cornell box scene shown in the pictures above with about 250 secondary lights, and without secondary shadows: 60-100 FPS
with secondary shadows: ~17 FPS

DAMN that's a fast card :)

Demo

Requirements: Windows, newish drivers (needs GLSL support among other things), newish 3D card (approx GeForce FX 5200+, Radeon 9000 +).

rtrad.zip v1.0

More screens

Credits

Written by Nik Chapman a.k.a. Ono-Sendai, 2004.

Thanks to SnowKrash for comments and for finding the Instant Radiosity paper.

References

1: Illumination for computer-generated images, Communications of the ACM, Volume 18, Issue 6, June 1975
2: Phong For Dummies (Delphi3d.net)
3: Practical and Robust Stenciled Shadow Volumes for Hardware-Accelerated Rendering, Cass Everitt and Mark J. Kilgard March 12, 2002
4: Real-Time Global Illumination on GPU
5: Radiosity on Graphics Hardware, Coombe et al.
6: Instant Radiosity, Computer Graphics Proceedings, Annual Conference Series, SIGGRAPH 97, pp. 49-56.


Back to Homepage.