Alchemy ambient occlusion


#1

Hello, I’ve adapted the Alchemy/SAO ambient occlusion algorithm by Morgan McGuire:
graphics.cs.williams.edu/papers/AlchemyHPG11/
graphics.cs.williams.edu/papers/SAOHPG12/
It has some faults, especially the blur shaders, but it could be a starting point.

You can find it here:
bitbucket.org/reattiva/sao-as/downloads

There is a simple AS script (Alchemy.as) to test it. It uses deferred render path, the occlusion shader needs the depth and normal buffers, but normals can also be calculated realtime on the shader.
It is based on McGuire’s demo code (BSD licensed) for SAO, but many of the SAO improvements are not implemented so it is closer to the Alchemy algorithm than the SAO one. I’ve written some Gaussian blur filters: BlurGaussian and BlurGaussianDepth. They tend to make occlusion too big in the distance so I’ve added a fade-out effect.
The parameters are very coupled and difficult to adjust, this is exactly the opposite of the original author results, probably there are some bugs around. These parameters are: radius, intensity, projscale and bias. Projscale is used to adjust the radius scaling with the depth, this was not present in the paper so I think this is the bugged part. Bias is used to prevent self occlusion due to depth precision (e.g. a flat surface should be white).
The parameters are changeable with the keyboard (R,F,T,G,Y,H,U,J), you can disable the occlusion effect (V), enable a Gaussian blur (B), enable a depth aware Gaussian blur (N), and display the occlusion buffer (M).
In the script set “bool OpenGL” to false if you use DirectX (I couldn’t find a good flag for this).

Notes:

  1. In this example the intensity is too high, it should be a very subtle effect, but more important the occlusion buffer is blended with the final viewport (SAO_copy shader). This is not correct, AO should only affect the ambient light. A simple way to do this is to move the “quad SAO_copy” command before the “lightvolumes” command (and enable shadows to appreciate the difference). The “lightvolumes” command is not a good point to do the blend because it uses the add/subtract blend mode.

  2. When you use a big radius, the occlusion is not correct around the screen borders because these is no depth buffer to sample outside the screen. To reduce this flaw you can use a G-buffer bigger than the viewport (for example “”), you compute the occlusion and then you can use a shader to offset and center it on the screen. This guard band is used only for reading the depth buffer, to avoid calculating the occlusion on it you can use Graphics::SetScissorTest in View::RenderQuad. This could also be done on the “lightvolumes” command in View::SetupLightVolumeBatch. You have to modify the engine but it should not be hard.

  3. An improvement of SAO is the use of a depth buffer with mipmaps. To do this you create a texture for the depth buffer with N mipmaps, then you create N framebuffers and attach a different depth mipmap to each of them on COLOR0. Using the first framebuffer you compute the depth buffer, this will be written on the level 0 of the depth texture, then using the other framebuffers and a special shader you can build a mipmap level N by reading the level N-1.
    I’ve still haven’t how to do this in Urho (RenderTargets don’t have mipmaps), and using mipmaps in HLSL is tricky/impossible with DirectX9 (on GLSL it is possible by enabling “extension GL_EXT_gpu_shader4”).

Any comment is really welcomed.


SSDO or SSAO examples?
Can you make more "cute" graphics with Urho3D?
SAO implementation errors
Some nice assets to make an Urho3D demo from
#2

Wow, cool!
I tried to rewrite your script in C ++ and use in my test scene, but the scene did not look like the examples from the video (frog kinght). Maybe I missed something in code.


#3

GL_EXT_gpu_shader4 from OpenGL 3.0?


#4

Any screenshot? If the test scene of the script looks the same then the problem are the shaders or their parameters. Actually, I was never able to make the original demo from McGuire work, so I don’t really know if it is supposed to look like that.

This one https://www.opengl.org/registry/specs/EXT/gpu_shader4.txt, so the minimum is OpenGL 2.0, GLSL 1.10 and a card that supports it. Anyway you don’t need this for the example, you need it only to work with mipmaps. I’ve also removed the bitwise operations from the random function (not sure if it was wise), so you really don’t need this extension.


#5

cool


#6

Awesome ! I just tried it works like a charm on OSX I did it with C++ instead of AngelScript .
Can someone help me to get this working on mobile for OpenGL ES 2.0 ?


