Skip to main content
Heat Shimmer Manager         A simple to use manager for adding a particle based heat shimmer effect
Image


Created for the purpose to easily create multiple heat shimmers either via scene loader or manually, and manage the shader in general.

Usage

Include HeatShimmerManager.h in your project, make sure the needed scripts are in place, create your manager object, and after the scene is loaded, call its initialize(bool) function.

If you use a scene loader, name your scene nodes where you want the heat shimmer effect to begin with "HeatSystem_". Same goes with bones on skeletal entities. If you use either, it would be best to call initialize(true), as that goes through and looks for those named elements.

HeatShimmerManager relies heavily on the systems being a certain name, and being a certain amount of systems. As such, there is not yet a remove function, however in case of changing scenes, the manager has a reset() function that will clear the -SceneManager of the particle system and the HeatShimmerManager of how many particle systems there are.

For manual adding of systems, there are add functions.

Code

HeatShimmerManager

Copy to clipboard
/* ----------------------------------------------------------------------------- Filename: HeatShimmerManager.h ----------------------------------------------------------------------------- This is the source file to create and manage multiple instances of the heat shimmer particle shader. Uses OGRE (Object-oriented Graphics Rendering Engine) Copyright (c) 2000-2009 The OGRE Team For the latest info, see http://www.ogre3d.org/ While OGRE is under the LGPL, this file is in the public domain, and may be used for whatever you like. If your -SceneManager or loader can have user data attached to nodes, you can modify the addHeat and setHeatParticle functions to load user data to modify the particle system. I did not do this because from what I can tell, there are different user data systems that store it differently. By Shadowking97 @ Ogre 3d forums ----------------------------------------------------------------------------- */ #ifndef __HeatShimmerManager_H__ #define __HeatShimmerManager_H__ #include "Ogre.h" #include "OgreStringConverter.h" #include "OgreException.h" using namespace Ogre; //Renderable Listener combined with RenderTargetListener to black out non-heat related //renderables class HeatRenderableListener: public RenderTargetListener, RenderQueue::RenderableListener { private: SceneManager* mSceneMgr; Camera* cam; //Number of heat particle emmiters. int numParticles; RenderQueue* queue; public: HeatRenderableListener(SceneManager* scene, Camera* c, int p): cam(c) { numParticles = p; mSceneMgr = scene; queue = mSceneMgr->getRenderQueue(); } void preRenderTargetUpdate(const RenderTargetEvent& evt) { // Todo: Set non-heat related soft particles to black while keeping alpha //set heat particle camera to the same loc/rot of the default camera mSceneMgr->getCamera("HeatCam")->setPosition(cam->getPosition()); mSceneMgr->getCamera("HeatCam")->setOrientation(cam->getOrientation()); //Set heat particles visible and set renderablelistener for(int i = 0;i<numParticles;i++) mSceneMgr->getParticleSystem("HeatParticleSystem_"+StringConverter::toString(i))->setVisible(true); queue->setRenderableListener(this); } void postRenderTargetUpdate(const RenderTargetEvent& evt) { // Reset the heat particles and RenderableListener for(int i = 0;i<numParticles;i++) mSceneMgr->getParticleSystem("HeatParticleSystem_"+StringConverter::toString(i))->setVisible(false); queue->setRenderableListener(0); } bool renderableQueued(Renderable* rend, uint8 groupID, ushort priority, Technique** ppTech, RenderQueue* pQueue) { //If renderable's technique isn't the Technique from Particle/Heat, //Renderable is not a heat particle so set the technique to Darkness Technique* tech = *ppTech; if(tech->getParent()->getName()!="Particle/Heat") { MaterialPtr mat = MaterialManager::getSingleton().getByName("Heat/Darkness"); tech = mat->getTechnique(0); *ppTech = tech; } return true; } }; //Main manager for heat particles. class HeatShimmerManager { private: //Main render camera Camera* mCamera; //Camera created for heat rendering Camera* mHeatCam; SceneManager* mSceneMgr; RenderTexture* heatRender; RenderWindow* mWindow; //Number of particle systems int particleCount; public: HeatShimmerManager(Camera* MainCam, SceneManager* MainScene, RenderWindow* win): mCamera(MainCam), mSceneMgr(MainScene), mWindow(win) { particleCount=0; } /* HeatShimmerManager::Initialize(bool) Used to initialize Heat Shimmer Manager. Sets up the heat camera, viewport, render texture Adds Renderable/RenderTarget Lisstener to the texture Adds Heat Compositor Call this function after the scene has been set up inbetween the compositors you want the heat wave to go. @setUpParticles If true, calls functions to search for nodes and bones named for particle system attachment. */ void initialize(bool setUpParticles) { mHeatCam = mSceneMgr->createCamera("HeatCam"); mHeatCam->setNearClipDistance(mCamera->getNearClipDistance()); mHeatCam->setFarClipDistance(mCamera->getFarClipDistance()); mHeatCam->setAspectRatio(mCamera->getAspectRatio()); mHeatCam->setFOVy (mCamera->getFOVy()); mHeatCam->setOrientation(mCamera->getOrientation()); mHeatCam->setPosition(mCamera->getPosition()); TexturePtr heatTex = TextureManager::getSingleton().createManual("heat", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, TEX_TYPE_2D, mWindow->getWidth()/2, mWindow->getHeight()/2, 0, PF_R8G8B8, TU_RENDERTARGET); heatRender = heatTex->getBuffer()->getRenderTarget(); heatRender->addViewport(mHeatCam); heatRender->getViewport(0)->setClearEveryFrame(true); heatRender->getViewport(0)->setBackgroundColour(ColourValue::Black); heatRender->getViewport(0)->setOverlaysEnabled(false); heatRender->setAutoUpdated(true); if(setUpParticles) { setHeatParticles(mSceneMgr->getRootSceneNode()); setBonedHeatParticles(); } HeatRenderableListener * heatListener = new HeatRenderableListener(mSceneMgr,mCamera,particleCount); heatRender->addListener(heatListener); CompositorManager::getSingleton().addCompositor(mCamera->getViewport(), "heat_waves"); CompositorManager::getSingleton().setCompositorEnabled(mCamera->getViewport(),"heat_waves",true); } /* HeatShimmerManager::reset() Clears the Heat Shimmer and -Scene Managers of all particle systems. */ void reset() { for(int i = 0; i<particleCount;i++) { mSceneMgr->destroyParticleSystem("HeatParticleSystem_"+StringConverter::toString(i)); } particleCount=0; } //Manual heat particle creation on SceneNode void addHeat(SceneNode* n) { ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat"); particleCount++; n->attachObject(heat_Particles); heat_Particles->setVisible(false); } //Manual heat particle creation on SceneNode void addHeat(String sceneNodeName) { ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat"); particleCount++; mSceneMgr->getSceneNode(sceneNodeName)->attachObject(heat_Particles); heat_Particles->setVisible(false); } //Manual heat particle creation on Entity bone void addHeatToBone(Bone* b, Entity* e) { ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat"); particleCount++; e->attachObjectToBone(b->getName(),heat_Particles); heat_Particles->setVisible(false); } //Manual heat particle creation on Entity bone void addHeatToBone(String boneName, Entity* e) { ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat"); particleCount++; e->attachObjectToBone(boneName,heat_Particles); heat_Particles->setVisible(false); } protected: /* setHeatParticles(SceneNode*) Goes through the scene recursivly via root scene node attaching ParticleSystems for heat to the apropriate nodes. */ void setHeatParticles(SceneNode* node) { if(node->getName().find("HeatSystem_")!=-1) { ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat"); particleCount++; node->attachObject(heat_Particles); heat_Particles->setVisible(false); } Node::ChildNodeIterator iterator = node->getChildIterator(); while(iterator.hasMoreElements()) { SceneNode* n = dynamic_cast<SceneNode*>(iterator.getNext()); if(n) setHeatParticles(n); } } /* setBonedHeatParticles() Goes through Entities in a scene and attaches heat particles to the appropriate bones. */ void setBonedHeatParticles() { SceneManager::MovableObjectIterator iterator = mSceneMgr->getMovableObjectIterator("Entity"); while(iterator.hasMoreElements()) { Entity* e = static_cast<Entity*>(iterator.getNext()); if(e->hasSkeleton()&&e->isInScene()) { SkeletonInstance* s = e->getSkeleton(); Skeleton::BoneIterator bones = s->getBoneIterator(); while(bones.hasMoreElements()) { Bone* bone = static_cast<Bone*>(bones.getNext()); if(bone->getName().find("HeatSystem_")!=-1) { ParticleSystem* heat_Particles = mSceneMgr->createParticleSystem("HeatParticleSystem_"+StringConverter::toString(particleCount), "Render/Heat"); particleCount++; e->attachObjectToBone(bone->getName(),heat_Particles); heat_Particles->setVisible(false); } } } } } }; #endif

