SSAO [Screen Space Ambient Occlusion] Demo + Source

A place to show off your latest screenshots and for people to comment on them. Only start a new thread here if you have some nice images to show off!
User avatar
DanielSefton
Ogre Magi
Posts: 1235
Joined: Fri Oct 26, 2007 12:36 am
Location: Mountain View, CA
x 10
Contact:

Post by DanielSefton »

Hmm. I'm so impatient with these things! I want SSAO, and I want it NOW. :twisted:

I'm trying to integrate it in my old and horrible framework with frame listeners and inheritance galore. No luck so far -- I guess it's most probably to do with inheritance, but I'm not quite sure how your functions work.

It builds fine, but crashes at this line (in the constructor of MainFrameListener):

Code: Select all

geom->create(mWindow->getWidth(), mWindow->getHeight(), Ogre::PF_FLOAT32_RGBA);
And traces back to this line:

sseffect.cpp

Code: Select all

cam = qr.cam->getSceneManager()->createCamera(name + "_cam");
Why does it need to create another camera? :shock:

Code: Select all

-->>OpenRacer.exe!SSEffect::create(unsigned int w=1024, unsigned int h=768, Ogre::PixelFormat pf=PF_FLOAT32_RGBA)  Line 138 + 0x8 bytes	C++
 	OpenRacer.exe!Geom::create(unsigned int w=1024, unsigned int h=768, Ogre::PixelFormat pf=PF_FLOAT32_RGBA)  Line 19	C++
 	OpenRacer.exe!MainFrameListener::MainFrameListener(Ogre::RenderWindow * win=0x013eff98, Ogre::Camera * cam=0x01408010, Ogre::SceneManager * sceneMgr=0x013fe8f0, bool bufferedKeys=false, bool bufferedMouse=false, bool bufferedJoy=false)  Line 142 + 0x21 bytes	C++
Edit: Further info about the crash line (sseffect.cpp) from Autos:
- cam
+ mSceneMgr | CXX0030: Error: expression cannot be evaluated
- qr
+ quad | CXX0030: Error: expression cannot be evaluated
+ cam | CXX0030: Error: expression cannot be evaluated
Is it trying to find the application's scene manager and failing? :?

I'm too lazy to figure it out, so I was hoping you'd have a magical answer before I begin scratching my head. :D
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Post by nullsquared »

The problem is your QuadRenderer. Did you create a QuadRenderer and pass it your main rendering camera? It should look like this:

Code: Select all

QuadRenderer *quad = new QuadRenderer(mainCamera);
ssao = new SSAO("ssao", *quad); // pass reference to quad renderer
// then create
ssao->create(...);
It uses the quad renderer's camera to fetch the scene manager, and since it crashes on that line, I'm assuming the camera you passed to QuadRenderer() was invalid or something. Technically, the SSEffect's camera is never used, it was only there to satisfy creating a viewport. I think you might be able to remove it completely.

In Portalized I do not have a QuadRenderer, instead my renderer just has a function called Renderer::renderQuad(). My renderer also contains pointers to the current scene manager and what-not. Meaning, in this demo, I had to get around my own code with this ugly QuadRenderer stuff. You're probably better implementing your own quad rendering function (using QuadRenderer::go() as a reference), and replacing all of the QuadRenderer stuff which I (ab)used for the demo.
User avatar
DanielSefton
Ogre Magi
Posts: 1235
Joined: Fri Oct 26, 2007 12:36 am
Location: Mountain View, CA
x 10
Contact:

Post by DanielSefton »

Doh! You're right, I missed that line out. I didn't spot it. :oops:

Now I get a strange error saying that the manual object already exists, even when it's only ever created once. And renaming it doesn't make any difference...

Code: Select all

OGRE EXCEPTION(4:ItemIdentityException): An object of type 'ManualObject' with name 'QuadRenderer::quad' already exists. in SceneManager::createMovableObject at e:\projects\ogrecvs\branches\eihort_clean_vc8\ogre\ogremain\src\ogrescenemanager.cpp
sseffect.cpp

Code: Select all

quad = cam->getSceneManager()->createManualObject("QuadRenderer::quad");
I've come across this error before, but I can't remember how I solved it. Where it complains that an object already exists (whether it be a mesh/resource or anything of the kind)

Here's my code so far:

