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.
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.
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.
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.
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.
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.
Are you looking into general AO or screen space AO?
Tigrou:
You can look up from an environment map by the bent-normal to get an approximation of lighting from distances i believe.
You can look up from an environment map by the bent-normal to get an approximation of lighting from distances i believe.
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)
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)
@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.
@spike and the others : cool, i will try that.
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.
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).
well this is what i learned about AO at my CG course:
The theory:
realtime implementation:
never implemented it, but sounds pretty straightforward to me
The theory:
realtime implementation:
never implemented it, but sounds pretty straightforward to me
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.
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.
slite=slide
kusma: true. hmm...
Some low-order SH should be able to fix that tho.
"Off-line: compute ambient occlusion" - I can see another problem there, if the scene isn't static :)
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:
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))
}
kb_: Indeed, that's the standard solution for AOVs.
Here is the random() function I am using now (taken from somewhere)
with input being gl_TexCoord[0].xy
is this good random source for project rays in SSAO ?
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 ?
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?
The paper makes sense, but I still don't believe the implementation that Kusma presented:
So far, so good. But this:
isn't an axial projection but it's projected towards the center of the sphere. Wouldn't an axial projection look like this:
?
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);
?
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.
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.
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.
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.
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.
the transforms -> then transforms