1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-22 08:53:54 +00:00

use hardware occlusion query for sun glare effect

This commit is contained in:
scrawl 2012-03-24 17:59:26 +01:00
parent 5fba52c238
commit 743ea0c9be
7 changed files with 216 additions and 61 deletions

View file

@ -2,13 +2,17 @@
#include <OgreRenderSystem.h> #include <OgreRenderSystem.h>
#include <OgreRoot.h> #include <OgreRoot.h>
#include <OgreBillboardSet.h>
using namespace MWRender; using namespace MWRender;
using namespace Ogre; using namespace Ogre;
OcclusionQuery::OcclusionQuery() : OcclusionQuery::OcclusionQuery(OEngine::Render::OgreRenderer* renderer, SceneNode* sunNode) :
mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0) mSunTotalAreaQuery(0), mSunVisibleAreaQuery(0), mActiveQuery(0), mDoQuery(0), mSunVisibility(0)
{ {
mRendering = renderer;
mSunNode = sunNode;
try { try {
RenderSystem* renderSystem = Root::getSingleton().getRenderSystem(); RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
@ -23,5 +27,113 @@ OcclusionQuery::OcclusionQuery() :
} }
if (!mSupported) if (!mSupported)
{
std::cout << "Hardware occlusion queries not supported." << std::endl; std::cout << "Hardware occlusion queries not supported." << std::endl;
return;
}
// This means that everything up to RENDER_QUEUE_MAIN can occlude the objects that are tested
const int queue = RENDER_QUEUE_MAIN+1;
MaterialPtr matBase = MaterialManager::getSingleton().getByName("BaseWhiteNoLighting");
MaterialPtr matQueryArea = matBase->clone("QueryTotalPixels");
matQueryArea->setDepthWriteEnabled(false);
matQueryArea->setColourWriteEnabled(false);
matQueryArea->setDepthCheckEnabled(false); // Not occluded by objects
MaterialPtr matQueryVisible = matBase->clone("QueryVisiblePixels");
matQueryVisible->setDepthWriteEnabled(false);
matQueryVisible->setColourWriteEnabled(false);
matQueryVisible->setDepthCheckEnabled(true); // Occluded by objects
mBBQueryTotal = mRendering->getScene()->createBillboardSet(1);
mBBQueryTotal->setDefaultDimensions(150, 150);
mBBQueryTotal->createBillboard(Vector3::ZERO);
mBBQueryTotal->setMaterialName("QueryTotalPixels");
mBBQueryTotal->setRenderQueueGroup(queue);
mSunNode->attachObject(mBBQueryTotal);
mBBQueryVisible = mRendering->getScene()->createBillboardSet(1);
mBBQueryVisible->setDefaultDimensions(150, 150);
mBBQueryVisible->createBillboard(Vector3::ZERO);
mBBQueryVisible->setMaterialName("QueryVisiblePixels");
mBBQueryVisible->setRenderQueueGroup(queue);
mSunNode->attachObject(mBBQueryVisible);
mRendering->getScene()->addRenderObjectListener(this);
mDoQuery = true;
} }
OcclusionQuery::~OcclusionQuery()
{
RenderSystem* renderSystem = Root::getSingleton().getRenderSystem();
if (mSunTotalAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunTotalAreaQuery);
if (mSunVisibleAreaQuery) renderSystem->destroyHardwareOcclusionQuery(mSunVisibleAreaQuery);
}
bool OcclusionQuery::supported()
{
return mSupported;
}
void OcclusionQuery::notifyRenderSingleObject(Renderable* rend, const Pass* pass, const AutoParamDataSource* source,
const LightList* pLightList, bool suppressRenderStateChanges)
{
if (!mSupported) return;
// The following code activates and deactivates the occlusion queries
// so that the queries only include the rendering of their intended targets
// Close the last occlusion query
// Each occlusion query should only last a single rendering
if (mActiveQuery != NULL)
{
mActiveQuery->endOcclusionQuery();
mActiveQuery = NULL;
}
// Open a new occlusion query
if (mDoQuery == true)
{
if (rend == mBBQueryTotal)
mActiveQuery = mSunTotalAreaQuery;
else if (rend == mBBQueryVisible)
mActiveQuery = mSunVisibleAreaQuery;
if (mActiveQuery != NULL)
{
mActiveQuery->beginOcclusionQuery();
}
}
}
void OcclusionQuery::update()
{
if (!mSupported) return;
// Stop occlusion queries until we get their information
// (may not happen on the same frame they are requested in)
mDoQuery = false;
if (!mSunTotalAreaQuery->isStillOutstanding() && !mSunVisibleAreaQuery->isStillOutstanding())
{
unsigned int totalPixels;
unsigned int visiblePixels;
mSunTotalAreaQuery->pullOcclusionQuery(&totalPixels);
mSunVisibleAreaQuery->pullOcclusionQuery(&visiblePixels);
if (totalPixels == 0)
{
// probably outside of the view frustum
mSunVisibility = 0;
}
else
{
mSunVisibility = float(visiblePixels) / float(totalPixels);
if (mSunVisibility > 1) mSunVisibility = 1;
}
mDoQuery = true;
}
}