Constructor:

Code: Select all

qr = new QuadRenderer(mCamera);

geom = new Geom("Geom", *qr);
geom->create(mWindow->getWidth(), mWindow->getHeight(), Ogre::PF_FLOAT32_RGBA);

ssao = new SSAO("ssao", *qr);
ssao->create(mWindow->getWidth(), mWindow->getHeight(), Ogre::PF_R8G8B8);
frameStarted():

Code: Select all

geom->clear();
geom->update();

ssao->clear();
ssao->update();

mWindow->swapBuffers(true);
I always have every problem possible before I get something working. You could construct a troubleshooting guide from my posts. :lol:

Cheers!
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Post by nullsquared »

Are you sure you're creating the QuadRenderer once and only once? Because each separate QuadRenderer will try to create its own manual object, but they'll all use the same name.

There's lots of others things to tweak to get the SSAO working as you want it, since SSAO is a bit over-involved with each specific scene:

- swapBuffers(), you only need that if you're doing manual rendering, like I did in the demo with _renderScene(). If you're using Ogre's rendering, then you don't need that.

- SSAO::update() will calculate the SSAO, but it won't do anything to the main scene. you'll need to manually use the SSAO texture on your scene, as such I did in ambient.cg for the demo. (basically, I use the final clip-space vertex position as the UV for the SSAO texture)

- you'll need to tweak the SSAO radius inside ssao.cg. reason being is that my scenes are in meters, but other's scenes might be in feet/centimeters/inches/etc.
User avatar
DanielSefton
Ogre Magi
Posts: 1235
Joined: Fri Oct 26, 2007 12:36 am
Location: Mountain View, CA
x 10
Contact:

Post by DanielSefton »

For some very odd reason, part of the MainFrameListener constructor loops, and the other part doesn't. :shock: Or, at least I think so... Otherwise OIS would be initialized multiple times. Yet it doesn't loop; and the SSAO code does, apparently. :shock: Anyway, nevermind that.

One damn thing that I could never figure out, was how to initialize a class, then loop its function calls. How on Earth! Or, I'm just getting myself in a mess again. When I have "geom->clear(); geom->update(); ssao->clear(); ssao->update();" in frameStarted(), it crashes, but when I move it to where the classes are initialized (in the constructor), it works. But obviously I need those calls to loop. Argh!
basically, I use the final clip-space vertex position as the UV for the SSAO texture
Ohh, right, of course, I knew that. :lol:

Seriously though, which part of the code overlays the shaders on the screen? :oops:

Sorry for my ignorance; I'm just not a natural programmer. :( ...And this shader stuff is like Chinese to me. :lol:
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Post by nullsquared »

DanielSefton wrote:For some very odd reason, part of the MainFrameListener constructor loops, and the other part doesn't. :shock: Or, at least I think so... Otherwise OIS would be initialized multiple times. Yet it doesn't loop; and the SSAO code does, apparently. :shock: Anyway, nevermind that.

One damn thing that I could never figure out, was how to initialize a class, then loop its function calls. How on Earth! Or, I'm just getting myself in a mess again. When I have "geom->clear(); geom->update(); ssao->clear(); ssao->update();" in frameStarted(), it crashes, but when I move it to where the classes are initialized (in the constructor), it works. But obviously I need those calls to loop. Argh!
Sorry I couldn't make it into a simple compositor, I never figured out how to use a compositor properly for this type of thing :D. So this needs some manual render and stuff. See how the demo does it? It clears the window frame buffer, updates the geometry and ssao effects, then does SceneManager::_renderScene(), and then swapBuffers(). This whole process is the rendering portion of the game loop (startRendering() and renderOneFrame() are completely avoided).
basically, I use the final clip-space vertex position as the UV for the SSAO texture
Ohh, right, of course, I knew that. :lol:

Seriously though, which part of the code overlays the shaders on the screen? :oops:
That's what I'm getting at, the SSAO is never "overlayed" on the screen. It simply modulates the ambient light of each pixel (the light that is used in SceneManager::setAmbientLight()). How it does this is very simple:

- pass the SSAO texture to the ambient light shader (I did this very hackishly, I simply created the SSAO in-code before any materials were loaded, and then the material script [00diffuse.material] references the texture called ssao_tex as if it was any regular texture)