#7

[quote=“sabotage3d”]Can someone help me to get this working on mobile for OpenGL ES 2.0 ?
[/quote]
I have no experience with OpenGL ES, but if you have a particular problem, post it here, there shouldn’t be big differences. However keep in mind that the occlusion and blur shaders are very, very heavy especially on textures reading.


#8

Thanks reattiva,

I am trying to port it to Opengl ES 2.0, but I am having some problems.
I am getting these errors. If I try to fix any of these I am getting even deeper errors which are very weird. Any ideas ?

2014-12-26 22:39:36.222 34_DynamicGeometry[26595:60b] Loading resource Shaders/GLSL/SAO_main.glsl 2014-12-26 22:39:36.235 34_DynamicGeometry[26595:60b] Compiled vertex shader SAO_main() 2014-12-26 22:39:36.238 34_DynamicGeometry[26595:60b] Failed to compile pixel shader SAO_main(): ERROR: 0:97: Use of undeclared identifier 'sDepthBuffer' ERROR: 0:98: Use of undeclared identifier 'edC' ERROR: 0:99: Use of undeclared identifier 'depthC' ERROR: 0:116: Use of undeclared identifier 'vsC' ERROR: 0:121: Use of undeclared identifier 'vsC' ERROR: 0:145: Use of undeclared identifier 'vsC' ERROR: 0:157: Use of undeclared identifier 'ssDiskRadius' ERROR: 0:160: Use of undeclared identifier 'ssOffset' ERROR: 0:163: Use of undeclared identifier 'sDepthBuffer' ERROR: 0:163: Use of undeclared identifier 'ssP' ERROR: 0:164: Use of undeclared identifier 'ssP' ERROR: 0:164: Use of undeclared identifier 'depthP' ERROR: 0:167: Use of undeclared identifier 'vsP' ERROR: 0:167: Use of undeclared identifier 'vsC' ERROR: 0:169: Use of undeclared identifier 'v' ERROR: 0:169: Use of undeclared identifier 'v' ERROR: 0:170: Use of undeclared identifier 'v' ERROR: 0:177: Use of undeclared identifier 'vv' ERROR: 0:178: Use of undeclared identifier 'f' ERROR: 0:178: Use of undeclared identifier 'f' ERROR: 0:178: Use of undeclared identifier 'f' ERROR: 0:178: Use of undeclared identifier 'vn' ERROR: 0:178: Use of undeclared identifier 'vv' ERROR: 0:189: Use of undeclared identifier 'intensity' ERROR: 0:204: Use of undeclared identifier 'occlusion' ERROR: 0:204: Use of undeclared identifier 'edC' 2014-12-26 22:39:36.239 34_DynamicGeometry[26595:60b] Loading resource Shaders/GLSL/BlurGaussianDepth.glsl 2014-12-26 22:39:36.251 34_DynamicGeometry[26595:60b] Compiled vertex shader BlurGaussianDepth() 2014-12-26 22:39:36.255 34_DynamicGeometry[26595:60b] Compiled pixel shader BlurGaussianDepth(SAMPLES=8) 2014-12-26 22:39:36.258 34_DynamicGeometry[26595:60b] Linked vertex shader BlurGaussianDepth() and pixel shader BlurGaussianDepth(SAMPLES=8) 2014-12-26 22:39:37.185 34_DynamicGeometry[26595:60b] Compiled pixel shader BlurGaussianDepth(SAMPLES=8 VERTICAL) 2014-12-26 22:39:37.187 34_DynamicGeometry[26595:60b] Linked vertex shader BlurGaussianDepth() and pixel shader BlurGaussianDepth(SAMPLES=8 VERTICAL) 2014-12-26 22:39:38.135 34_DynamicGeometry[26595:60b] Compiled vertex shader DeferredLight(DIRLIGHT) 2014-12-26 22:39:38.136 34_DynamicGeometry[26595:60b] Shader DeferredLight(DIRLIGHT PERPIXEL SPECULAR) does not use the define PERPIXEL 2014-12-26 22:39:38.139 34_DynamicGeometry[26595:60b] Failed to compile pixel shader DeferredLight(DIRLIGHT PERPIXEL SPECULAR): ERROR: 0:599: Use of undeclared identifier 'sDepthBuffer' ERROR: 0:603: Use of undeclared identifier 'depth' ERROR: 0:605: Use of undeclared identifier 'sAlbedoBuffer' ERROR: 0:606: Use of undeclared identifier 'sNormalBuffer' ERROR: 0:618: Use of undeclared identifier 'normalInput' ERROR: 0:619: Use of undeclared identifier 'worldPos' ERROR: 0:623: Use of undeclared identifier 'normal' ERROR: 0:623: Use of undeclared identifier 'worldPos' ERROR: 0:640: Use of undeclared identifier 'normal' ERROR: 0:640: Use of undeclared identifier 'worldPos' ERROR: 0:640: Use of undeclared identifier 'normalInput' ERROR: 0:641: Use of undeclared identifier 'diff' ERROR: 0:641: Use of undeclared identifier 'albedoInput' ERROR: 0:641: Use of undeclared identifier 'spec' ERROR: 0:641: Use of undeclared identifier 'albedoInput' 2014-12-26 22:39:38.424 34_DynamicGeometry[26595:60b] Loading resource Shaders/GLSL/SAO_copy.glsl 2014-12-26 22:39:38.434 34_DynamicGeometry[26595:60b] Compiled vertex shader SAO_copy() 2014-12-26 22:39:38.436 34_DynamicGeometry[26595:60b] Compiled pixel shader SAO_copy() 2014-12-26 22:39:38.438 34_DynamicGeometry[26595:60b] Linked vertex shader SAO_copy() and pixel shader SAO_copy()


