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 Aleksandar Jovanov
Alex Haddad (rainChu) Alex Haddad (rainChu)
Alex McKibben Alex McKibben
Alexander Booher (Sleepy_wade)
alexanderkjall alexanderkjall
Alexander Nadeau (wareya) Alexander Nadeau (wareya)
Alexander Olofsson (Ananace) Alexander Olofsson (Ananace)

View File

@ -286,6 +286,7 @@
Bug #8048: Actors can generate negative collision extents and have no collision 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 #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 #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 #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 #8097: GetEffect doesn't detect 0 magnitude spells
Bug #8099: Reaching Lua memory limit leads to a crash Bug #8099: Reaching Lua memory limit leads to a crash

View File

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

View File

@ -112,6 +112,7 @@ namespace MWRender
void createRain(); void createRain();
void destroyRain(); void destroyRain();
void switchUnderwaterRain(); void switchUnderwaterRain();
void switchUnderwaterSun();
void updateRainParameters(); void updateRainParameters();
Resource::SceneManager* mSceneManager; 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 visible = mOcclusionQueryVisiblePixels->getQueryGeometry()->getNumPixels(camera);
int total = mOcclusionQueryTotalPixels->getQueryGeometry()->getNumPixels(camera); int total = mOcclusionQueryTotalPixels->getQueryGeometry()->getNumPixels(camera);
float visibleRatio = 0.f; float visibleRatio = 0.f;
if (total > 0) if (total > 0)
visibleRatio = static_cast<float>(visible) / static_cast<float>(total); 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; 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 /// SunFlashCallback handles fading/scaling of a node depending on occlusion query result. Must be attached as a
/// cull callback. /// cull callback.
class SunFlashCallback : public OcclusionCallback, class SunFlashCallback : public SunOcclusionCallback,
public SceneUtil::NodeCallback<SunFlashCallback, osg::Node*, osgUtil::CullVisitor*> public SceneUtil::NodeCallback<SunFlashCallback, osg::Node*, osgUtil::CullVisitor*>
{ {
public: public:
SunFlashCallback( SunFlashCallback(
osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal) 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) , mGlareView(1.f)
{ {
} }
virtual ~SunFlashCallback() {}
void operator()(osg::Node* node, osgUtil::CullVisitor* cv) 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; 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 /// 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. /// 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 SceneUtil::NodeCallback<SunGlareCallback, osg::Node*, osgUtil::CullVisitor*>
{ {
public: public:
SunGlareCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible, SunGlareCallback(osg::ref_ptr<osg::OcclusionQueryNode> oqnVisible,
osg::ref_ptr<osg::OcclusionQueryNode> oqnTotal, osg::ref_ptr<osg::PositionAttitudeTransform> sunTransform) 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)) , mSunTransform(std::move(sunTransform))
, mTimeOfDayFade(1.f) , mTimeOfDayFade(1.f)
, mGlareView(1.f) , mGlareView(1.f)
@ -255,10 +295,12 @@ namespace MWRender
mColor[i] = std::min(1.f, mColor[i]); mColor[i] = std::min(1.f, mColor[i]);
} }
virtual ~SunGlareCallback() {}
void operator()(osg::Node* node, osgUtil::CullVisitor* cv) void operator()(osg::Node* node, osgUtil::CullVisitor* cv)
{ {
float angleRadians = getAngleToSunInRadians(*cv->getCurrentRenderStage()->getInitialViewMatrix()); float angleRadians = getAngleToSunInRadians(*cv->getCurrentRenderStage()->getInitialViewMatrix());
float visibleRatio = getVisibleRatio(cv->getCurrentCamera()); float visibleRatio = getVisibleRatio(cv->getCurrentCamera(), !isUnderwater());
const float angleMaxRadians = osg::DegreesToRadians(mSunGlareFaderAngleMax); const float angleMaxRadians = osg::DegreesToRadians(mSunGlareFaderAngleMax);
@ -831,6 +873,14 @@ namespace MWRender
mSunFlashCallback->setGlareView(ratio); 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) void Sun::setDirection(const osg::Vec3f& direction)
{ {
osg::Vec3f normalizedDirection = direction / direction.length(); osg::Vec3f normalizedDirection = direction / direction.length();

View File

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