pouët.net

Ambient occlusion for dummies

category: code [glöplog]
In one renderer i'm working on i'd like to add some ambient occlusion in the scene but do not know how to proceed.


Basic idea : once surface has been hit at point P, shoot a number of rays in a random direction but inside a given hemisphere oriented towards normal n, then check if something has been hit. The more distance from other objects the brighter the point.

BB Image

The problem is : how to shoot points that are always inside the hemisphere ? should i calculate some (u,v) coordinates first, then use that to calculate where to shoot the ray ?

Another question : from distances, how can i compute color attenuation from that ?

Any tip or small snippets of code are welcome.
added on the 2013-10-01 21:57:53 by Tigrou Tigrou
A bad solution could be to take a normalized random vector v and multiply by sign(dot(n,v)) to flip any vectors that point away from the normal.

The trouble with most random approaches is going to be biased sampling. Taking a random point within a [-1,1] cube and normalizing it will end up with denser sampling around the corners of the cube.

You might be better off with pre-selected evenly-distributed sampling points and jitter them slightly.
added on the 2013-10-01 22:23:07 by bloodnok bloodnok
Are you looking into general AO or screen space AO?
added on the 2013-10-01 23:29:20 by TLM TLM
Tigrou:
You can look up from an environment map by the bent-normal to get an approximation of lighting from distances i believe.
added on the 2013-10-02 00:28:32 by rudi rudi
google for cosine hemisphere sampling. e.g. see http://pathtracing.wordpress.com/2011/03/03/cosine-weighted-hemisphere/.
Also you should use some form of stratified sampling to get more bang for the buck (i.e. higher quality for the same number of rays)
added on the 2013-10-02 08:50:27 by arm1n arm1n
@bloodnok : the bad solution seems not so bad. anyway i get a lot of noise using this I dont know if the reason is because i shoot not enough rays (i shoot 80 per hit) or if my random() glsl function do not provide good output.

@spike and the others : cool, i will try that.
added on the 2013-10-02 09:28:27 by Tigrou Tigrou
SSAO, last time I tried I couldn't get it working with good results and not slow. I must be doing something wrong. Is it worth it to have in an engine? Maybe I'll try again in the future.
added on the 2013-10-02 10:21:50 by Optimus Optimus
Well this is not in an engine but a raytracer. Yes i also realized that until you shoot tons of ray SSAO does not look good. Maybe its time to switch to a path tracer, it will be slow but at least i will get many things for free (like shadows).
added on the 2013-10-02 10:32:29 by Tigrou Tigrou
well this is what i learned about AO at my CG course:

The theory:
BB Image

realtime implementation:
BB Image

never implemented it, but sounds pretty straightforward to me
added on the 2013-10-02 11:19:53 by v3nom v3nom
There may be special cases you can take advantage of. I have AO in that terrain thing I keep posting screenshots of. Since the terrain is 2D and is the only occluder, I simply find the highest point on a number of segments radiating out from the sample location. From there it's straightforward to work out the proportion of the sky visible in the slice.
added on the 2013-10-02 11:22:44 by bloodnok bloodnok
v3nom: On problem that last slite forgets, is that ambient occlusion is surface-direction dependent. You only want the integral over the hemisphere around the surface-normal.
added on the 2013-10-02 11:24:57 by kusma kusma
slite=slide
added on the 2013-10-02 11:26:21 by kusma kusma
kusma: true. hmm...
added on the 2013-10-02 11:29:20 by v3nom v3nom
Some low-order SH should be able to fix that tho.
added on the 2013-10-02 11:31:15 by kb_ kb_
"Off-line: compute ambient occlusion" - I can see another problem there, if the scene isn't static :)
added on the 2013-10-02 11:31:29 by psonice psonice
Tigrou: bloodnok's "bad" solution is actually what you want, but you'll want to start with uniformly distributed points on a sphere. To generate uniformly distributed random points on a sphere, you generate them on a unit cylinder (but not the caps), and project onto the sphere. While it's not intuitive to see, this actually gives uniform distribution.

So yeah, something like this:
Code: vec3 randomSpherePoint() { float s = random() * 3.1415926 * 2.0; float t = random() * 2.0 - 1.0; return vec3(sin(s), cos(s), t) / sqrt(1.0 + t * t); } vec3 randomHemispherePoint(vec3 dir) { vec3 v = randomSpherePoint(); return v * sign(dot(v, dir)) }
added on the 2013-10-02 11:37:44 by kusma kusma
kb_: Indeed, that's the standard solution for AOVs.
added on the 2013-10-02 11:39:16 by kusma kusma
Here is the random() function I am using now (taken from somewhere)

Code:float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); }


with input being gl_TexCoord[0].xy

is this good random source for project rays in SSAO ?
added on the 2013-10-02 11:44:53 by Tigrou Tigrou
Quote:
...you generate them on a unit cylinder (but not the caps), and project onto the sphere. While it's not intuitive to see, this actually gives uniform distribution.


This is so non-intuitive I refused to believe it. Then I read the paper, and I still didn't believe it.

I had a good portion of a graduate level lecture dedicated to uniform sampling from a sphere, and *this* never came up?

Thanks for the nugget Kusma :)

Does it generalize to higher dimensions?
added on the 2013-10-02 12:34:44 by revival revival
The paper makes sense, but I still don't believe the implementation that Kusma presented:

Quote:
Generate a random point on the cylinder
[- 1,1] x [0.2~] and then find its inverse axial projection on the unit sphere.


So far, so good. But this:

Code:return vec3(sin(s), cos(s), t) / sqrt(1.0 + t * t);


isn't an axial projection but it's projected towards the center of the sphere. Wouldn't an axial projection look like this:

Code: float r = sqrt(1.0 - t * t); return vec3(sin(s)*r, cos(s)*r, t);


?
added on the 2013-10-02 12:49:01 by kb_ kb_
Quote:
you'll want to start with uniformly distributed points on a sphere


ehm, for sampling the upper hemisphere you'll _not_ want to have uniform distributed points, but a cosine weighted distribution (i.e. more rays close to the normal direction, because on average they have the most important information).

Using stratified sampling (i.e. dividing the hemisphere into N equally sized sectors and then shooting M random rays inside each sector) improves quality even more dramatically.

This is described in the two links i posted above, including source code.
added on the 2013-10-02 12:59:38 by arm1n arm1n
kb_ has leading I believe.

Spike, the first article you linked doesn't do *uniform* sampling correctly, and the cos weighted code is so complicated it's impossible to understand :-) Second article seems better.
added on the 2013-10-02 13:14:43 by revival revival
Ehm, sorry, i think the cos weighted code is rather simple to understand.

First it calculates the cartesian coordinates from the theta and phi polar angles and the transforms the resulting coord into the local frame constructed according to the normal.
Easy.
added on the 2013-10-02 13:37:24 by arm1n arm1n
the transforms -> then transforms
added on the 2013-10-02 13:38:02 by arm1n arm1n

login