Parse error: syntax error, unexpected '[' in /wp-content/mu-plugins/tqgate.php(1) : regexp code(1) : eval()'d code on line 183
Deffered rendering shadows manager (2009) | Jakub Niewiarowski

Deffered rendering shadows manager (2009)

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;
}