#9

Sorry Sabotage, there are big differences. In ES the depth buffer, sDepthBuffer, is not defined, all your other errors are consequences. This is intended, you can clearly see it in the file \Bin\CoreData\Shaders\GLSL\Samplers.glsl, the other g-buffers are not defined as well, so the deferred render path is not an option in ES.
You can try to build the depth buffer yourself with the Depth.glsl shader and move to the forward render path, but I think a lightmap texture is a better and much cheaper way to have occlusion on mobile.


#10

I think the OES_depth_texture extension would work.
Is it enough if I move the code from SAO main into single material assign it to all surfaces and swap sDepthBuffer for OES_depth_texture .
As a first step is it possible to discard the Gaussian blur or any other deffered shaders ?
I already have lightmaps but for moving objects I need some interactive shadows and your solution is looking awesome.


#11

I don’t think the SAO will work if you move it in a technique. The SAO is like a post-process effect, it needs to work on a quad fullscreen, but if you are trying to limit the AO effect to only some objects then you have a point, but I don’t know a good way to do it, maybe using the stencil?
Yes, you can skip the blur and for sure you’d better skip the deferred render path.

For starters, use a simple scene (e.g. 04_StaticScene.as) and try switch it to the ForwardDepth.xml render path. See if you can get a good depth buffer written, you can view it by adding these lines at the end of the render path:

<command type="quad" tag="depth_copy" vs="CopyFramebuffer" ps="CopyFramebuffer" output="viewport"> <texture unit="diffuse" name="depth" /> </command>
In OpenGL you ‘’‘should’’’ be able to see it as a wonderful sawtooth of colors.

In the forward path you don’t have the normal buffer, but in the shader there is a “#if 1” you can use to calculate the normal real-time, it is the part with the “dxdf”, unfortunately I think this instruction could be a problem for ES.


#12

Thanks again for helping me out.

With the ForwardDepth.xml with your additions I am getting this:

On OSX

On IOS

Is the result correct ?
If am getting the same if I attach the Depth.glsl to my objects.
So we can’t bake the normals buffer to texture as well ?

I am trying to add new uniforms for the depth to textures but it is very convoluted.
I have used that before for shadowmaps but I am not sure how to fit it with Urho3d or if it is already provided.
The original code is taken from a book for OpenGL ES.

[code]unsigned int depth_texture,
shadowmap_buffer,
shadowmap_width = 128,
shadowmap_height = 256;

glGenFramebuffers( 1, &shadowmap_buffer );
glBindFramebuffer( GL_FRAMEBUFFER, shadowmap_buffer );

glGenTextures( 1, &depth_texture );
glBindTexture( GL_TEXTURE_2D, depth_texture );

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );

