Skip to main content
GameDev.net gamedev.net
🔒 Locked

radiosity normal map help!

Started by JorenJoestar Mar 19, 2009 at 10:59 AM 7 replies 3.8k views
Original Post
JorenJoestar
JorenJoestar
Hi guys, I want to develop a lightmap generator but I've never done it, and so far I've not found good resources on how to create it! Also I've read about radiosity normal map (the valve paper) and I found it very interesting, but I don't know where to start. Could someone give some hints about lightmap generation and radiosity? Thanks!
---------------------------------------http://badfoolprototype.blogspot.com/
spek
spek
I used this paper:
http://freespace.virgin.net/hugo.elias/radiosity/radiosity.htm

It's an easy to understand approach, no wacky formula's here. It's not complete though. Before starting here, you must also create 'atlas coordinates' for your world. There were several threads here about how to do create those. Or you let them create by your modeling software...

Ow, and about radiosity normalMapping, basically you create the lightMap 3 (or more times) for a certain incoming direction. The paper shows how to catch light coming from all directions. But if you want normalMapping, you could choose to catch light coming from a certain direction. HL2 uses 3 directions (from above, down left, down right, I thought). when shading later on, you mix between the 3 lightMaps depending on the pixel normal.

Succes,
Rick
JorenJoestar
JorenJoestar
Thanks for the link...it is a very good one!
One question if I may...are you calculating it in the cpu or in the gpu?
What is a 'patch' in your case?

Thanks!
---------------------------------------http://badfoolprototype.blogspot.com/
spek
spek
I'm calculating it on the GPU by rendering the surrounding world with the previous pass lightMap / emissive texture. Then I read the pixels, sum the colors and divide for the average. However I think the article is doing it slightly different though. I think they render patch pointers. Then when reading the pixels, we don't have color values but the pointers towards the patches. I'm not sure though.

The biggest problems I found were patches at edges, as part of the hemicube/sphere looks outside the sector. And rendering lightsources. You could render a bright sphere as a point light, but it will loose influence very quickly when the distance between you(camera) and the light 'shape' grows (less pixel on the screen = less influence). Come to think of it, it might be a better idea to do the first pass with direct lighting/shadowMapping. Bake that into a flat lightMap, and then use it for the next pass(es)... Well, I'd say just follow the link and deal with those problems later on. I'm not an expert on lightMaps either :)


A patch in my case is just a pixel on the lightMap. Eventually you could render more (or less) patches than pixels, but for simplicity I just made 1 patch per lightMap pixel. So the bigger the lightMap, the more patches. Besides that, a patch has a
- position 3D coordinate
- 2D coordinates on the output texture
- direction(s) for setting the camera
- reflectance material property (or reference to your materials)
- emissive material property (or reference to your materials)
- Incident / Excident (incoming / outgoing light, see paper)
JorenJoestar
JorenJoestar
Ok...but how do you know the 3d position of the 'patch' ? Is it the position of the corresponding vertex of the lightmap?
---------------------------------------http://badfoolprototype.blogspot.com/
spek
spek
You know your polygon vertex coordinates and their lightMap texture coordinates. Use that information to interpolate 3D and/or 2D lightMap coordinates between those 3 points. Take a look at barycentric coordinates... I can't explain you in detail how that works, as I'm an idiot when it comes to math.

