Merge branch 'underwater_sun' into 'master'

Fix sun flash and glare rendering underwater (#8083)

See merge request OpenMW/openmw!4388
This commit is contained in:
Alexander Booher 2025-08-01 18:25:31 +00:00
commit c39d9490d6
6 changed files with 101 additions and 24 deletions

View File

@ -24,6 +24,7 @@ Programmers
Aleksandar Jovanov
Alex Haddad (rainChu)
Alex McKibben
Alexander Booher (Sleepy_wade)
alexanderkjall
Alexander Nadeau (wareya)
Alexander Olofsson (Ananace)

View File

@ -286,6 +286,7 @@
Bug #8048: Actors can generate negative collision extents and have no collision
Bug #8063: menu_background.bik video with audio freezes the game forever
Bug #8064: Lua move360 script doesn't respect the enableZoom/disableZoom Camera interface setting
Bug #8083: Sun glare visible when underwater
Bug #8085: Don't search in scripts or shaders directories for "Select directories you wish to add" menu in launcher
Bug #8097: GetEffect doesn't detect 0 magnitude spells
Bug #8099: Reaching Lua memory limit leads to a crash

View File

@ -543,6 +543,7 @@ namespace MWRender
return;
switchUnderwaterRain();
switchUnderwaterSun();
if (mIsStorm && mParticleNode)
{
@ -640,6 +641,14 @@ namespace MWRender
mRainParticleSystem->setFrozen(freeze);
}
void SkyManager::switchUnderwaterSun()
{
if (!mSun)
return;
bool underwater = mUnderwaterSwitch->isUnderwater();
mSun->setUnderwater(underwater);
}
void SkyManager::setWeather(const WeatherResult& weather)
{
if (!mCreated)

View File

@ -112,6 +112,7 @@ namespace MWRender
void createRain();
void destroyRain();
void switchUnderwaterRain();
void switchUnderwaterSun();
void updateRainParameters();
Resource::SceneManager* mSceneManager;

View File

@ -133,47 +133,87 @@ namespace MWRender
{
}
float OcclusionCallback::getVisibleRatio(osg::Camera* camera)
float OcclusionCallback::getVisibleRatio(osg::Camera* camera, bool smooth)
{
float visibleRatio = updateVisibleRatio(camera);
float dt = MWBase::Environment::get().getFrameDuration();
if (smooth)
{
float lastRatio = mLastRatio[osg::observer_ptr<osg::Camera>(camera)];
float change = dt * 5;
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;
}
OcclusionCallback::~OcclusionCallback() {}
float OcclusionCallback::updateVisibleRatio(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;
}
SunOcclusionCallback::SunOcclusionCallback(
osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal)
: OcclusionCallback(oqnVisible, oqnTotal)
, mUnderwater(false)
{
}
SunOcclusionCallback::~SunOcclusionCallback() {}
float SunOcclusionCallback::updateVisibleRatio(osg::Camera* camera)
{
if (mUnderwater)
{
return 0.0f;
}
return OcclusionCallback::updateVisibleRatio(camera);
}
void SunOcclusionCallback::setUnderwater(bool underwater)
{
mUnderwater = underwater;
}
bool SunOcclusionCallback::isUnderwater()
{
return mUnderwater;
}
/// SunFlashCallback handles fading/scaling of a node depending on occlusion query result. Must be attached as a
/// cull callback.
class SunFlashCallback : public OcclusionCallback,
class SunFlashCallback : public SunOcclusionCallback,
public SceneUtil::NodeCallback<SunFlashCallback, osg::Node*, osgUtil::CullVisitor*>
{
public:
SunFlashCallback(
osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal)
: OcclusionCallback(std::move(oqnVisible), std::move(oqnTotal))
: SunOcclusionCallback(std::move(oqnVisible), std::move(oqnTotal))
, mGlareView(1.f)
{
}
virtual ~SunFlashCallback() {}
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
{
float visibleRatio = getVisibleRatio(cv->getCurrentCamera());
float visibleRatio = getVisibleRatio(cv->getCurrentCamera(), !isUnderwater());
osg::ref_ptr<osg::StateSet> stateset;
@ -232,13 +272,13 @@ namespace MWRender
/// 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,
class SunGlareCallback : public SunOcclusionCallback,
public SceneUtil::NodeCallback<SunGlareCallback, osg::Node*, osgUtil::CullVisitor*>
{
public:
SunGlareCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible,
osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal, osg::ref_ptr<osg::PositionAttitudeTransform> sunTransform)
: OcclusionCallback(std::move(oqnVisible), std::move(oqnTotal))
: SunOcclusionCallback(std::move(oqnVisible), std::move(oqnTotal))
, mSunTransform(std::move(sunTransform))
, mTimeOfDayFade(1.f)
, mGlareView(1.f)
@ -255,10 +295,12 @@ namespace MWRender
mColor[i] = std::min(1.f, mColor[i]);
}
virtual ~SunGlareCallback() {}
void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
{
float angleRadians = getAngleToSunInRadians(*cv->getCurrentRenderStage()->getInitialViewMatrix());
float visibleRatio = getVisibleRatio(cv->getCurrentCamera());
float visibleRatio = getVisibleRatio(cv->getCurrentCamera(), !isUnderwater());
const float angleMaxRadians = osg::DegreesToRadians(mSunGlareFaderAngleMax);
@ -831,6 +873,14 @@ namespace MWRender
mSunFlashCallback->setGlareView(ratio);
}
void Sun::setUnderwater(bool underwater)
{
if (mSunFlashCallback)
mSunFlashCallback->setUnderwater(underwater);
if (mSunGlareCallback)
mSunGlareCallback->setUnderwater(underwater);
}
void Sun::setDirection(const osg::Vec3f& direction)
{
osg::Vec3f normalizedDirection = direction / direction.length();

View File

@ -113,17 +113,32 @@ namespace MWRender
public:
OcclusionCallback(
osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal);
float getVisibleRatio(osg::Camera* camera, bool smooth);
virtual ~OcclusionCallback();
protected:
float getVisibleRatio(osg::Camera* camera);
virtual float updateVisibleRatio(osg::Camera* camera);
private:
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryVisiblePixels;
osg::ref_ptr<osg::OcclusionQueryNode> mOcclusionQueryTotalPixels;
std::map<osg::observer_ptr<osg::Camera>, float> mLastRatio;
};
class SunOcclusionCallback : public OcclusionCallback
{
public:
SunOcclusionCallback(
osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal);
virtual ~SunOcclusionCallback();
virtual float updateVisibleRatio(osg::Camera* camera) override;
void setUnderwater(bool underwater);
bool isUnderwater();
private:
bool mUnderwater;
};
class AtmosphereUpdater : public SceneUtil::StateSetUpdater
{
public:
@ -249,7 +264,7 @@ namespace MWRender
void setColor(const osg::Vec4f& color);
void adjustTransparency(const float ratio) override;
void setUnderwater(bool underwater);
void setDirection(const osg::Vec3f& direction);
void setGlareTimeOfDayFade(float val);
void setSunglare(bool enabled);