- since the SSAO is aligned to the screen (thus, screen-space), we simply project it from the main camera onto the screen (sort of like a flash light) such that it matches up perfectly with the scene. this is very easy since we're moving each vertex to screen-space anyways when we multiply by the worldViewProjection matrix - since we know where the current fragment will end up on the screen, we simply use that position to index the SSAO texture, and get the projective texturing for free (the add and multiply there are so that we're indexing texture-space [0..1] and not clip-space [-1..1])
Sorry for my ignorance; I'm just not a natural programmer. :( ...And this shader stuff is like Chinese to me. :lol:
It's partly my fault for requiring so much tweaking and manual operations to integrate into your own project.
User avatar
DanielSefton
Ogre Magi
Posts: 1235
Joined: Fri Oct 26, 2007 12:36 am
Location: Mountain View, CA
x 10
Contact:

Post by DanielSefton »

So this needs some manual render and stuff. See how the demo does it? It clears the window frame buffer, updates the geometry and ssao effects, then does SceneManager::_renderScene(), and then swapBuffers(). This whole process is the rendering portion of the game loop (startRendering() and renderOneFrame() are completely avoided).
Ahh. So you have to do manual rendering? :(

Manual rendering and frame listeners don't go well. :D I wish I could use my new manual framework, but unfortunately its not quite ready. So I have to stick with an ugly modified example framework for now.

When startRendering() is removed, the whole application ceases to work. Any idea how to initialise frame listeners without that magical function? I've never needed to do this before, so I wouldn't know. :?

I tried hacking it together, and adding a loop. But a loop AND a frame listener is just silly. So would I just move the following to frameStarted and frameEnded, and it would work?

Code: Select all

while(1)
{
Ogre::WindowEventUtilities::messagePump();

	geom->clear();
	geom->update();

	ssao->clear();
	ssao->update();

	mRenderSys->_setViewport(vp);

	mRenderSys->clearFrameBuffer(Ogre::FBT_COLOUR | Ogre::FBT_DEPTH);

	mSceneMgr->_renderScene(mCamera,vp,true);

	mWindow->swapBuffers(true);
}
The example framework is structured completely differently to your demo, so its hard to know what needs to go where and how. =\

I'm not giving up until I get this thing working. :P Hopefully my ignorance will be of benefit to someone else looking to integrate it. ;)
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Post by nullsquared »

DanielSefton wrote:
So this needs some manual render and stuff. See how the demo does it? It clears the window frame buffer, updates the geometry and ssao effects, then does SceneManager::_renderScene(), and then swapBuffers(). This whole process is the rendering portion of the game loop (startRendering() and renderOneFrame() are completely avoided).
Ahh. So you have to do manual rendering? :(

Manual rendering and frame listeners don't go well. :D I wish I could use my new manual framework, but unfortunately its not quite ready. So I have to stick with an ugly modified example framework for now.

When startRendering() is removed, the whole application ceases to work. Any idea how to initialise frame listeners without that magical function? I've never needed to do this before, so I wouldn't know. :?

I tried hacking it together, and adding a loop. But a loop AND a frame listener is just silly. So would I just move the following to frameStarted and frameEnded, and it would work?

Code: Select all

while(1)
{
Ogre::WindowEventUtilities::messagePump();

	geom->clear();
	geom->update();

	ssao->clear();
	ssao->update();

	mRenderSys->_setViewport(vp);

	mRenderSys->clearFrameBuffer(Ogre::FBT_COLOUR | Ogre::FBT_DEPTH);

	mSceneMgr->_renderScene(mCamera,vp,true);

	mWindow->swapBuffers(true);
}
The example framework is structured completely differently to your demo, so its hard to know what needs to go where and how. =\

I'm not giving up until I get this thing working. :P Hopefully my ignorance will be of benefit to someone else looking to integrate it. ;)
Hm. The idea would be this, and your frame listeners will still work 100%:

Code: Select all

// I'm pretty sure ExampleApplication called startRendering() in
// some function named go() or something
// so override it and do NOT call startRendering(), instead:
void ExampleApplication::go() {
    // usual ExampleApplication::go() stuff goes here,
    // except don't call root->startRendering()
    // .......
    // .......


    // our own loop
    for (bool running = true; running; ) {
        // manually pump window events
        Ogre::WindowEventUtilities::messagePump();

        // trigger frameStarted()
        running = root->_fireFrameStarted();

        // clear/update the effects
        geom->clear(); geom->update();
        ssao->clear(); ssao->update();

        // tell render system what viewport we're accessing
        renderSystem->_setViewport(windowViewport);
        // clear window
        renderSystem->clearFrameBuffer(Ogre::FBT_COLOUR | Ogre::FBT_DEPTH, windowViewport->getBackgroundColour());

        // render main scene
        sceneMgr->_renderScene(camera, windowViewport, true);

        window->swapBuffers(); // show to screen

        // fire frameEnded()
        running = running && root->_fireFrameEnded();

        // if any frame listeners returned false
        // the for loop would exit
    }
}
Also, you probably will want to create the effects somewhere at the top of createScene() - after resources are parsed, but before any resources are actually loaded and used (so that ssao_tex picks up the right texture).
User avatar
DanielSefton
Ogre Magi
Posts: 1235
Joined: Fri Oct 26, 2007 12:36 am
Location: Mountain View, CA
x 10
Contact:

Post by DanielSefton »

Great! That's perfect! :)

I think we're almost there...

For the last thing you mentioned, did you mean to create QuadRenderer, Geom and SSAO just before the resources are initialised?

If I create them after the resources are initialised, the game runs as normal (but with no SSAO). However, it must be doing something, because its really slow with the clear and update functions.

However, if I create them before the resources are initialised, I get this error:

Code: Select all

Assertion failed: index < mTextureUnitStates.size() && "Index out of bounds", file e:\projects\ogrecvs\branches\eihort_clean_vc8\ogre\ogremain\src\ogrepass.cpp
Crashes at this line:

Code: Select all

ssao = new SSAO("ssao", *qr);
And the debugger points to this line:

Code: Select all

pass->getTextureUnitState(0)->setTextureName("geom_tex");
Which is right? If the latter, any idea what the error means? And is there anything left to do?

Thanks again!
dudeabot
Gnome
Posts: 334
Joined: Thu Jun 28, 2007 2:12 pm
Location: Brazil
x 5
Contact:

Post by dudeabot »

nice! how do i see my FPS?

----

edit

i found this on .log

19:25:58: Render Target 'rtt/2922496' Average FPS: 72.4055 Best FPS: 79.4439 Worst FPS: 0.715308

I have a radeon x1600 core 2 duo 1.8ghZ

great demo :)
bharling
Gremlin
Posts: 166
Joined: Fri Jun 30, 2006 1:04 pm

Post by bharling »

Really nice demo :)

