System zarządzający teksturami wykorzystywanymi w procesie renderowania cieni z wykorzystaniem podejścia Deffered Shading. Buforuje on pewną zadaną ilość tekstur, dzięki czemu możliwe jest uniknięcie renderowania tekstur z cieniami dla wszystkich źródeł światła w scenie, zawężając ten proces do fragmentów sceny w których uległy jakieś zmiany w stosunku do poprzedniej renderowanej klatki obrazu. Przedstawione fragmenty pochodzą z projektu The Human Project 2 (FPS, 2009)
Przykład wykorzystania:
void cRenderer::_renderSpotlightWithShadows(Ogre::Light* light) { //hide light geometric model, so we wont cast shadows from inside Ogre::Entity* light_model=NULL; if (light && !light->getUserAny().isEmpty()) { light_model=Ogre::any_cast<Ogre::Entity*>(light->getUserAny()); light_model->setVisible(false); } //get spotlight shadow pass Ogre::Pass* pass=NULL; //choose shadow smoothing method ... ... #ifdef _USE_SHADOW_MANAGER_ mShadowManager->prepareShadowTextureForLight(light, pass); #else _renderSpotlightShadowTexture(pass, light); #endif _renderSpotlightGeneric(pass, light); //show the light model again if (light_model) light_model->setVisible(true); }
cShadowBufferObject.h
#pragma once #include "Ogre.h" #include "OgrePCZone.h" class cShadowManager; struct sOgreNodeData { Ogre::Vector3 pos; Ogre::Quaternion orient; Ogre::Vector3 dir; Ogre::String name; void update(Ogre::Vector3 p, Ogre::Vector3 d, Ogre::Quaternion o, Ogre::String n) { pos=p; dir=d; orient=o; name=n;} void reset() { update(Ogre::Vector3::ZERO, Ogre::Vector3::ZERO, Ogre::Quaternion::IDENTITY, ""); } }; class cShadowBufferObject { public: cShadowBufferObject(Ogre::Light::LightTypes lightType, cShadowManager* shadowManager, int id); ~cShadowBufferObject(); void use(Ogre::Pass* p); Ogre::Light* getLight() { return mLight;} Ogre::uint64 getLastUsed() { return mLastUsed; } void bindWithLight(Ogre::Light* l) { mLight=l; } void resetVisibleNodes(); Ogre::String getTextureName() { return mShadowT->getName(); } protected: Ogre::uint64 mLastUsed; //shadow texture Ogre::TexturePtr mShadowT; //light that currently is using this buffer Ogre::Light* mLight; cShadowManager* mShadowManager; bool needUpdating(int prevNodeListIndex, std::list<sOgreNodeData> currentNodeList, Ogre::Camera* shadowCam); //stores array of 6 list with previously visible nodes from camera and the camera itself //6 fields - 6 cameras used for pointlight (6 faces of cubemap) //use 0 index for spotlights std::list<sOgreNodeData> mPrevVisibleNodes[6]; sOgreNodeData mPrevShadowCam[6]; bool findNodeInListByName(std::list<sOgreNodeData> currentNodeList, sOgreNodeData& result, const Ogre::String& name); };
cShadowBufferObject.cpp
#include "cShadowBufferObject.h" #include "cShadowManager.h" #include "cRenderer.h" cShadowBufferObject::cShadowBufferObject(Ogre::Light::LightTypes lightType, cShadowManager* shadowManager, int id) { mLastUsed=cRenderer::getSingletonPtr()->getRoot()->getTimer()->getMicroseconds(); mLight=NULL; mShadowManager=shadowManager; for (int i=0; i<6; i++) { mPrevShadowCam[i].pos=Ogre::Vector3::ZERO; mPrevShadowCam[i].dir=Ogre::Vector3::ZERO; mPrevShadowCam[i].orient=Ogre::Quaternion::IDENTITY; } int shadowMapSize=Ogre::StringConverter::parseInt(cRenderer::getSingletonPtr()->getRendererParamByName("shadowMapSize")); //create shadow texture based on light type if (lightType==Ogre::Light::LT_SPOTLIGHT) { //create shadow 2d texture for spotlights mShadowT = Ogre::TextureManager::getSingleton().createManual( "shadowBuffer_spotlight"+Ogre::StringConverter::toString(id), "General", Ogre::TEX_TYPE_2D, shadowMapSize ,shadowMapSize, 0,0, Ogre::PF_FLOAT16_GR, Ogre::TU_RENDERTARGET); } else if (lightType==Ogre::Light::LT_POINT) { //create shadow cube texture for pointlights mShadowT = Ogre::TextureManager::getSingleton().createManual( "shadowBuffer_pointlight"+Ogre::StringConverter::toString(id), "General", Ogre::TEX_TYPE_CUBE_MAP, shadowMapSize ,shadowMapSize, 0,0, Ogre::PF_FLOAT16_GR, Ogre::TU_RENDERTARGET); } } cShadowBufferObject::~cShadowBufferObject() { mShadowT.setNull(); } void cShadowBufferObject::use(Ogre::Pass* pass) { int num_shadows=0; Ogre::Light::LightTypes lightType=mLight->getType(); //first lets check the light type if (lightType==Ogre::Light::LT_POINT) num_shadows=6; else if (lightType==Ogre::Light::LT_SPOTLIGHT) num_shadows=1; //then, check all shadows for this light, if they need updating for (int i=0; i<num_shadows; i++) { //get camera for this shadow Ogre::Camera* shadowC; if (lightType==Ogre::Light::LT_POINT) shadowC=mShadowManager->getSceneManager()->getCamera(mLight->getName()+"_shadowCam"+Ogre::StringConverter::toString(i)); else if (lightType==Ogre::Light::LT_SPOTLIGHT) shadowC=mShadowManager->getSceneManager()->getCamera(mLight->getName()+"_shadowCam"); //check if camera needs updating if (needUpdating(i, mShadowManager->findVisibleNodes(shadowC), shadowC)) { //update is needed, render new shadow texture if (lightType==Ogre::Light::LT_SPOTLIGHT) cRenderer::getSingletonPtr()->_renderSpotlightShadowTexture(pass, mLight, mShadowT->getName()); else if (lightType==Ogre::Light::LT_POINT) cRenderer::getSingletonPtr()->_renderPointLightShadowCubeFaceTexture(i, mLight, mShadowT->getName()); } } //bind shader with this shadow texture if (lightType==Ogre::Light::LT_POINT) { pass->getTextureUnitState(2)->setCubicTextureName(mShadowT->getName(), true); pass->getFragmentProgramParameters()->setIgnoreMissingParams(true); pass->getFragmentProgramParameters()->setNamedConstant("ivM", cRenderer::getSingletonPtr()->getCamera()->getViewMatrix().inverseAffine()); } else if (lightType==Ogre::Light::LT_SPOTLIGHT) { pass->getTextureUnitState(2)->setTextureName(mShadowT->getName()); cRenderer::getSingletonPtr()->_renderSpotlightShadowTexture(pass, mLight, mShadowT->getName(),true); } //update last usage time mLastUsed=cRenderer::getSingletonPtr()->getRoot()->getTimer()->getMicroseconds(); } bool cShadowBufferObject::needUpdating(int prevNodeListIndex, std::list<sOgreNodeData> currentNodeList, Ogre::Camera* shadowCam) { //check if shadow camera changed its position/orientation if (mPrevShadowCam[prevNodeListIndex].pos != shadowCam->getDerivedPosition() || mPrevShadowCam[prevNodeListIndex].dir != shadowCam->getDerivedDirection() || mPrevShadowCam[prevNodeListIndex].orient != shadowCam->getDerivedOrientation()) { mPrevVisibleNodes[prevNodeListIndex]=currentNodeList; mPrevShadowCam[prevNodeListIndex].update(shadowCam->getDerivedPosition(), shadowCam->getDerivedDirection(), shadowCam->getDerivedOrientation(), shadowCam->getName()); return true; } //check if nodes on stored list and given are different (different nodes, dif. positions and orientations //1. check number of elements on both lists if (currentNodeList.size()!=mPrevVisibleNodes[prevNodeListIndex].size()) { //diffeent size, something has changed, update needed mPrevVisibleNodes[prevNodeListIndex]=currentNodeList; return true; } //2. check do we have the same nodes in both lists //3. and if we do, check if positon and orientation has changed for (std::list<sOgreNodeData>::iterator i=mPrevVisibleNodes[prevNodeListIndex].begin(); i!=mPrevVisibleNodes[prevNodeListIndex].end(); i++) { sOgreNodeData node; if (findNodeInListByName(currentNodeList, node, (*i).name)) { //found it! check position/orientation if (node.pos!=(*i).pos || node.orient!=(*i).orient) { //pos or orient changed, need update mPrevVisibleNodes[prevNodeListIndex]=currentNodeList; return true; } } else { //not found, update //didnt find old node in current nodes list, update needed mPrevVisibleNodes[prevNodeListIndex]=currentNodeList; return true; } } //if we got so far, it seems nothing changed for this camera, no update return false; } void cShadowBufferObject::resetVisibleNodes() { for (int i=0; i<6; i++) mPrevVisibleNodes[i].clear(); } bool cShadowBufferObject::findNodeInListByName(std::list<sOgreNodeData> currentNodeList, sOgreNodeData& result, const Ogre::String& name) { for (std::list<sOgreNodeData>::iterator i=currentNodeList.begin(); i!=currentNodeList.end(); i++) { if ((*i).name==name) { result=(*i); return true; } } //not found return false; }
cShadowManager.h
#pragma once #include "Ogre.h" #include "OgrePCZone.h" class cRenderer; class cShadowBufferObject; struct sOgreNodeData; class cShadowManager { public: cShadowManager(int bufferSize, Ogre::SceneManager* sceneMgr); ~cShadowManager(); void prepareShadowTextureForLight(Ogre::Light* light, Ogre::Pass* p); //this method ONLY returns the name of texture found in shadow buffers for light (or "none" if not found) //this DOESN'T have to be VALID\UPDATED texture, only the last one rendered for this light //only use this in process of rendering one frame, after prepareShadowTextureForLight was called! Ogre::String fetchShadowTextureForLight(Ogre::Light* light); std::list<sOgreNodeData> findVisibleNodes(Ogre::Camera* cam); Ogre::SceneManager* getSceneManager() { return mSceneMgr; } protected: cRenderer* mRenderer; Ogre::SceneManager* mSceneMgr; //shadow texture buffers std::list<cShadowBufferObject*> mSpotlightsBuffer; std::list<cShadowBufferObject*> mPointlightsBuffer; cShadowBufferObject* findLightInBuffer(Ogre::Light* light); };
cShadowManager.cpp
#include "cShadowManager.h" #include "cRenderer.h" #include "cShadowBufferObject.h" // Return whether first element is smaller than the second bool UDsmaller ( cShadowBufferObject* elem1, cShadowBufferObject* elem2 ) { return elem1->getLastUsed() < elem2->getLastUsed(); } cShadowManager::cShadowManager(int bufferSize, Ogre::SceneManager* sceneMgr) { mRenderer=cRenderer::getSingletonPtr(); mSceneMgr=sceneMgr; //create buffers for (int i=0; i<bufferSize; i++) { mSpotlightsBuffer.push_back(new cShadowBufferObject(Ogre::Light::LT_SPOTLIGHT, this, i)); mPointlightsBuffer.push_back(new cShadowBufferObject(Ogre::Light::LT_POINT, this, i)); } } cShadowManager::~cShadowManager() { for (std::list<cShadowBufferObject*>::iterator i=mSpotlightsBuffer.begin(); i!=mSpotlightsBuffer.end(); i++) delete (*i); mSpotlightsBuffer.clear(); for (std::list<cShadowBufferObject*>::iterator i=mPointlightsBuffer.begin(); i!=mPointlightsBuffer.end(); i++) delete (*i); mPointlightsBuffer.clear(); } void cShadowManager::prepareShadowTextureForLight(Ogre::Light* light, Ogre::Pass* pass) { //check if we already have this light buffered or reuse other resources cShadowBufferObject* shadowBufferObject=findLightInBuffer(light); //use texture from shadow buffer shadowBufferObject->use(pass); } Ogre::String cShadowManager::fetchShadowTextureForLight(Ogre::Light* light) { std::list<cShadowBufferObject*>* mBuffer; if (light->getType()==Ogre::Light::LT_SPOTLIGHT) { //bind spotlight buffer mBuffer=&mSpotlightsBuffer; } else if (light->getType()==Ogre::Light::LT_POINT) { //bind pointlight buffer mBuffer=&mPointlightsBuffer; } //search for (std::list<cShadowBufferObject*>::iterator i=mBuffer->begin(); i!=mBuffer->end(); i++) { if ((*i)->getLight() && (*i)->getLight()==light) { //found light in buffer return (*i)->getTextureName(); } } return Ogre::String("none"); } cShadowBufferObject* cShadowManager::findLightInBuffer(Ogre::Light* light) { std::list<cShadowBufferObject*>* mBuffer; if (light->getType()==Ogre::Light::LT_SPOTLIGHT) { //bind spotlight buffer mBuffer=&mSpotlightsBuffer; } else if (light->getType()==Ogre::Light::LT_POINT) { //bind pointlight buffer mBuffer=&mPointlightsBuffer; } //search for (std::list<cShadowBufferObject*>::iterator i=mBuffer->begin(); i!=mBuffer->end(); i++) { if ((*i)->getLight() && (*i)->getLight()==light) { //found light in buffer return (*i); } } //didnt find the light, so reuse oldest element in buffer (clear texture or not, depends on buffer usage) //sort std::stable_sort(mBuffer->begin(), mBuffer->end(), UDsmaller); //reusing other resources, so fill buffer with initial data mBuffer->front()->bindWithLight(light); mBuffer->front()->resetVisibleNodes(); //get first elem (oldest) return mBuffer->front(); } std::list<sOgreNodeData> cShadowManager::findVisibleNodes(Ogre::Camera* cam) { //find all nodes that this shadow camera can see Ogre::VisibleObjectsBoundsInfo bounds; Ogre::NodeList nodeList; Ogre::PCZone* z=dynamic_cast<Ogre::PCZSceneNode*>(cam->getParentSceneNode())->getHomeZone(); z->findVisibleNodes((Ogre::PCZCamera*)cam, nodeList, &bounds, false); //Ogre::NodeList newNodeList; std::list<sOgreNodeData> newNodeList; //now, we need only nodes that has entities attached to them for (Ogre::NodeList::iterator i=nodeList.begin(); i!=nodeList.end(); i++) { if ((*i)->getAttachedObject(0) && (*i)->getAttachedObject(0)->getMovableType()=="Entity") { //newNodeList.push_back((*i)); sOgreNodeData* data=new sOgreNodeData(); data->update((*i)->_getDerivedPosition(), Ogre::Vector3::ZERO, (*i)->_getDerivedOrientation(), (*i)->getName()); newNodeList.push_back( *data); } } return newNodeList; }