Skip to main content
Screen Space Effects         Say you have a shader effect that you simply want to render to a texture without using a compositor

Ogre's compositors are definitely useful and elegant due to their .compositor scripts as well as natural compliance with Ogre's viewports. However - say you have a shader effect that you simply want to render to a texture. Well, here's some quick utilities to do this:

string.hpp

Copy to clipboard
#ifndef STRING_HPP_INCLUDED #define STRING_HPP_INCLUDED #include <OgreString.h> namespace engine { typedef Ogre::String string; } #endif // STRING_HPP_INCLUDED


pre_ogre.hpp

Copy to clipboard
#ifndef PRE_OGRE_HPP_INCLUDED #define PRE_OGRE_HPP_INCLUDED #include <OgrePrerequisites.h> #endif // PRE_OGRE_HPP_INCLUDED


sseffect.hpp

Copy to clipboard
#ifndef SSEFFECT_HPP_INCLUDED #define SSEFFECT_HPP_INCLUDED #include <map> #include <OgrePixelFormat.h> #include <boost/shared_ptr.hpp> #include "pre_ogre.hpp" #include "string.hpp" namespace engine { namespace gfx { class renderer; class sseffect { protected: Ogre::Camera *_camera; Ogre::Viewport *_viewport; Ogre::Texture *_texture; bool _textureIsExtern; Ogre::RenderTarget *_rt; Ogre::Pass *_pass; const renderer &_renderer; bool _active; public: sseffect(const renderer &r); virtual ~sseffect(); virtual void create(const engine::string &name, size_t w = 256, size_t h = 256, Ogre::PixelFormat pf = Ogre::PF_R8G8B8); virtual void create(Ogre::Texture *tex); virtual void destroy(); virtual void update() const; virtual void clear() const; Ogre::Camera *camera() const { return _camera; } Ogre::Viewport *viewport() const { return _viewport; } Ogre::Texture *texture() const { return _texture; } Ogre::RenderTarget *rt() const { return _rt; } Ogre::Pass *pass() const { return _pass; } void pass(Ogre::Pass *p) { _pass = p; } bool active() const { return _active; } void active(bool b) { _active = b; } }; typedef boost::shared_ptr<sseffect> sseffectPtr; class sseffectManager { private: typedef std::map<engine::string, sseffectPtr> effectList; effectList _effects; sseffectPtr _tempEffect; public: void clear() { _effects.clear(); } sseffectManager &operator +=(const sseffectPtr &p) { _tempEffect = p; // hold on to pointer return *this; } void operator ,(const engine::string &name) { if (_tempEffect) { _effects[name] = _tempEffect; _tempEffect.reset(); } } sseffectPtr operator -=(const engine::string &name) { effectList::iterator i = _effects.find(name); // if not found, then just do nothing if (i == _effects.end()) return sseffectPtr(); // copy effect sseffectPtr ptr = i->second; // erase it _effects.erase(i); // return it return ptr; } sseffectPtr operator [](const engine::string &name) { effectList::iterator i = _effects.find(name); // if not found, then just do nothing if (i == _effects.end()) return sseffectPtr(); // return it return i->second; } void operator()() const { effectList::const_iterator i = _effects.begin(); for (; i != _effects.end(); ++i) { const sseffectPtr &p = i->second; p->update(); } } }; } } #endif // SSEFFECT_HPP_INCLUDED


sseffect.cpp

Copy to clipboard
#include <OgreViewport.h> #include <OgreCamera.h> #include <OgreRenderTarget.h> #include <OgrePixelFormat.h> #include <OgreTextureManager.h> #include <OgrePass.h> #include <OgreSceneManager.h> #include <OgreTexture.h> #include <OgreHardwarePixelBuffer.h> #include <OgreRoot.h> #include <OgreRenderTexture.h> #include <OgreRenderSystem.h> #include "sseffect.hpp" #include "renderer.hpp" namespace engine { namespace gfx { sseffect::sseffect(const renderer &r): _camera(NULL), _viewport(NULL), _texture(NULL), _textureIsExtern(false), _rt(NULL), _pass(NULL), _renderer(r), _active(true) { } sseffect::~sseffect() { destroy(); } void sseffect::destroy() { if (!_textureIsExtern) { if (_texture) Ogre::TextureManager::getSingleton().remove(_texture->getName()); if (_camera) _camera->getSceneManager()->destroyCamera(_camera); _texture = NULL; _camera = NULL; _rt = NULL; _viewport = NULL; } } void sseffect::create( const engine::string &name, size_t w, size_t h, Ogre::PixelFormat pf) { destroy(); Ogre::TexturePtr midPtr = Ogre::TextureManager::getSingleton().createManual( name, "Internal", Ogre::TEX_TYPE_2D, w, h, 0, // 0 mip maps pf, Ogre::TU_RENDERTARGET); _texture = midPtr.get(); _textureIsExtern = false; _rt = _texture->getBuffer(0)->getRenderTarget(0); _rt->setAutoUpdated(false); _camera = _renderer.sceneMgr()->createCamera(name + "_camera"); _viewport = _rt->addViewport(_camera, _rt->getNumViewports()); _viewport->setBackgroundColour(Ogre::ColourValue::White); _viewport->setClearEveryFrame(false); _viewport->setShadowsEnabled(false); _camera->setAspectRatio(float(w) / h); _camera->setFOVy(Ogre::Degree(90)); } void sseffect::create(Ogre::Texture *tex) { destroy(); _texture = tex; _textureIsExtern = true; _rt = _texture->getBuffer(0)->getRenderTarget(0); _viewport = _rt->getViewport(0); _camera = _viewport->getCamera(); } void sseffect::update() const { if (!_active || !_pass) return; _renderer.fsquad(_pass, _viewport); } void sseffect::clear() const { _renderer.renderSys()->_setViewport(_viewport); _renderer.renderSys()->clearFrameBuffer( Ogre::FBT_COLOUR | Ogre::FBT_DEPTH | Ogre::FBT_STENCIL, _viewport->getBackgroundColour()); } } }


