OpenGL Library for Delphi,
maintained by Eric Grange,
based on Mike Lischke's GLScene.

 

Oldest known version of this page was edited on 2006-11-12 11:37:33 by DintHer []
Page view:
Lightmaps versus real time lighting By Rick
Lightmaps are quite an old trick to get the lighting done, Quake1 already used them. In other words, its a very fast/simple technique, way faster than real time lighting. Until recently, dynamic lighting was impossible. Doom3 / F.E.A.R. are now using real time lighting, but it still has its limits. The big problem with real time lighting is that the light isn't very realistic. In reality we have soft-edges shadows, and indirect lighting. Turn off the light in your bedroom, and its still not 100% dark. Why? Because light from other sources bounces of other surfaces and still come into your room.

This technique can also be implemented for lightMaps (which is often called radiosity). It's quite heavy to compute that, but that's not a problem since you only pre-calculate the lightMap once. So, you can add more realistic techniques to your lighting, which are (still) impossible to do with realtime lighting. However, the big problem is that a lightMap can't really change once computed. So moving/flickering lights are very difficult/impossible to implement, which is a big drawback for more futuristic or heavily lighted scenes. Nowadays it's a big designer decision, real time lighting for dynamic effects, or a more realistic/faster lightMap? Halflife2 used a lightMap, Doom3 real time, etcetera.

LightMap production:
Anyway, a lightMap is ussually one (big) texture for the whole level. The static mesh part. Objects like doors/boxes/characters can't use the lightMap, since they are dynamic and can move around. So, basically you calculate (second) texture coordinates for your static mesh, which is an unwrap that fits in 1 texture without tiling/overlapping polygons. Then you can start calculating the lightMap. Most pixels on the lightMap (or even smaller points) are connected to a little piece of polygon in the world. We call this "patches". You have to calculate for each patch how much light it receives. You can do this by raytracing, and/or rendering. With raytracing you put a line between a lightSource and the patch. If it's not interrupted, then it means the light is shining on the patch, so add it:

for each light do
<raytrace from light to patch (or vice versa)>
if not collision then
patch.incidentLight += light.color * <distance & falloff factor>

Radiosity / indirect lighting:
This is "direct lighting". If you want indirect lighting(radiosity), you must not only check which lights are coming in for a patch, but also all other patches. A patch can reflect a certain amount of light (depending on its material). For example, a shiny plastic red wall reflects reddish light. So, nearby objects also catch light from that red wall indirectly (which is called "color bleeding"). You can calculate this too by raytracing, but a (way) faster approach is doing it by rendering. The incident (incoming) light of a patch, depends on "what the patch sees". So, put the camera in that patch, and rendering from that viewpoint. For more realistic results, you should render a hemisphere or hemicube, with a filter. Light coming from straightforward has more influence than it comes from a larger angle (Lambert Cosine Law). When you rendering everything around the patch (including the lights), fetch the rendering to a snapshot. Then scale it down to a small texture. 16 x 16 pixels for example, or even smaller. Than sum all pixel values, and divide it for an average. This color is your incident, and can be used as the pixel color on your lightMap.

If you want indirect lighting, you should do the whole process multiple times (more than 1 pass). After the first pass, all patches have calculated their incident light. When the patches have their incident, they can reflect it:
excident = emission + incident * reflection

emission is what the patch shines from itself. Normal walls have no emission (black), while lightSources have a relative high emission. For even better results you can do it on floating point buffers, for a High Dynamic Range lightMap. Anyway, if you render the second pass, each patch should use its excident color. So, now patches are catching more light, since patches around them are brighter too (unless there's really no light there). No patches also catch indirect light. You can do as many passes as you want (but it takes time of course). Once the patches don't change a lot
anymore, the process is "stabilized", and you store the patch incidents to an image, which is the resulting lightMap.

NormalMapping & lightMaps
Nowaydays many games are using normalMapping. A shader that does normalMapping needs to know from which direction the light is coming, and its color (and maybe more factors like distance / fall-off). This is a big problem when using lightMaps. The lightMap image can tell for each pixel what color it should be, but not where the light is coming from. And what if you have 10 lights? Then you need to know 10 directions as well (if a pixel is catching all 10 lights). A normal lightMap isn't suitable for normalMapping, but there's a solution though. It's not 100% accurate, but
it's good enough if you don't have real flashy/complex lighting.

In Halflife2 they use "Fake Radiosity NormalMapping". They don't calculate 1 lightMap, but 3. When measuring the incoming light for a certain patch, they do it in 3 steps. First they check what light comes from left/under, then from right/under, and at last from above (or 3 other directions, whatever you like). Those are 3 different directions, so you also have 3 different results. You can even do more directions if you like, but 3 is pretty good so far. Light that comes from left/under (from the patch viewpoint) is stored in lightMap1, from right/under in lightMap2, etcetera. Well, in fact
everything is stored in 1 lightMap, each part gets 1/3 of the size.

Now when rendering the pixels in the game, the shader does 3 lighting computations. It knows the 3 colors (by using an offset in the vertex shader):
Calculate lightMap texture coordinates.
lightMap1UV.xy = lightMapUVcoords.xy;
lightMap2UV.xy = lightMapUVcoords.xy;
lightMap3UV.xy = lightMapUVcoords.xy;
lightMap2UV.x += 0.333;
color 2 is the mid section
lightMap3UV.x += 0.666; color 3 is at 2/3 of the image

lightColor_leftUnder = tex2D( lightMap, lightMap1UV.xy);
lightColor_rightUnder = tex2D( lightMap, lightMap2UV.xy);
lightColor_above = tex2D( lightMap, lightMap3UV.xy);

Now you that you have those 3 colors from 3 different directions, you need to make a balance between them. You can do this with an ordinary normalMap. The normal is used to calculate the balance between the 3 colors. If the normal is pointing towards left, then obviously the color that comes from the left should have a high influence. If the normalMap is plain flat (no bumps), than the resulting color is 1/3 of color1 + 1/3 of color2 + 1/3 of color3.

Links:
http://freespace.virgin.net/hugo.elias/radiosity/radiosity.htm
www2.ati.com/developer/gdc/D3DTutorial10_Half-Life2_Shading.pdf


Rick
Valid XHTML 1.0 Transitional :: Valid CSS :: Powered by Wikka Wakka Wiki 1.1.6.2
Page was generated in 0.0796 seconds