In my generator I calculate those patch coordinates when mapping a polygon onto the 2D image. I calculate the outer bounds of the polygons 2D coords. With that info, I can draw a rectangle on the image. I loop through all pixels with a x/y loop. Before drawing, I first check if the x/y coordinate falls inside the triangle, if not continue the loop. Ifso, draw a pixel. I know the interpolated 2D uv coordinates already, and we can use them to calculate the 3D (world) position:
for x:=polyBounds.offsetX to polyBounds.OffsetX + polyBounds.Width do   for y:=polyBounds.offsetY to polyBounds.OffsetY + polyBounds.Height do       if insideTriangle( poly, x,y ) then       begin           // In case x/y are pixel coordinates, convert them to UV           patch.u := x / image.width;           patch.v := y / image.height;           // Interpolate between the 3D vertex coordinates           // With the help of barycentric coordinates           patch.pos := UV_to_3D( patch.u, patch.v,                                  poly.vertex[0].pos, poly.vertex[1].pos, poly.vertex[2].pos,                                  poly.vertex[0].atlasUV, poly.vertex[1].atlasUV, poly.vertex[2].atlasUV                                   );       end;...      function  UV_To_3D( const u,v       : single;                          const v1,v2,v3  : TVector3f;                          const t1,t2,t3  : TVector2f  ) : TVector3f;      // u,v is interpolation input,      // v1,v2,v3 are the 3D vertex coordinates we must interpolate between      // t1,t2,t3 are the 2D vertex atlas coordinates      var          f1,f2,f3  : single;       begin            if (vecCompare(t1,t2)) and (vecCompare(t2,t3)) then            begin                 // I think this and should be an or...                result := v1;            end else begin                { Calculate barycentric coordinates }                baryCentric( t1[0], t1[1],                             t2[0], t2[1],                             t3[0], t3[1],                             u,v,                             f1,f2,f3 );                result[0] := v1[0] * f1 +   v2[0] * f2 +   v3[0] * f3;                result[1] := v1[1] * f1 +   v2[1] * f2 +   v3[1] * f3;                result[2] := v1[2] * f1 +   v2[2] * f2 +   v3[2] * f3;            end;      end; // UV_To_3D3      // compute the barycentric coordinates of a 2d point inside a 2d triangle      // (x0, y0) (x1, y1) (x2, y2) are the vertices of a 2d triangle      // (vx, vy) is a point inside the 2d triangle      // u, v, w are the barycentric coordinates of (vx, vy) in the triangle      procedure baryCentric( const x0,y0,x1,y1,x2,y2 : single;                             const vx,vy  : single;                             var   u,v,w  : single );      var a,b,c     : single;          totalArea : single;          length0,          length1,          length2   : single;      begin            // compute the area of the big triangle            a := dist2D(x0, y0, x1, y1);            b := dist2D(x1, y1, x2, y2);            c := dist2D(x2, y2, x0, y0);            totalarea := triarea(a, b, c);            // compute the distances from the outer vertices to the inner vertex            length0 := dist2D(x0, y0, vx, vy);            length1 := dist2D(x1, y1, vx, vy);            length2 := dist2D(x2, y2, vx, vy);            // divide the area of each small triangle by the area of the big triangle            u := triArea(b, length1, length2)/totalarea;            v := triArea(c, length0, length2)/totalarea;            w := triArea(a, length0, length1)/totalarea;      end; // baryCentric      // compute the area of a triangle using Heron's formula      function  triArea( const a,b,c : single ) : single;      var s : single;      begin            s       := (a+b+c) / 2;            result  := sqrt( s * (s-a)*(s-b)*(s-c) );      end; // triArea


I haven't checked the code, but something like this should work. However, I'm npt 100% sure if the 3D positions are in all cases correct. And also watch out with the polygon mapping. A common problem are the edges. You could map the polygon bigger on the image than they actually are, just to make sure your uv coordinates don't cover unfilled pixels.

Greetings,
Rick
jpventoso
jpventoso
I am doing the approximate same method as spek and I can tell you it works just fine =) and using the GPU allows you to get this to work faster and with less coding.

For reference, these were the learning steps for me:

- How to generate an UV atlas for a mesh. I used plannar mapping technique (this article was really helpful: Click)
- How to pack the single lightmaps into a big texture using a binary tree (Click)
- Understanding the radiosity principles and using the GPU to accelerate and simplificate the luxel calculation (Click)
- Optimizing the code and the results (f.e. applying a final blur step).

On the other hand, I used a different approach to the normal mapping problem. I render my direct-lighting lightmaps separated from the radiosity lightmap (this also allows me to apply reallistic dynamic shadows) and I store the light/shadow factor only (without N.L). Then, on the scene, I do the N.L (with normal mapping) on the pixel shader and multiply it with the lightmap lookup.

These are my results:



Hope this helps =)
JorenJoestar
JorenJoestar
Thank you very much guys!

@jpventoso: these are the steps I need...because I've never done a lightmapper! :P
Your technique seems very interesting...and the screenshots are beaufitul!
Are you using it in your engine?

@spek: your code is self-explanatory, it will be a great brick on my wall :D


By the way, I found another link that can be really interesting:

http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter39.html

Thanks again!
---------------------------------------http://badfoolprototype.blogspot.com/
jpventoso
jpventoso
Quote:
Original post by JorenJoestarYour technique seems very interesting...and the screenshots are beaufitul!
Are you using it in your engine?


Thanks!

Yes, I'm using lightmaps + precomputed ambient occlusion for static objects and dynamic lighting + SSAO for dynamic objects. The results are good so far =)

I saw that article, its a good guide for certain parts of the lightmap generation. Good luck and I hope to see the results =)

Cheers,
Juan Pablo

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.