renderer.hpp

Copy to clipboard
#ifndef RENDERER_HPP_INCLUDED #define RENDERER_HPP_INCLUDED #include "pre_ogre.hpp" #include "string.hpp" namespace engine { namespace gfx { class renderer { protected: Ogre::SceneManager *_sceneMgr; Ogre::Viewport *_viewport; Ogre::Camera *_camera; Ogre::ManualObject *_quad; void destroyQuad(); void buildQuad(Ogre::Camera *cam); public: renderer(Ogre::SceneManager *sceneMgr, Ogre::Viewport *viewport); virtual ~renderer(); void ensureQuad(); void fsquad(Ogre::Pass *p, Ogre::Viewport *vp) const; Ogre::SceneManager *sceneMgr() const { return _sceneMgr; } Ogre::Viewport *viewport() const { return _viewport; } Ogre::Camera *camera() const { return _camera; } Ogre::RenderSystem *renderSys() const; }; } } #endif // RENDERER_HPP_INCLUDED


renderer.cpp

Copy to clipboard
#include <OgreCamera.h> #include <OgreRoot.h> #include <OgreRenderSystem.h> #include <OgreViewport.h> #include <OgreSceneManager.h> #include <OgreManualObject.h> #include "renderer.hpp" #include "string.hpp" namespace engine { namespace gfx { Ogre::RenderSystem *renderer::renderSys() const { return Ogre::Root::getSingleton().getRenderSystem(); } renderer::renderer(Ogre::SceneManager *sceneMgr, Ogre::Viewport *viewport): _sceneMgr(sceneMgr), _viewport(viewport), _camera(viewport->getCamera()), _quad(NULL) { ensureQuad(); } renderer::~renderer() { destroyQuad(); } void renderer::destroyQuad() { if (_quad) _sceneMgr->destroyManualObject(_quad); _quad = NULL; } void renderer::buildQuad(Ogre::Camera *cam) { destroyQuad(); _quad = _sceneMgr->createManualObject("renderer::_quad"); //! (courtesy of Ogre API docs) //! The corners are ordered as follows: //! [0] = top-right near //! [1] = top-left near //! [2] = bottom-left near //! [3] = bottom-right near //! [4] = top-right far <--- want //! [5] = top-left far <--- want //! [6] = bottom-left far <--- want //! [7] = bottom-right far <--- want const vec3 *CORNERS = cam->getWorldSpaceCorners(); //! we want them in VIEW-SPACE!!! very important! mat4 viewMat = cam->getViewMatrix(true); _quad->clear(); _quad->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_TRIANGLE_LIST); { //! store frustum corner in normal attribute //! go anti-clockwise // top-left _quad->position(Ogre::Vector3(-1, 1, 0)); _quad->normal(viewMat * CORNERS[5]); _quad->textureCoord(Ogre::Vector2(0, 0)); // bottom-left _quad->position(Ogre::Vector3(-1, -1, 0)); _quad->normal(viewMat * CORNERS[6]); _quad->textureCoord(Ogre::Vector2(0, 1)); // bottom-right _quad->position(Ogre::Vector3(1, -1, 0)); _quad->normal(viewMat * CORNERS[7]); _quad->textureCoord(Ogre::Vector2(1, 1)); // top-right _quad->position(Ogre::Vector3(1, 1, 0)); _quad->normal(viewMat * CORNERS[4]); _quad->textureCoord(Ogre::Vector2(1, 0)); // we put the vertices in anti-clockwise, // so just start at 0 and go to 3 _quad->quad(0, 1, 2, 3); } _quad->end(); } void renderer::ensureQuad() { buildQuad(_camera); } void renderer::fsquad(Ogre::Pass *p, Ogre::Viewport *vp) const { if (!_quad) { return; } Ogre::RenderSystem *renderSys = Ogre::Root::getSingleton().getRenderSystem(); Ogre::RenderOperation rop; _quad->getSection(0)->getRenderOperation(rop); renderSys->_setViewport(vp); // render to said viewport _sceneMgr->_setPass(p, true, false); // parse the pass // bind/unbind GPU programs if (p->hasVertexProgram()) renderSys->bindGpuProgramParameters( Ogre::GPT_VERTEX_PROGRAM, p->getVertexProgramParameters()); else renderSys->unbindGpuProgram(Ogre::GPT_VERTEX_PROGRAM); if (p->hasFragmentProgram()) renderSys->bindGpuProgramParameters( Ogre::GPT_FRAGMENT_PROGRAM, p->getFragmentProgramParameters()); else renderSys->unbindGpuProgram(Ogre::GPT_FRAGMENT_PROGRAM); // clear any matrices renderSys->_setWorldMatrix(mat4::IDENTITY); renderSys->_setViewMatrix(mat4::IDENTITY); renderSys->_setProjectionMatrix(mat4::IDENTITY); renderSys->_beginFrame(); { renderSys->_render(rop); } renderSys->_endFrame(); } } }


