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

Realtime Global Illumination without maps ...

Started by idioglossia Oct 23, 2006 at 6:40 AM 6 replies 2.7k views
Original Post
idioglossia
idioglossia
Hi! :) My situation: I have to develop a good looking illumination for a realtime 3D environment with lots auf buildings with simple geometry (most buldings are just blocks). I am looking for a way to fake a global illumination ... BUT: I won't be able to precompute nice light-, radiosity- or whatever maps because the target system won't have enought disk space for this (it's a 3D navigation system). I found a dynamic ambient occlusion demo in the NVIDIA SDK ... but this one also computes texture maps. http://download.nvidia.com/developer/GPU_Gems_2/GPU_Gems2_ch14.pdf Are there any techniques that can do nice illumination without texture maps? I know that highquality GI isn't possible in realtime but perhaps it's possible to find an image-quality/computation-time compromise because I know that my scenes will be static (houses don't fly around) and my meshes will be little blocks. Any thoughts? Suggestions? :) Thanks and greets!
Thr33d
Thr33d
How about using an environment map for the diffuse lighting? Should look really nice and will give some of those "realistic lighting" cues you're looking for.

I'd recommend taking/generating a sky box, then using ATI's CubeMapGen utility. Read up on it in the link.

If you have specular objects, you can use the original (or filtered) skybox to do environment reflections as well.

This should be a great first step to realistic lighting.
The main aspect it doesn't hit is shadows.
I have some ideas in that area as well, but thought this would be a great start.

-M
lonesock
lonesock
You can do ambient occlusion per vertex...but you may need to tesselate your buildings more finely (i.e. just 1 quad per side will definitely not be enough!)

I have a simple demo using per vertex AO (it's calculated in a very non-standard way [8^).

here

the good news is you can just store the bent normal instead of the regular normal per vertex. And you can even encode the Occlusion value as the length of the bent normal, so no extra storage required, other than the extra verts if you need to tesselate more.

here's the actual workhorse code to approximate AO (no ray shooting).

bool trimesh::compute_ambient_occlusion(){	//	error check	if( verts.empty() )		return false;    int nvert_step = verts.size() / 21;    if( nvert_step < 1 )        nvert_step = 1;	//	for each vertex, add up all "contributions"	//std::cerr << "        [0.........5..........9]" << std::endl;;	//std::cerr << "Status: [";	for( int i = 0; i < (int)verts.size(); ++i )	{	    //*	    if( i % nvert_step == 0 )            std::cerr << ".";        //*/		//	clear out the data for this guy		const vec3 pos = verts.pos;		const vec3 norm = verts.normal;		vec3 bn = vec3( 0.0, 0.0, 0.0 );		//	add in the contributions from all the triangles		for( int j = 0; j < (int)tris.size(); ++j )		{			//	vector from the triangle to the vertex			const vec3 contrib = pos - tris[j].center;			//	is the triangle in front of the vertex?			if( norm * contrib < 0.0 )			{				const vec3_type l2 = contrib * contrib;				const vec3_type dot_p = 0.5 * fabs(tris[j].normal * contrib);				//const vec3_type scalar = 1.0 - 0.25*l2/(radius*radius);				bn += contrib * (/*					scalar * //*/					dot_p / ( 1e-6+l2*l2 ));			}// yep, in front		}		verts.bent_normal = bn;	}	std::cerr << "]" << std::endl;	//	scale all contributions!	const vec3_type largest = 2.0 * 3.14159;	//	2Pi = max solid angle possible (4Pi = sphere)	for( int i = 0; i < (int)verts.size(); ++i )	{		vec3_type l;		//	modify the bent normal to account for the initial solid angle		//	(use the pre-computed solid angle for this vertex)		verts.bent_normal += verts.normal * verts.solid_angle;		//	get the length		l = verts.bent_normal.Length();		//	normalize		if( l > 0.0 )			verts.bent_normal *= 1.0 / l;		//*		//	 and average with regular normal		verts.bent_normal += verts.normal;		verts.bent_normal.Normalize();		//*/		//	compute the occlusion		l /= largest;		//l = 1.0;		//l = verts.bent_normal * verts.normal;		//	don't let it be totally dark!		if( l < 0.2 )			l = 0.2;		//	and no brighter than maximum!		else if( l > 1.0 )			l = 1.0;		verts.color = vec3( l, l, l );	}	return false;}

notes:
* if you add in some optimization and ignore far away buildings, you could even compute this on load.
* this is under MIT, use if you desire
* you can use 2*Pi instead of "verts.solid_angle" if you want (especially if all your buildings are flat, then it's correct)
wolf
wolf
Hey lonesock,
just looked at the demo. Looks impressive. Did you rely your technique on any paper?

- Wolf
lonesock
lonesock
hi, wolf.

No, no papers. I was inspired by sunray's demo here, and wanted to make my own. All the papers that I saw on Ambient Occlusion used either graphics hardware, or shot many rays per vertex. I wanted a technique that could run on unextended OpenGL 1.1, so this was just a per-vertex computation.

Algorithm per vertex:

each vertex starts off with it's bent normal = the standard normal vector scaled by the solid angle of the mesh geometry at that vertex (I have some cool code to approximate this! otherwise, just use 2*PI to approximate a flat plate at every vertex)

then, for each tri in front of it, the solid angle of that tri (approximated as projected area of the triangle / dist_to_center_of_tri^2) is "taken out of" then bent normal (i.e. subtracted from the bent normal

and that's it!

it's a hack (there is an algorithm for the exact solid angle of a triangle as seen from a point, but it was long and complicated (and expensive), but all of ambient occlusion is a hack, so I just wanted to do it faster. It probably would be a good idea to use the exact fomula for tri's that are very close to a vertex (as you can see, the approximation doesn't work up close...the vertex is too bright)

anyway, the magnitude of the bent normal is used as the occlusion value (stored in the vertex color), and the bent normal is normalized and used in place of the regular normal.

You can optimize by ignoring tri's that are farther away than a specified limit, and you can cause the contribution of each triangle to fall off with the distance (make indoor lighting work!)

anyway, glad you liked the demo

note - some keys :
L - lighting
W - wireframe
N - show normals
B - show bent normals

you can also drag your own .OBJ files onto the EXE to see how they work. More tesselated objects look better (it _is_ a per-vertex technique after all), but take longer to compute. Obviously for static scenes this can all be precomputed.
lonesock
lonesock
@AP: I wish! No, it is just computed once after the mesh is loaded; I just meant that if you have a single mesh (like the OP's buildings/city) you could compute and store it in the mesh file instead of on-load.

That having been said, I'm certain you could do some hierarchical bounding scheme and replace groups of tri's with an equivalent sphere or something (so no angle contributions, just a pre-computed area and the distance) as they get farther away from the vertex of interest. Still a lot of work to do on a CPU per frame. May be worth some further investigation. [8^)
Thr33d
Thr33d
Nvidia also has a rather interesting "realtime" AO demo. They conceptually use circular/disc "patches" for each vertex. IIRC they show numbers of ~500k patches/sec in hardware.
dynamic ambient occlusion

-M

Topic Locked

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

Sign in to reply to this topic.