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

#ifndef STRING_HPP_INCLUDED
#define STRING_HPP_INCLUDED

#include <OgreString.h>

namespace engine
{

    typedef Ogre::String string;

}

#endif // STRING_HPP_INCLUDED


pre_ogre.hpp

#ifndef PRE_OGRE_HPP_INCLUDED
#define PRE_OGRE_HPP_INCLUDED

#include <OgrePrerequisites.h>

#endif // PRE_OGRE_HPP_INCLUDED


sseffect.hpp

#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

#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

#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

#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

vertex_program fsquad_vs cg
{
    source utils.cg
    entry_point fsquad_vs

    profiles vs_1_1 arbvp1
}


fsquad.cg

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:

#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...).