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

Bent Normals & Ambient Occlusion for heightmaps

Started by lonesock Oct 20, 2007 at 8:56 PM 2 replies 18.7k views
Original Post
lonesock
lonesock
I have been looking for a quick / simple way to compute bent normals and Ambient Occlusion style information for heightmaps. Having not found anything I came up with something which does a decent job and is decently quick [8^) Quick Overview: * computes bent normals for a heightmap * computes an occlusion term and scales the bent normals (so a fully occluded vertex would have a bent normal length of 0.0, while a fully un-occluded vertex would have a bent normal of length 1.0) * time is O(pixels * LOD), so a 512x512 takes ~4x longer than a 256x256 (given a fixed LOD, I'm recommending LOD=8) Here is the zip with full source and a demo. Press the up/down arrows to cycle through LOD levels (hint: the effect is most apparent as you go from LOD=0 upward). The demo will generate a random terrain when you run it. Alternatively drag an image onto the exe and it will be loaded as a terrain heightmap. Check the readme.txt for key bindings, implementation notes, etc. All my code in there is under MIT license, except SOIL which is public domain. Before no Ambient Occlusion After with Ambient Occlusion Note, the effect may be subtle or hard to spot in the above pics, so feel free to watch it in action. Comments welcome! [Edited by - lonesock on October 21, 2007 2:06:37 AM]
yahastu
yahastu
Quote:
Original post by lonesock
I have been looking for a quick / simple way to compute Ambient Occlusion style information for heightmaps. Having not found anything I came up with something which does a decent job and is decently quick [8^)


Hmm, well Im not sure what youre talking about with the bent normals...but the normal way to do AO maps is pretty simple and easy...you just take a random sampling of rays coming out of the hemisphere oriented at the local surface normal and count the number of rays that are self intersecting with the original mesh. The ratio of intersecting rays to escaped rays gives you your occlusion map.

A height map can be even simpler because it is a Monge patch...which basically means that you can look at the average value of the local neighborhood surrounding the pixel. If the average is higher than the center pixel, then this pixel will be more occluded. If the average is lower, then this pixel will be exposed and brighter.

In other words, your AO map can be computed as simply as:

AmbientOcclusion(x,y) = 255 + GaussianAverage(x,y,sigma) - PixelValue(x,y)

You should display your images with ambient lighting only to show the effects of ambient occlusion, it's not really evident with all the lighting
lonesock
lonesock
Quote:
Original post by yahastu
Hmm, well Im not sure what youre talking about with the bent normals...

I should have been more specific. I've editied the title to reflect the following. The bent normal vectors are actually what I was most interested in computing. The bent normal is the average direction away from all blockers, so basically the average of all rays which did not hit anything from your method. I have implemented that method, btw, as well as a totally different one (see my reply here). I was looking for something faster and simpler to include as standalone code (i.e. no acceleration structures, ray tracing, stochastic methods, etc.). Admittedly, your code for computing just the occlusion factor is much simpler. [8^)

Quote:
Original post by yahastu
You should display your images with ambient lighting only to show the effects of ambient occlusion, it's not really evident with all the lighting

This demo uses the bent normal and the ambient occlusion value, all baked into a single vector which is used _instead_ of the normal vector for lighting the heightmap. So the mesh is drawn with position and bent normal vectors per vertex, using the standard OpenGL lighting, no performance hit whatsoever, and you get a bit of fake GI looking lighting. (note that there is a performance hit because I'm rendering the heightmap in the worst way possible: no cache priming strips, just the whole thing drawn as a series of tri-strips and dropped into a display list.) You can press 'N' to draw the normal vectors, which you can see being "bent" as you increase the LOD of the algorithm. That's a good idea, though, to show only the occlusion term's effects.

Using both the bent normal and the occlusion value, I have a shader which performs Ambient Aperture lighting (see the Oat and Sander papers here, though mine is simplified so I can run it per-pixel instead of per-vertex). However, I feel that you get a decent GI look from just using the shortened bent normal vector with simple diffuse lighting, hence this demo. [8^)

Another reason I wanted this code is to use bent-normal ambient occlusion maps, instead of simple normal maps. It can be combined with something as simple as parallax mapping or as complex as Cone Step Mapping to get a decent lighting effect without the shadowing pass (which is a performance killer for Relief Mapping methods)...in fact, this is great for deferred rendering with multiple light sources.
yahastu
yahastu
Ah, cool...that is a neat idea. I overlaid the two images and tried swapping back and forth to see if I could notice the AO, but it just looks like the light has changed position to me. It would be a lot easier to see the AO if you disabled all light sources and used only a little bit of ambient light

Topic Locked

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

Sign in to reply to this topic.