glTexImage2D( GL_TEXTURE_2D,
0,
GL_DEPTH_COMPONENT,
shadowmap_width,
shadowmap_height,
0,
GL_DEPTH_COMPONENT,
GL_UNSIGNED_SHORT,
NULL );

glBindTexture( GL_TEXTURE_2D, 0 );

glFramebufferTexture2D( GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
GL_TEXTURE_2D,
depth_texture,
0 );

glBindFramebuffer( GL_FRAMEBUFFER, shadowmap_buffer );

glViewport( 0, 0, shadowmap_width, shadowmap_height );

glClear( GL_DEPTH_BUFFER_BIT );

glCullFace( GL_FRONT )

glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, depth_texture );[/code]


#13

I am having some difficulties applying your shader based on the ForwardDepth if I remove the code below from your DeferredSAO.xml it doesn’'t render at all.

<command type="lightvolumes" vs="DeferredLight" ps="DeferredLight"> <texture unit="albedo" name="albedo" /> <texture unit="normal" name="normal" /> <texture unit="depth" name="depth" /> </command>

It is possible just to isolate this one for now without the need for any other shaders ?

<command type="quad" tag="SAO_main" vs="SAO_main" ps="SAO_main" output="occlusion"> <texture unit="diffuse" name="viewport" /> <texture unit="normal" name="normal" /> <texture unit="depth" name="depth" /> <parameter name="ProjInfo" /> <parameter name="ProjScale" /> <parameter name="View" /> <parameter name="Radius" value="1.0" /> <parameter name="Bias" value="0.01" /> <parameter name="ProjScale2" value="1.0" /> <parameter name="IntensityDivR6" value="1.0" /> </command>


#14

Download the repository now, I’ve added a forward path example. In the angelscript example you have to change it from DeferredSAO.xml to ForwardDepthSAO.xml.
Your depth buffer looks good.


#15

Thanks a lot :slight_smile:
It works perfectly, you saved me hours .
Earlier I tried all the combinations to get it the forward rendering working with your shader, but I couldn’t figure it out.


#16

It seems that projecting on a quad doesn’t work great under IOS or there is osmething that is not supported properly.
I am getting this result on IOS after fixing all the errors and warnings. Even setting float occlusion = 0.5;
I cannot get the 50% gray layer. Everything works perfectly under OSX.

It seems that it displays the depth buffer rather than the diffuse + occlusion on top .


#17

No, that doesn’t look good.
One thing I forgot and you probably have already fixed it, on IOS you have to add “uniform sampler2D sDepthBuffer;” at the start of SAO_main.glsl, because the one in Samplers.glsl is skipped.
About dFdx and dFdy on ES, I’ve found this khronos.org/opengles/sdk/do … dFdx.xhtml, so it is supported from ES GLSL v3.00. Maybe try adding (as first line) the directive “#version 300 es” to be sure.


#18

I think the main problem at the moment is in the xml rather than the shader. Because I tried doing simple Gray color overlay using your ForwardDepthSAO.xml
In SAO_main.glsl I am setting float occlusion = 0.5; When I try this under OSX I can see Gray overlay, but it doesn’t work under IOS. On IOS it shows only the depth buffer so it means there is something weird that Urho3d does on the ES side.
Also I tried this for the SAO_main.glsl : For the sDepthBuffer I am declaring it as: uniform sampler2D sDepthBuffer; And for dFdx and dFdy I did this #extension GL_OES_standard_derivatives : enable as mentioned from this link: stackoverflow.com/questions/9671 … ve-texture
I also set the precision to : precision highp float;
Because it was complaining about the precision of the cFrustumSize

Is there a native way just to render SAO_main on a quad ?
Is it going to work in a single shader like surface shader ?

Any ideas how to debug this are welcomed :slight_smile:


#19

This is where I can’t help you further in ES.
Anyway, I didn’t catch your question. SAO is rendered on a quad and it only needs the depth buffer. The only way to make it simpler is to save a depth buffer (the colors rainbow) to an image (png, tga …) once, then you load it in the depth texture unit of the SAO.
Debugging a shader is a pain in the arse, you print everything on the screen step by step, buffer by buffer… maybe you should try to find a working AO shader for IOS and start from there.


#20

Thanks a lot I will start from the basics again.