CodeSnippits Easy RTT Materials Management
From PyWiki
Managing Materials Easily for MRTs
Often when doing fullscreen effects like depth of field or screen space ambient occlusion you'll want to quickly change all the materials in the scene to render a specific effect, for example to make a texture of the depth buffer or the normals of all objects. It can be a real pain to do this because it can mean either changing all your materials for all objects, or setting up complicated schemes which also have to be added to all materials.
Luckily, there is a simple way to do this using render targets, and the ogre.MaterialManager.Listener class. The listener has only one method - 'handleSchemeNotFound', which provides a very convenient way to temporarily switch all materials in the scene ( or just those being rendered to a specific target ) to whatever you want, without affecting the main scene, and without having to change all your material scripts. To do this, you have to set up at least one render target (though that could even be the main window if you wanted), and set it so that it renders using a specific material scheme. It doesn't really matter what this scheme is, as the listener will catch all the materials that don't implement that scheme and force them to render what you want instead.
Below is an example application that creates 2 additional render targets, and displays them both on the screen. One renders the depth buffer, and the other renders the object space normals in the scene. Both are controlled by shaders ( also below ), so you have complete control over how each target ( or even each object ) is rendered. Note, there are many ways to do this, but this is perhaps one of the most simple methods in OGRE, and its quite efficient both in framerate and the amount of time to implement.
MRT_Example.py
import sys sys.path.insert(0,'..') import PythonOgreConfig import psyco psyco.full() import ogre.renderer.OGRE as ogre import ogre.io.OIS as OIS import SampleFramework as sf # Simple frameListener that just enables a compositor for testing class tListener(sf.FrameListener): def __init__(self, rw, cam, dcam): sf.FrameListener.__init__(self, rw, cam) self.it = 0 self.vp = rw.getViewport(0) self.depthCam = dcam def frameStarted(self, evt): self.it += 1 self.depthCam.setOrientation ( self.camera.getOrientation() ) self.depthCam.setPosition (self.camera.getPosition()) if self.it == 10: instance = ogre.CompositorManager.getSingleton().addCompositor(self.vp, 'Bloom', 0) ogre.CompositorManager.getSingleton().setCompositorEnabled(self.vp, 'Bloom', True) return sf.FrameListener.frameStarted(self, evt) # class to handle material switching without having to modify scene materials individually class MaterialSwitcher( ogre.MaterialManager.Listener ): def __init__(self): ogre.MaterialManager.Listener.__init__(self) depthMat = ogre.MaterialManager.getSingleton().getByName("BasicDepthWrite") depthMat.load() self.depthTechnique = depthMat.getBestTechnique() normMat = ogre.MaterialManager.getSingleton().getByName("BasicWorldNormalWrite") normMat.load() self.normalsTechnique = normMat.getBestTechnique() def handleSchemeNotFound(self, index, name, material, lod, rend): #print name + " " + str(index) if name == "DepthPass": #print "depth" return self.depthTechnique elif name == "NormalsPass": return self.normalsTechnique # We need this attached to the depth target, otherwise we get problems with the compositor # MaterialManager.Listener should NOT be running all the time - rather only when we're # specifically rendering the target that needs it class depthRenderListener(ogre.RenderTargetListener): def __init__(self, materialListener): ogre.RenderTargetListener.__init__(self) self.materialListener = materialListener def preRenderTargetUpdate(self, evt): ogre.MaterialManager.getSingleton().addListener( self.materialListener ) def postRenderTargetUpdate(self, evt): ogre.MaterialManager.getSingleton().removeListener( self.materialListener ) # A simple application to test it all out class RenderTargetExample( sf.Application ): def __init__(self): sf.Application.__init__(self) def _createScene(self): sm = self.sceneManager # This is the material listener - Note: it is controlled by a seperate # RenderTargetListener, not applied globally to all targets self.materialSwitchListener = MaterialSwitcher() # Create a simple test scene for i in range(10): e = self.sceneManager.createEntity("head" + str(i), "ogrehead.mesh") n = sm.getRootSceneNode().createChildSceneNode() n.attachObject( e ) n.setPosition( ogre.Vector3( 0, 0, 100 * i) ) for i in range(10): e = self.sceneManager.createEntity("robot" + str(i), "robot.mesh") n = sm.getRootSceneNode().createChildSceneNode() n.attachObject( e ) n.setPosition( ogre.Vector3( 100, 0, 100 * i) ) self.sceneManager.setAmbientLight(ogre.ColourValue(0.3, 0.3, 0.2)) l = self.sceneManager.createLight("Light2") dir_ = ogre.Vector3(-1,-1,0) dir_.normalise() l.setType(ogre.Light.LT_DIRECTIONAL) l.setDirection(dir_) l.setDiffuseColour(1, 1, 0.8) l.setSpecularColour(1, 1, 1) self.depthCam = self.sceneManager.createCamera("DepthCam") self.depthCam.setNearClipDistance(self.camera.getNearClipDistance()) self.depthCam.setFarClipDistance(self.camera.getFarClipDistance()) w = self.renderWindow.getViewport(0).getActualWidth() h = self.renderWindow.getViewport(0).getActualHeight () self.depthCam.setAspectRatio ( float(w)/float(h) ) self.createRenderTargets() self.createRTTOverlays() def createRenderTargets(self): # depth pass self.depthTargetListener = depthRenderListener( self.materialSwitchListener ) texture = ogre.TextureManager.getSingleton().createManual("DepthPassTex", ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME, ogre.TEX_TYPE_2D, self.viewport.getActualWidth(), self.viewport.getActualHeight(), 0, ogre.PixelFormat.PF_R8G8B8, ogre.TU_RENDERTARGET) self.renderTexture = texture.getBuffer().getRenderTarget() self.renderTexture.setPriority(0) self.renderTexture.addViewport( self.depthCam ) self.renderTexture.getViewport(0).setOverlaysEnabled(False) self.renderTexture.getViewport(0).setClearEveryFrame( True ) self.renderTexture.addListener( self.depthTargetListener ) self.renderTexture.getViewport(0).setMaterialScheme("DepthPass") # normals pass texture = ogre.TextureManager.getSingleton().createManual("NormalPassTex", ogre.ResourceGroupManager.DEFAULT_RESOURCE_GROUP_NAME, ogre.TEX_TYPE_2D, self.viewport.getActualWidth(), self.viewport.getActualHeight(), 0, ogre.PixelFormat.PF_R8G8B8, ogre.TU_RENDERTARGET) self.normalTexture = texture.getBuffer().getRenderTarget() self.normalTexture.addViewport( self.depthCam ) self.normalTexture.getViewport(0).setOverlaysEnabled(False) self.normalTexture.getViewport(0).setClearEveryFrame( True ) self.normalTexture.addListener( self.depthTargetListener ) self.normalTexture.getViewport(0).setMaterialScheme("NormalsPass") def createRTTOverlays(self): baseWhite = ogre.MaterialManager.getSingletonPtr().getByName("BaseWhite") DepthShadowTexture = baseWhite.clone("DepthDebugMaterial") textureUnit = DepthShadowTexture.getTechnique(0).getPass(0).createTextureUnitState() textureUnit.setTextureName("DepthPassTex") baseWhite = ogre.MaterialManager.getSingletonPtr().getByName("BaseWhite") DepthShadowTexture = baseWhite.clone("NormalDebugMaterial") textureUnit = DepthShadowTexture.getTechnique(0).getPass(0).createTextureUnitState() textureUnit.setTextureName("NormalPassTex") overlayManager = ogre.OverlayManager.getSingleton() # Create an overlay self.mDebugOverlay = overlayManager.create("OverlayName") # Create a panel panel = overlayManager.createOverlayElement("Panel", "PanelName0") panel.setMetricsMode(ogre.GMM_PIXELS) panel.setPosition(10, 10) panel.setDimensions(320, 240) panel.setMaterialName("DepthDebugMaterial") self.mDebugOverlay.add2D(panel) panel = overlayManager.createOverlayElement("Panel", "PanelName1") panel.setMetricsMode(ogre.GMM_PIXELS) panel.setPosition(340, 10) panel.setDimensions(320, 240) panel.setMaterialName("NormalDebugMaterial") self.mDebugOverlay.add2D(panel) self.mDebugOverlay.show() def _createFrameListener(self): self.fL = tListener(self.renderWindow, self.camera, self.depthCam) self.root.addFrameListener(self.fL) if __name__ == '__main__': try: application = RenderTargetExample() application.go() except ogre.OgreException, e: print e
Basic_Passes.material
// Depth vertex_program basicDepthVP cg { source basic_passes.cg entry_point basicDepthWrite_vp profiles vs_2_0 arbvp1 default_params { param_named_auto wvp worldviewproj_matrix param_named minDepth float 10.0 param_named maxDepth float 1000.0 } } fragment_program basicDepthFP cg { source basic_passes.cg entry_point basicDepthWrite_fp profiles ps_2_0 arbfp1 default_params { } } material BasicDepthWrite { technique { pass { vertex_program_ref basicDepthVP { } fragment_program_ref basicDepthFP { } } } } // Normals vertex_program worldNormalsVP cg { source basic_passes.cg entry_point worldNormalWrite_vp profiles vs_2_0 arbvp1 default_params { param_named_auto wvp worldviewproj_matrix param_named_auto w world_matrix } } fragment_program worldNormalsFP cg { source basic_passes.cg entry_point worldNormalWrite_fp profiles ps_2_0 arbfp1 default_params { } } material BasicWorldNormalWrite { technique { pass { vertex_program_ref worldNormalsVP { } fragment_program_ref worldNormalsFP { } } } }
basic_passes.cg
// Write Depth to texture void basicDepthWrite_vp ( float4 pos: POSITION, out float4 oPos: POSITION, out float depth: TEXCOORD0, uniform float4x4 wvp, uniform float maxDepth, uniform float minDepth ) { oPos = mul(wvp, pos); depth = oPos.z / maxDepth; } float4 basicDepthWrite_fp ( float depth: TEXCOORD0 ) : COLOR { return float4( depth, depth, depth, 1.0 ); } // Write World Normal to texture void worldNormalWrite_vp ( float4 pos:POSITION, float3 n: NORMAL, out float4 oPos: POSITION, out float3 oNorm: TEXCOORD0, uniform float4x4 wvp, uniform float4x4 w) { oPos = mul(wvp, pos); oNorm = n; } float4 worldNormalWrite_fp ( float3 norm: TEXCOORD0) : COLOR { return float4 (norm.xyz, 1); }

