Add the full-screen sun glare effect

sceneinput
scrawl 9 years ago
parent ac5d0bf405
commit a2a4532e71

@ -14,6 +14,7 @@
#include <osg/OcclusionQueryNode>
#include <osg/ColorMask>
#include <osg/MatrixTransform>
#include <osg/BlendFunc>
#include <osgParticle/ParticleSystem>
#include <osgParticle/ParticleSystemUpdater>
@ -376,7 +377,7 @@ public:
// Slightly downscale the query geometry since the sun quad has a transparent texture that doesn't cover the whole area
osg::ref_ptr<osg::PositionAttitudeTransform> queryTransform (new osg::PositionAttitudeTransform);
queryTransform->setScale(osg::Vec3f(0.5f, 0.5f, 0.5f));
queryTransform->setScale(osg::Vec3f(0.4f, 0.4f, 0.4f));
// Need to render after the world geometry so we can correctly test for occlusions
queryTransform->getOrCreateStateSet()->setRenderBinDetails(RenderBin_OcclusionQuery, "RenderBin");
queryTransform->getOrCreateStateSet()->setNestRenderBins(false);
@ -386,17 +387,26 @@ public:
mOcclusionQueryVisiblePixels = createOcclusionQueryNode(queryTransform, true);
mOcclusionQueryTotalPixels = createOcclusionQueryNode(queryTransform, false);
createSunFlash(textureManager);
osg::PositionAttitudeTransform* sunFlashNode = createSunFlash(textureManager);
mSunFlashCallback = new SunFlashCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels, sunFlashNode, mTransform);
mTransform->addCullCallback(mSunFlashCallback);
createSunGlare();
}
~Sun()
{
mTransform->removeUpdateCallback(mUpdater);
destroySunFlash();
destroySunGlare();
}
virtual void adjustTransparency(const float ratio)
{
mUpdater->mColor.a() = ratio;
if (mSunGlareCallback)
mSunGlareCallback->setGlareView(ratio);
}
void setDirection(const osg::Vec3f& direction)
@ -409,6 +419,12 @@ public:
mTransform->setAttitude(quat);
}
void setGlareTimeOfDayFade(float val)
{
if (mSunGlareCallback)
mSunGlareCallback->setTimeOfDayFade(val);
}
private:
/// @param queryVisible If true, queries the amount of visible pixels. If false, queries the total amount of pixels.
osg::ref_ptr<osg::OcclusionQueryNode> createOcclusionQueryNode(osg::Group* parent, bool queryVisible)
@ -450,7 +466,7 @@ private:
return oqn;
}
void createSunFlash(Resource::TextureManager& textureManager)
osg::PositionAttitudeTransform* createSunFlash(Resource::TextureManager& textureManager)
{
osg::ref_ptr<osg::Texture2D> tex = textureManager.getTexture2D("textures/tx_sun_flash_grey_05.dds",
osg::Texture::CLAMP,
@ -474,12 +490,58 @@ private:
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin");
stateset->setNestRenderBins(false);
return transform;
}
void destroySunFlash()
{
mTransform->removeCullCallback(mSunFlashCallback);
mSunFlashCallback = NULL;
}
// TODO: change size depending on occlusion
void createSunGlare()
{
osg::ref_ptr<osg::Camera> camera (new osg::Camera);
camera->setProjectionMatrix(osg::Matrix::identity());
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF); // add to skyRoot instead?
camera->setViewMatrix(osg::Matrix::identity());
camera->setClearMask(0);
camera->setRenderOrder(osg::Camera::NESTED_RENDER);
camera->setAllowEventFocus(false);
osg::ref_ptr<osg::Geode> geode (new osg::Geode);
osg::ref_ptr<osg::Geometry> geom = osg::createTexturedQuadGeometry(osg::Vec3f(-1,-1,0), osg::Vec3f(2,0,0), osg::Vec3f(0,2,0));
geode->addDrawable(geom);
camera->addChild(geode);
osg::StateSet* stateset = geom->getOrCreateStateSet();
stateset->setRenderBinDetails(RenderBin_SunGlare, "RenderBin");
stateset->setNestRenderBins(false);
stateset->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
// set up additive blending
osg::ref_ptr<osg::BlendFunc> blendFunc (new osg::BlendFunc);
blendFunc->setSource(osg::BlendFunc::SRC_ALPHA);
blendFunc->setDestination(osg::BlendFunc::ONE);
stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);
mSunGlareCallback = new SunGlareCallback(mOcclusionQueryVisiblePixels, mOcclusionQueryTotalPixels, mTransform);
mSunGlareNode = camera;
mSunGlareNode->addCullCallback(mSunGlareCallback);
mTransform->addChild(camera);
}
void destroySunGlare()
{
mSunGlareNode->removeCullCallback(mSunGlareCallback);
mSunGlareCallback = NULL;
}
struct Updater : public SceneUtil::StateSetUpdater
class Updater : public SceneUtil::StateSetUpdater
{
public:
osg::Vec4f mColor;
Updater()
@ -489,8 +551,7 @@ private:
virtual void setDefaults(osg::StateSet* stateset)
{
stateset->setAttributeAndModes(createUnlitMaterial(),
osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE);
stateset->setAttributeAndModes(createUnlitMaterial(), osg::StateAttribute::ON);
}
virtual void apply(osg::StateSet* stateset, osg::NodeVisitor*)
@ -500,7 +561,181 @@ private:
}
};
class OcclusionCallback : public osg::NodeCallback
{
public:
OcclusionCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal)
: mOcclusionQueryVisiblePixels(oqnVisible)
, mOcclusionQueryTotalPixels(oqnTotal)
{
}
protected:
float getVisibleRatio (osg::Camera* camera)
{
int visible = mOcclusionQueryVisiblePixels->getQueryGeometry()->getNumPixels(camera);
int total = mOcclusionQueryTotalPixels->getQueryGeometry()->getNumPixels(camera);
float visibleRatio = 0.f;
if (total > 0)
visibleRatio = static_cast<float>(visible) / static_cast<float>(total);
float dt = MWBase::Environment::get().getFrameDuration();
float lastRatio = mLastRatio[osg::observer_ptr<osg::Camera>(camera)];
float change = dt*10;
if (visibleRatio > lastRatio)
visibleRatio = std::min(visibleRatio, lastRatio + change);
else
visibleRatio = std::max(visibleRatio, lastRatio - change);
mLastRatio[osg::observer_ptr<osg::Camera>(camera)] = visibleRatio;
return visibleRatio;
}
private:
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels;
std::map<osg::observer_ptr<osg::Camera>, float> mLastRatio;
};
/// SunFlashCallback handles fading/scaling of the sun flash depending on occlusion query result. Must be attached as a cull callback.
class SunFlashCallback : public OcclusionCallback
{
public:
SunFlashCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal,
osg::ref_ptr<osg::PositionAttitudeTransform> flashNode,
osg::ref_ptr<osg::PositionAttitudeTransform> sunTransform)
: OcclusionCallback(oqnVisible, oqnTotal)
, mFlashNode(flashNode)
{
mInitialScale = mFlashNode->getScale().x();
}
virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
float visibleRatio = getVisibleRatio(cv->getCurrentCamera());
handleOcclusionResult (visibleRatio, cv);
traverse(node, nv);
}
void handleOcclusionResult(float visibleRatio, osgUtil::CullVisitor* cv)
{
// TODO
}
private:
float mInitialScale;
osg::ref_ptr<osg::PositionAttitudeTransform> mFlashNode;
};
/// SunGlareCallback controls a full-screen glare effect depending on occlusion query result and the angle between sun and camera.
/// Must be attached as a cull callback to the node above the glare node.
class SunGlareCallback : public OcclusionCallback
{
public:
SunGlareCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal,
osg::ref_ptr<osg::PositionAttitudeTransform> sunTransform)
: OcclusionCallback(oqnVisible, oqnTotal)
, mSunTransform(sunTransform)
, mTimeOfDayFade(1.f)
, mGlareView(1.f)
{
}
virtual void operator ()(osg::Node* node, osg::NodeVisitor* nv)
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(nv);
float angleRadians = getAngleToSunInRadians(cv->getCurrentCamera());
float visibleRatio = getVisibleRatio(cv->getCurrentCamera());
const float angleMaxRadians = osg::DegreesToRadians(30.f); // Sun Glare Fader Angle Max
float value = 1.f - std::min(1.f, angleRadians / angleMaxRadians);
const float sunGlareFaderMax = 0.5f;
float fade = value * sunGlareFaderMax;
fade *= mTimeOfDayFade * mGlareView * visibleRatio;
if (fade == 0.f)
{
// no traverse
return;
}
else
{
osg::ref_ptr<osg::StateSet> stateset (new osg::StateSet);
osg::ref_ptr<osg::Material> mat (createUnlitMaterial());
osg::Vec4f sunGlareFaderColor (222/255.f, 95/255.f, 39/255.f, 1);
// Replicating a design flaw in MW. The color was being set on both ambient and emissive properties, which multiplies the result by two,
// then finally gets clamped by the fixed function pipeline. With the default INI settings, only the red component gets clamped,
// so the resulting color looks more orange than red.
sunGlareFaderColor *= 2;
for (int i=0; i<3; ++i)
sunGlareFaderColor[i] = std::min(1.f, sunGlareFaderColor[i]);
mat->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,1));
mat->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,fade));
mat->setEmission(osg::Material::FRONT_AND_BACK, sunGlareFaderColor);
mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(0,0,0,0));
stateset->setAttributeAndModes(mat, osg::StateAttribute::ON);
cv->pushStateSet(stateset);
traverse(node, nv);
cv->popStateSet();
}
}
void setTimeOfDayFade(float val)
{
mTimeOfDayFade = val;
}
void setGlareView(float glareView)
{
mGlareView = glareView;
}
private:
float getAngleToSunInRadians(osg::Camera* camera) const
{
osg::Vec3d eye, center, up;
camera->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d forward = center - eye;
osg::Vec3d sun = mSunTransform->getPosition();
forward.normalize();
sun.normalize();
float angleRadians = std::acos(forward * sun);
return angleRadians;
}
osg::ref_ptr<osg::PositionAttitudeTransform> mSunTransform;
float mTimeOfDayFade;
float mGlareView;
};
osg::ref_ptr<Updater> mUpdater;
osg::ref_ptr<SunFlashCallback> mSunFlashCallback;
osg::ref_ptr<SunGlareCallback> mSunGlareCallback;
osg::ref_ptr<osg::Node> mSunGlareNode;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels;
float mInitialFlashScale;
@ -1223,11 +1458,6 @@ void SkyManager::setWeather(const WeatherResult& weather)
mParticleFader->setAlpha(weather.mEffectFade);
}
void SkyManager::setGlare(const float glare)
{
mGlare = glare;
}
void SkyManager::sunEnable()
{
if (!mCreated) return;
@ -1276,11 +1506,9 @@ void SkyManager::setDate(int day, int month)
mMonth = month;
}
void SkyManager::setGlareEnabled (bool enabled)
void SkyManager::setGlareTimeOfDayFade(float val)
{
if (!mCreated || !mEnabled)
return;
//mSunGlare->setVisible (mSunEnabled && enabled);
mSun->setGlareTimeOfDayFade(val);
}
}

@ -140,8 +140,7 @@ namespace MWRender
void setMasserState(const MoonState& state);
void setSecundaState(const MoonState& state);
void setGlare(const float glare);
void setGlareEnabled(bool enabled);
void setGlareTimeOfDayFade(float val);
private:
void create();

@ -624,6 +624,14 @@ void WeatherManager::update(float duration, bool paused)
mRendering.setSunDirection( final * -1 );
}
float peakHour = mSunriseTime + (mSunsetTime - mSunriseTime) / 2;
if (time.getHour() < mSunriseTime || time.getHour() > mSunsetTime)
mRendering.getSkyManager()->setGlareTimeOfDayFade(0);
else if (time.getHour() < peakHour)
mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (peakHour - time.getHour()) / (peakHour - mSunriseTime));
else
mRendering.getSkyManager()->setGlareTimeOfDayFade(1 - (time.getHour() - peakHour) / (mSunsetTime - peakHour));
mRendering.getSkyManager()->setMasserState(mMasser.calculateState(time));
mRendering.getSkyManager()->setSecundaState(mSecunda.calculateState(time));

Loading…
Cancel
Save