View file

@ -2,25 +2,49 @@
#define _GAME_OCCLUSION_QUERY_H #define _GAME_OCCLUSION_QUERY_H
#include <OgreHardwareOcclusionQuery.h> #include <OgreHardwareOcclusionQuery.h>
#include <OgreRenderObjectListener.h>
#include <openengine/ogre/renderer.hpp>
namespace MWRender namespace MWRender
{ {
/// ///
/// \brief Implements hardware occlusion queries on the GPU /// \brief Implements hardware occlusion queries on the GPU
/// ///
class OcclusionQuery class OcclusionQuery : public Ogre::RenderObjectListener
{ {
public: public:
OcclusionQuery(); OcclusionQuery(OEngine::Render::OgreRenderer*, Ogre::SceneNode* sunNode);
~OcclusionQuery();
bool supported(); bool supported();
///< returns true if occlusion queries are supported on the user's hardware ///< returns true if occlusion queries are supported on the user's hardware
void update();
///< per-frame update
float getSunVisibility() const {return mSunVisibility;};
private: private:
Ogre::HardwareOcclusionQuery* mSunTotalAreaQuery; Ogre::HardwareOcclusionQuery* mSunTotalAreaQuery;
Ogre::HardwareOcclusionQuery* mSunVisibleAreaQuery; Ogre::HardwareOcclusionQuery* mSunVisibleAreaQuery;
Ogre::HardwareOcclusionQuery* mActiveQuery;
Ogre::BillboardSet* mBBQueryVisible;
Ogre::BillboardSet* mBBQueryTotal;
Ogre::SceneNode* mSunNode;
float mSunVisibility;
bool mSupported; bool mSupported;
bool mDoQuery;
OEngine::Render::OgreRenderer* mRendering;
protected:
virtual void notifyRenderSingleObject(Ogre::Renderable* rend, const Ogre::Pass* pass, const Ogre::AutoParamDataSource* source,
const Ogre::LightList* pLightList, bool suppressRenderStateChanges);
}; };
} }

View file

@ -55,11 +55,11 @@ RenderingManager::RenderingManager (OEngine::Render::OgreRenderer& _rend, const
Ogre::SceneNode *cameraPitchNode = cameraYawNode->createChildSceneNode(); Ogre::SceneNode *cameraPitchNode = cameraYawNode->createChildSceneNode();
cameraPitchNode->attachObject(mRendering.getCamera()); cameraPitchNode->attachObject(mRendering.getCamera());
mOcclusionQuery = new OcclusionQuery();
//mSkyManager = 0; //mSkyManager = 0;
mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment); mSkyManager = new SkyManager(mMwRoot, mRendering.getCamera(), &environment);
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode); mPlayer = new MWRender::Player (mRendering.getCamera(), playerNode);
mSun = 0; mSun = 0;
@ -150,8 +150,12 @@ void RenderingManager::update (float duration){
mActors.update (duration); mActors.update (duration);
mOcclusionQuery->update();
mSkyManager->update(duration); mSkyManager->update(duration);
mSkyManager->setGlare(mOcclusionQuery->getSunVisibility());
mRendering.update(duration); mRendering.update(duration);
mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() ); mLocalMap->updatePlayer( mRendering.getCamera()->getRealPosition(), mRendering.getCamera()->getRealDirection() );

