the best ssao ive seen

Started by
237 comments, last by Paksas 12 years, 5 months ago
Quote:Original post by swiftcoder
Last I checked, light had a frequency too

With that usage, the images would be blue-violet hued ;D

In all seriousness, I was just joking around of course
"But who prays for Satan? Who, in eighteen centuries, has had the common humanity to pray for the one sinner that needed it most?" --Mark Twain

~~~~~~~~~~~~~~~Looking for a high-performance, easy to use, and lightweight math library? http://www.cmldev.net/ (note: I'm not associated with that project; just a user)
Advertisement
Another update to the algorithm. It feels wrong contributing to the lenght of an already big thread, but this could be useful to some.

RM Project


Two changes:
-2D sampling has been left behind. The sampling method is now interleaved in object space, like the original implementation by Crytek. This allows to use only 8 samples per pixel, and stil obtain very good quality. This also makes it faster (sponza +130fps, while the old method ran at 100 fps.)

-I´ve added a "self-occlusion" artist variable. It is possible to achieve the traditional grayish look with brightened edges, but without halo artifacts, and you can control the amount of self occlusion added to the result. In the image below, you can see the Hebe model with different self-occlusion values. A bit of self-occlusion usually adds contrast and makes the effect more noticeable.

This is achieved by initializating the occlusion with some positive value (the self occlusion value), and modifying the the occlusion function so that it not only darkens creases (adds occlusion), but also brightens edges (subtracts occlusion).


-Added cubemap lighting and a texture to achieve a more in-game look.

[Edited by - ArKano22 on April 19, 2010 9:58:14 AM]
ArKano22:

First off thanks for sharing your method... Now

I'm looking over the code and have a problem with a few things... could you explain the reasoning?

First off I see that vec[8] is initialized to the corners of a cube with the dimensions of (2,2,2). And then you do some pseudo random sampling in those directions. The problem is you are sampling out in 2D so your +1 or -1 in Z of those values never matters. In fact you're only passing the vec2 value of this to your doAmbientOcclusion function anyway. Did I miss something here? Wouldn't you be better off sampling in an 8 way circle if this is the case?

Now for the random length it seems you're sampling a texture with 4 values. That work out to always be one of (1.0,0.72,0.46,0.23) .. is that correct?

Thanks!

Check out my project @ www.exitearth.com

Quote:Original post by REF_Cracker
ArKano22:

First off thanks for sharing your method... Now

I'm looking over the code and have a problem with a few things... could you explain the reasoning?

First off I see that vec[8] is initialized to the corners of a cube with the dimensions of (2,2,2). And then you do some pseudo random sampling in those directions. The problem is you are sampling out in 2D so your +1 or -1 in Z of those values never matters. In fact you're only passing the vec2 value of this to your doAmbientOcclusion function anyway. Did I miss something here? Wouldn't you be better off sampling in an 8 way circle if this is the case?

Now for the random length it seems you're sampling a texture with 4 values. That work out to always be one of (1.0,0.72,0.46,0.23) .. is that correct?

Thanks!



Hello REF:

I´m using 3d samples, the corners of a 2x2x2 cube as you say. Then by multiplying each sample vector with the view transform, i rotate it along with the view. Then i use them as 2d points. The Z location does not matter anymore, but because of the view transform, the x,y values are not the same. So the Z value of each sample is needed to calculate the new x,y values passed to the occlusion function.

This sampling method results in a completely view-independent sampling pattern, because the pattern moves with the camera , and each pixel samples always the same zone, no matter how you move around the scene. This is the original ssao sampling method. 100% 2D sampling was developed after Crysis.

About the sampling lengths, yes, they are always these values. That is because i´m using interleaved sampling, so in a 2x2 area, each pixel samples a different lenght, then the 4 lengths are averaged together using a blur filter. This is also the original method, except Crysis used 4x4 interleaved sampling instead of 2x2 (i think, not sure about this).

So in fact, this method is Crytek´s with a different occlusion function that helps with halos and improves accuracy, nothing more.

EDIT: btw, i forgot to scale the samples with the screen size:
doAmbientOcclusion(i.uv,coord, p, n );
should be
doAmbientOcclusion(i.uv,coord*g_inv_screen_size*1000.0, p, n );

I will correct that as soon as possible.
I can't really make out any quality difference between this and the earlier approach. Great result!
"But who prays for Satan? Who, in eighteen centuries, has had the common humanity to pray for the one sinner that needed it most?" --Mark Twain

~~~~~~~~~~~~~~~Looking for a high-performance, easy to use, and lightweight math library? http://www.cmldev.net/ (note: I'm not associated with that project; just a user)
Quote:Original post by Prune
I can't really make out any quality difference between this and the earlier approach. Great result!



The quality is more or less the same. Maybe a bit more contrast with the darken/brighten scheme.

Ths difference is in speed, because the sampling is less random, and increases cache coherency. the other approach used at least 16 samples while this can use 8 or 14 for the same quality.
Awesome, thanks for sharing.
that is some clever blurring.
I ended up using this line instead of texture.

Quote:
float getRandom(in float2 uv)
{
return ((frac(uv.x * (g_screen_size.x/2.0))*0.25)+(frac(uv.y*(g_screen_size.y/2.0))*0.75));
}


Looks the same, but you don`t need external random texture and I gained like 2 fps :D
Quote:Original post by martinsh
Awesome, thanks for sharing.
that is some clever blurring.
I ended up using this line instead of texture.

Quote:
float getRandom(in float2 uv)
{
return ((frac(uv.x * (g_screen_size.x/2.0))*0.25)+(frac(uv.y*(g_screen_size.y/2.0))*0.75));
}


Looks the same, but you don`t need external random texture and I gained like 2 fps :D


it should be
return ((frac(uv.x * (g_screen_size.x/2.0))*0.25)+(frac(uv.y*(g_screen_size.y/2.0))*0.5));

0.5 instead of 0.75.
neat trick! :D
@martinish

There's an error in the code you posted. With rings=3 and samples=3 it generates the following pattern consisting of 36 samples. Samples on the inner two rings are all double.
Photobucket

You probably want to do something like this:
int ringsamples;for (int i = 1; i <= rings; i += 1){   ringsamples = i * samples;   for (int j = 0 ; j < ringsamples ; j += 1)   {      float step = PI*2.0 / float(ringsamples);      pw = (cos(float(j)*step)*float(i));      ph = (sin(float(j)*step)*float(i))*aspect;      d = readDepth( vec2(texCoord.s+pw*w,texCoord.t+ph*h));      ao += compareDepths(depth,d);      s += 1.0;   }}

For for rings=3 and samples=3 this results in only 18 samples.
Results - Depth Buffer Only
Sampling 8 points per pixel.


SSAO

Check out my project @ www.exitearth.com

This topic is closed to new replies.

Advertisement