- I had to force SM2 to run it on my X1650, but it looks just as good and even seems to run faster, awesome!
Was here
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Post by nullsquared »

@ bharling: Well, the SM2 version is kinda-sorta-not-really SM2 since it uses pixel shader 2.b and not vanilla 2.0, but I'm glad it ran :D

@ Daniel: No, I meant put it in the the beginning of createScene(), before you actually load any models and stuff. The resources (at least the SSAO and Geom) resources need to be loaded before that.

Now, for the actual graphics: Geom expects all your materials to have a "geom" material scheme (check 00diffuse.material and diffuse.cg for the geom shaders). Other than that, you're not seeing SSAO in your scene since all the effect does is generate a SSAO texture. It's up to you to actually make use of it to attenuate the colour. Look at what I did with ambient.cg, I simply modulated the ambient colour by the SSAO texture.
User avatar
_tommo_
Gnoll
Posts: 677
Joined: Tue Sep 19, 2006 6:09 pm
x 5
Contact:

Post by _tommo_ »

Hmm... i downloaded the demo, but when i run it i can't see any ambient occlusion, while in Ogre.log there isn't any error... very strange :?

I ran it on a X1950, Vista SP1.
OverMindGames Blog
IndieVault.it: Il nuovo portale italiano su Game Dev & Indie Games
quanganht

Post by quanganht »

What do you mean "no ambient occlusion" ? can you give screenshot plz ?
bharling
Gremlin
Posts: 166
Joined: Fri Jun 30, 2006 1:04 pm

Post by bharling »

@ tommo

you probably need to do what i did, and disable the SM3 target, edit ssao.material and comment out the line:

Code: Select all