View file

@ -99,6 +99,8 @@ class RenderingManager: private RenderingInterface {
void sunEnable(); void sunEnable();
void sunDisable(); void sunDisable();
bool occlusionQuerySupported() { return mOcclusionQuery->supported(); };
void setGlare(bool glare); void setGlare(bool glare);
void skyEnable (); void skyEnable ();
void skyDisable (); void skyDisable ();

View file

@ -12,6 +12,7 @@
#include "../mwworld/environment.hpp" #include "../mwworld/environment.hpp"
#include "../mwworld/world.hpp" #include "../mwworld/world.hpp"
#include "occlusionquery.hpp"
using namespace MWRender; using namespace MWRender;
using namespace Ogre; using namespace Ogre;
@ -30,7 +31,7 @@ BillboardObject::BillboardObject()
void BillboardObject::setVisible(const bool visible) void BillboardObject::setVisible(const bool visible)
{ {
mNode->setVisible(visible); mBBSet->setVisible(visible);
} }
void BillboardObject::setSize(const float size) void BillboardObject::setSize(const float size)
@ -88,7 +89,7 @@ void BillboardObject::init(const String& textureName,
/// \todo These billboards are not 100% correct, might want to revisit them later /// \todo These billboards are not 100% correct, might want to revisit them later
mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1); mBBSet = sceneMgr->createBillboardSet("SkyBillboardSet"+StringConverter::toString(bodyCount), 1);
mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize); mBBSet->setDefaultDimensions(550.f*initialSize, 550.f*initialSize);
mBBSet->setRenderQueueGroup(RENDER_QUEUE_SKIES_EARLY+2); mBBSet->setRenderQueueGroup(RENDER_QUEUE_MAIN+2);
mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON); mBBSet->setBillboardType(BBT_PERPENDICULAR_COMMON);
mBBSet->setCommonDirection( -position.normalisedCopy() ); mBBSet->setCommonDirection( -position.normalisedCopy() );
mNode = rootNode->createChildSceneNode(); mNode = rootNode->createChildSceneNode();
@ -293,7 +294,7 @@ void SkyManager::ModVertexAlpha(Entity* ent, unsigned int meshType)
} }
SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environment* env) : SkyManager::SkyManager (SceneNode* pMwRoot, Camera* pCamera, MWWorld::Environment* env) :
mGlareFade(0), mGlareEnabled(false) mGlare(0), mGlareFade(0)
{ {
mEnvironment = env; mEnvironment = env;
mViewport = pCamera->getViewport(); mViewport = pCamera->getViewport();
@ -562,10 +563,23 @@ void SkyManager::update(float duration)
mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) ); mMasser->setPhase( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
mSecunda->setPhase ( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) ); mSecunda->setPhase ( static_cast<Moon::Phase>( (int) ((mDay % 32)/4.f)) );
// increase the strength of the sun glare effect depending
// on how directly the player is looking at the sun
if (mSunEnabled) if (mSunEnabled)
{ {
// take 1/5 sec for fading the glare effect from invisible to full
if (mGlareFade > mGlare)
{
mGlareFade -= duration*5;
if (mGlareFade < mGlare) mGlareFade = mGlare;
}
else if (mGlareFade < mGlare)
{
mGlareFade += duration*5;
if (mGlareFade > mGlare) mGlareFade = mGlare;
}
// increase the strength of the sun glare effect depending
// on how directly the player is looking at the sun
Vector3 sun = mSunGlare->getPosition(); Vector3 sun = mSunGlare->getPosition();
sun = Vector3(sun.x, sun.z, -sun.y); sun = Vector3(sun.x, sun.z, -sun.y);
Vector3 cam = mViewport->getCamera()->getRealDirection(); Vector3 cam = mViewport->getCamera()->getRealDirection();
@ -573,21 +587,10 @@ void SkyManager::update(float duration)
float val = 1- (angle.valueDegrees() / 180.f); float val = 1- (angle.valueDegrees() / 180.f);
val = (val*val*val*val)*2; val = (val*val*val*val)*2;
if (mGlareEnabled) mSunGlare->setSize(val * mGlareFade);
{
mGlareFade += duration*3;
if (mGlareFade > 1) mGlareFade = 1;
}
else
{
mGlareFade -= duration*3;
if (mGlareFade < 0.3) mGlareFade = 0;
}
mSunGlare->setSize(val * (mGlareFade));
} }
mSunGlare->setVisible(mGlareFade>0 && mSunEnabled); mSunGlare->setVisible(mSunEnabled);
mSun->setVisible(mSunEnabled); mSun->setVisible(mSunEnabled);
mMasser->setVisible(mMasserEnabled); mMasser->setVisible(mMasserEnabled);
mSecunda->setVisible(mSecundaEnabled); mSecunda->setVisible(mSecundaEnabled);
@ -689,15 +692,15 @@ void SkyManager::setWeather(const MWWorld::WeatherResult& weather)
else else
strength = 1.f; strength = 1.f;
mSunGlare->setVisibility(weather.mGlareView * strength); mSunGlare->setVisibility(weather.mGlareView * mGlareFade * strength);
mSun->setVisibility(strength); mSun->setVisibility(mGlareFade >= 0.5 ? weather.mGlareView * mGlareFade * strength : 0);
mAtmosphereNight->setVisible(weather.mNight && mEnabled); mAtmosphereNight->setVisible(weather.mNight && mEnabled);
} }
void SkyManager::setGlare(bool glare) void SkyManager::setGlare(const float glare)
{ {
mGlareEnabled = glare; mGlare = glare;
} }
Vector3 SkyManager::getRealSunPos() Vector3 SkyManager::getRealSunPos()
@ -782,3 +785,8 @@ void SkyManager::setDate(int day, int month)
mDay = day; mDay = day;
mMonth = month; mMonth = month;
} }
Ogre::SceneNode* SkyManager::getSunNode()
{
return mSun->getNode();
}