Also, here is some relevant shading:

fsquad.material

Copy to clipboard
vertex_program fsquad_vs cg { source utils.cg entry_point fsquad_vs profiles vs_1_1 arbvp1 }


fsquad.cg

Copy to clipboard
struct FragIn_fsquad { float2 uv : TEXCOORD0; float3 ray: TEXCOORD1; }; struct VertOut_fsquad { float4 p : POSITION; float2 uv : TEXCOORD0; float3 ray: TEXCOORD1; }; VertOut_fsquad fsquad_vs(in float4 p : POSITION, in float3 n : NORMAL, in float2 uv : TEXCOORD0) { VertOut_fsquad OUT; OUT.p = p; OUT.uv = uv; OUT.ray = n; return OUT; }


The renderer has more to it than just the quadrilateral rendering, but in this case this is all that it is used for.

Example usage for the SSAO demo:

Copy to clipboard
#include <OgreMaterialManager.h> #include <OgreMaterial.h> #include <OgrePass.h> #include <OgreTechnique.h> #include <OgreCamera.h> #include "string.hpp" #include "renderer.hpp" #include "sseffect.hpp" namespace engine { namespace gfx { class ssao: public sseffect { private: void create(Ogre::Texture *et) {} public: ssao(const renderer &r); virtual ~ssao(); void update() const; }; ssao::ssao(const renderer &r): sseffect(r) { if (!Ogre::MaterialManager::getSingleton().resourceExists("compute_ssao")) { return; } Ogre::MaterialPtr mat = (Ogre::MaterialPtr) Ogre::MaterialManager::getSingleton().load("compute_ssao", "Internal"); _pass = mat->getTechnique(0)->getPass(0); } ssao::~ssao() { } void ssao::update() const { clear(); if (!_active || !_pass || !_pass->hasFragmentProgram()) return; Ogre::GpuProgramParametersSharedPtr params = _pass->getFragmentProgramParameters(); if (params->_findNamedConstantDefinition("pMat")) params->setNamedConstant( "pMat", CLIP_SPACE_TO_IMAGE_SPACE * // automatic scale/bias _renderer.camera()->getProjectionMatrixWithRSDepth()); if (params->_findNamedConstantDefinition("texSize")) { vec4 texSize(1.0 / _texture->getWidth(), 1.0 / _texture->getHeight(), 0, 0); params->setNamedConstant("texSize", texSize); } if (params->_findNamedConstantDefinition("far")) params->setNamedConstant("far", _renderer.camera()->getFarClipDistance()); sseffect::update(); } } } engine::gfx::renderer renderer(sceneManager, viewport); engine::gfx::sseffectManager effects; engine::gfx::sseffectPtr _ssao(new engine::gfx::ssao(renderer)); _ssao->create("ssao_texture", width, height, Ogre::PF_R8G8B8); effects += _ssao, "ssao"; main loop { // do stuff // update effects effects(); // render scene }


I pretty much pulled this code straight out of Portalized, so it's in the engine::gfx namespace. Feel free to take it out and what-not. Also feel free to modify the code to your own needs. This is not production code and it is not guaranteed to be fast, bug-free, and it is not guaranteed to work at all (though it probably will...).