Particles


Copy to clipboard
particle_system Render/Heat { quota 1500 material Particle/Heat particle_width 1 particle_height 1 cull_each true renderer billboard billboard_type point emitter Box { angle 73.17 colour 1 1 1 1 colour_range_start 1 1 1 1 colour_range_end 1 1 1 1 direction 0 0 1 emission_rate 500 position 0 0 0 velocity 8 velocity_min 8 velocity_max 8 time_to_live 1.5 time_to_live_min 1.5 time_to_live_max 1.5 duration 0 duration_min 0 duration_max 0 repeat_delay 0 repeat_delay_min 0 repeat_delay_max 0 width 10 height 10 depth 10 } affector LinearForce { force_vector 0 9.8 0 force_application add } affector ColourFader { red -0.2967 green -0.2768 blue -0.7465 alpha 0 } }

Materials


Copy to clipboard
material Particle/Heat { technique { pass { lighting off depth_write off scene_blend add texture_unit { texture heat_sprite.png } } } } material Heat/Darkness { technique { pass { lighting off texture_unit { texture darkness.png } } } } fragment_program Distortion/Heat_ps cg { source heat_Distortion.cg entry_point main profiles ps_2_0 arbfp1 default_params { param_named distortionScale float 2 } } material Ogre/Compositor/Heat_wave { technique { pass { depth_check off vertex_program_ref Ogre/Compositor/StdQuad_Cg_vp { } fragment_program_ref Distortion/Heat_ps { } texture_unit Texture0 { tex_coord_set 0 tex_address_mode clamp filtering linear linear linear } texture_unit Texture1 { texture heat tex_coord_set 1 tex_address_mode clamp filtering linear linear linear } } } }

Compositor


Copy to clipboard
compositor heat_waves { technique { texture main target_width target_height PF_R8G8B8 target main { input previous } target_output { input none pass render_quad { material Ogre/Compositor/Heat_wave input 0 main } } } }

heat_Distortion.cg


Copy to clipboard
sampler Texture0 : register(s0); sampler Texture1 : register(s1); float distortionScale; float4 main(float2 texCoord : TEXCOORD0) : COLOR { float2 distort = tex2D(Texture1,texCoord).xy; return tex2D(Texture0, texCoord - distortionScale*(distort/100*(texCoord-0.5))); }

Images Used



darkness.png
heat_sprite.png