View file

@ -138,6 +138,8 @@ namespace MWRender
void setWeather(const MWWorld::WeatherResult& weather); void setWeather(const MWWorld::WeatherResult& weather);
Ogre::SceneNode* getSunNode();
void sunEnable(); void sunEnable();
void sunDisable(); void sunDisable();
@ -160,7 +162,7 @@ namespace MWRender
void setThunder(const float factor); void setThunder(const float factor);
void setGlare(bool glare); void setGlare(const float glare);
Ogre::Vector3 getRealSunPos(); Ogre::Vector3 getRealSunPos();
private: private:
@ -203,12 +205,12 @@ namespace MWRender
float mRemainingTransitionTime; float mRemainingTransitionTime;
float mGlareFade; float mGlare; // target
float mGlareFade; // actual
void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType); void ModVertexAlpha(Ogre::Entity* ent, unsigned int meshType);
bool mEnabled; bool mEnabled;
bool mGlareEnabled;
bool mSunEnabled; bool mSunEnabled;
bool mMasserEnabled; bool mMasserEnabled;
bool mSecundaEnabled; bool mSecundaEnabled;

View file

@ -705,13 +705,16 @@ namespace MWWorld
mWeatherManager->update (duration); mWeatherManager->update (duration);
// cast a ray from player to sun to detect if the sun is visible if (!mRendering->occlusionQuerySupported())
// this is temporary until we find a better place to put this code {
// currently its here because we need to access the physics system // cast a ray from player to sun to detect if the sun is visible
float* p = mPlayer->getPlayer().getRefData().getPosition().pos; // this is temporary until we find a better place to put this code
Vector3 sun = mRendering->getSkyManager()->getRealSunPos(); // currently its here because we need to access the physics system
sun = Vector3(sun.x, -sun.z, sun.y); float* p = mPlayer->getPlayer().getRefData().getPosition().pos;
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun)); Vector3 sun = mRendering->getSkyManager()->getRealSunPos();
sun = Vector3(sun.x, -sun.z, sun.y);
mRendering->getSkyManager()->setGlare(!mPhysics->castRay(Ogre::Vector3(p[0], p[1], p[2]), sun));
}
} }
bool World::isCellExterior() const bool World::isCellExterior() const