delegate ssao_ps_sm3
that worked for me ( even though i have an X1650, which is supposed to support SM3 )
Was here
User avatar
_tommo_
Gnoll
Posts: 677
Joined: Tue Sep 19, 2006 6:09 pm
x 5
Contact:

Post by _tommo_ »

Done, it works just perfectly :D
The Ambient Occlusion is really good, and it's perfect for convex things like the inner part of a knot...
the only thing that i don't like is that it's too strong near the angles of walls, wich for me is really unnatural...
Anyway this should be tweakable :D
OverMindGames Blog
IndieVault.it: Il nuovo portale italiano su Game Dev & Indie Games
User avatar
xadhoom
Minaton
Posts: 973
Joined: Fri Dec 28, 2007 4:35 pm
Location: Germany
x 1

Post by xadhoom »

Maybe this can be done with a more appropriate max distance value.
User avatar
aguru
Goblin
Posts: 236
Joined: Tue Feb 26, 2008 5:48 pm
x 3

Post by aguru »

I was wondering how far exactly opengl support has come with this thing. Did you get this beauty to work with a non d3d renderer, null?
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Post by nullsquared »

aguru wrote:I was wondering how far exactly opengl support has come with this thing. Did you get this beauty to work with a non d3d renderer, null?
TBH, no, I haven't. The biggest problem seems to be the fact that I just can't get OpenGL to render my screen-space quad, no matter what I do... and I can't quite figure out why. If anyone gets OpenGL to render the full screen quad correctly (regardless of the actual SSAO), it'd be nice to hear.
User avatar
aguru
Goblin
Posts: 236
Joined: Tue Feb 26, 2008 5:48 pm
x 3

Post by aguru »

I see. Anyway thanks for the info and happy coding. :wink:
User avatar
tuan kuranes
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 2653
Joined: Wed Sep 24, 2003 8:07 am
Location: Haute Garonne, France
x 4
Contact:

Post by tuan kuranes »

OpenGL to render my screen-space quad
Easiest would be to reuse one of StQuad_vp.cg vertex shader to have it working in any renderer and overcome projection matrix difference.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Post by nullsquared »

tuan kuranes wrote:
OpenGL to render my screen-space quad
Easiest would be to reuse one of StQuad_vp.cg vertex shader to have it working in any renderer and overcome projection matrix difference.
Can't, you need to reconstruct the view-space position from a depth metric, and the easiest way to do this is to do some interpolating from the vertex shader. Besides, I completely override all of Ogre's rendering with the quads, I simply do RenderSystem::_render(operation).
User avatar
tuan kuranes
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 2653
Joined: Wed Sep 24, 2003 8:07 am
Location: Haute Garonne, France
x 4
Contact:

Post by tuan kuranes »

Sorry, I didn't read your code yet...

If you set matrix yourself, Ogre provide some facility matrix (frustum members with 'RS' prefix/suffix in names are 'render system' specific.)
Perhaps have a look on that ogre code to find difference with your way to set matrix.
User avatar
nullsquared
Old One
Posts: 3245
Joined: Tue Apr 24, 2007 8:23 pm
Location: NY, NY, USA
x 11

Post by nullsquared »

tuan kuranes wrote:Sorry, I didn't read your code yet...

If you set matrix yourself, Ogre provide some facility matrix (frustum members with 'RS' prefix/suffix in names are 'render system' specific.)
Perhaps have a look on that ogre code to find difference with your way to set matrix.
I don't even use the matrix, I just pass the vertex coordinates unchanged, since I already specify them in clip-space [-1..1]. In fact, this used to work fine on Portalized in OpenGL, so I'm trying to figure out what I'm changing here to make it not work. Let me update my drivers first, though.
User avatar
Jerky
Orc Shaman
Posts: 791
Joined: Wed Mar 02, 2005 4:13 am
Location: Springville, Utah
Contact:

Post by Jerky »

nullsquared wrote:
nikki wrote:Looks very nice. But I have a really old graphics card (GeForce FX 5600), and get really low FPS on most good looking stuff like this. :(
Does your 5600FX even run this? Because the demo renders to a 128-bit texture (32 bits for each channel)...
Runs on my fx 5200 as well. Only about 0.3 FPS though :(. Looks friggin fantastic though. Nice work!
Erik Briggs (Jerky)
My Blog
Project Wish
Image
Post Reply