diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index c991e79f4..d2d6bd4bc 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -45,6 +45,7 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings: connect(standardRadioButton, SIGNAL(toggled(bool)), this, SLOT(slotStandardToggled(bool))); connect(screenComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(screenChanged(int))); connect(framerateLimitCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotFramerateLimitToggled(bool))); + connect(shadowDistanceCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotShadowDistLimitToggled(bool))); } @@ -129,6 +130,33 @@ bool Launcher::GraphicsPage::loadSettings() framerateLimitSpinBox->setValue(fpsLimit); } + if (mEngineSettings.getBool("actor shadows", "Shadows")) + actorShadowsCheckBox->setCheckState(Qt::Checked); + if (mEngineSettings.getBool("player shadows", "Shadows")) + playerShadowsCheckBox->setCheckState(Qt::Checked); + if (mEngineSettings.getBool("terrain shadows", "Shadows")) + objectShadowsCheckBox->setCheckState(Qt::Checked); + if (mEngineSettings.getBool("object shadows", "Shadows")) + objectShadowsCheckBox->setCheckState(Qt::Checked); + if (mEngineSettings.getBool("enable indoor shadows", "Shadows")) + indoorShadowsCheckBox->setCheckState(Qt::Checked); + + int shadowDistLimit = mEngineSettings.getInt("maximum shadow map distance", "Shadows"); + if (shadowDistLimit > 0) + { + shadowDistanceCheckBox->setCheckState(Qt::Checked); + shadowDistanceSpinBox->setValue(shadowDistLimit); + } + + float shadowFadeStart = mEngineSettings.getFloat("shadow fade start", "Shadows"); + if (shadowFadeStart != 0) + fadeStartSpinBox->setValue(shadowFadeStart); + + int shadowRes = mEngineSettings.getInt("shadow map resolution", "Shadows"); + int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes)); + if (shadowResIndex != -1) + shadowResolutionComboBox->setCurrentIndex(shadowResIndex); + return true; } @@ -185,6 +213,52 @@ void Launcher::GraphicsPage::saveSettings() { mEngineSettings.setFloat("framerate limit", "Video", 0); } + + int cShadowDist = shadowDistanceCheckBox->checkState() ? shadowDistanceSpinBox->value() : 0; + if (mEngineSettings.getInt("maximum shadow map distance", "Shadows") != cShadowDist) + mEngineSettings.setInt("maximum shadow map distance", "Shadows", cShadowDist); + float cFadeStart = fadeStartSpinBox->value(); + if (cShadowDist > 0 && mEngineSettings.getFloat("shadow fade start", "Shadows") != cFadeStart) + mEngineSettings.setFloat("shadow fade start", "Shadows", cFadeStart); + + bool cActorShadows = actorShadowsCheckBox->checkState(); + bool cObjectShadows = objectShadowsCheckBox->checkState(); + bool cTerrainShadows = terrainShadowsCheckBox->checkState(); + bool cPlayerShadows = playerShadowsCheckBox->checkState(); + if (cActorShadows || cObjectShadows || cTerrainShadows || cPlayerShadows) + { + if (mEngineSettings.getBool("enable shadows", "Shadows") != true) + mEngineSettings.setBool("enable shadows", "Shadows", true); + if (mEngineSettings.getBool("actor shadows", "Shadows") != cActorShadows) + mEngineSettings.setBool("actor shadows", "Shadows", cActorShadows); + if (mEngineSettings.getBool("player shadows", "Shadows") != cPlayerShadows) + mEngineSettings.setBool("player shadows", "Shadows", cPlayerShadows); + if (mEngineSettings.getBool("object shadows", "Shadows") != cObjectShadows) + mEngineSettings.setBool("object shadows", "Shadows", cObjectShadows); + if (mEngineSettings.getBool("terrain shadows", "Shadows") != cTerrainShadows) + mEngineSettings.setBool("terrain shadows", "Shadows", cTerrainShadows); + } + else + { + if (mEngineSettings.getBool("enable shadows", "Shadows")) + mEngineSettings.setBool("enable shadows", "Shadows", false); + if (mEngineSettings.getBool("actor shadows", "Shadows")) + mEngineSettings.setBool("actor shadows", "Shadows", false); + if (mEngineSettings.getBool("player shadows", "Shadows")) + mEngineSettings.setBool("player shadows", "Shadows", false); + if (mEngineSettings.getBool("object shadows", "Shadows")) + mEngineSettings.setBool("object shadows", "Shadows", false); + if (mEngineSettings.getBool("terrain shadows", "Shadows")) + mEngineSettings.setBool("terrain shadows", "Shadows", false); + } + + bool cIndoorShadows = indoorShadowsCheckBox->checkState(); + if (mEngineSettings.getBool("enable indoor shadows", "Shadows") != cIndoorShadows) + mEngineSettings.setBool("enable indoor shadows", "Shadows", cIndoorShadows); + + int cShadowRes = shadowResolutionComboBox->currentText().toInt(); + if (cShadowRes != mEngineSettings.getInt("shadow map resolution", "Shadows")) + mEngineSettings.setInt("shadow map resolution", "Shadows", cShadowRes); } QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) @@ -290,3 +364,9 @@ void Launcher::GraphicsPage::slotFramerateLimitToggled(bool checked) { framerateLimitSpinBox->setEnabled(checked); } + +void Launcher::GraphicsPage::slotShadowDistLimitToggled(bool checked) +{ + shadowDistanceSpinBox->setEnabled(checked); + fadeStartSpinBox->setEnabled(checked); +} diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index 05915e680..b230625fc 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -32,6 +32,7 @@ namespace Launcher void slotFullScreenChanged(int state); void slotStandardToggled(bool checked); void slotFramerateLimitToggled(bool checked); + void slotShadowDistLimitToggled(bool checked); private: Files::ConfigurationManager &mCfgMgr; diff --git a/components/sceneutil/mwshadowtechnique.cpp b/components/sceneutil/mwshadowtechnique.cpp index 92bf44ec1..f31d2faef 100644 --- a/components/sceneutil/mwshadowtechnique.cpp +++ b/components/sceneutil/mwshadowtechnique.cpp @@ -843,6 +843,11 @@ void SceneUtil::MWShadowTechnique::setPolygonOffset(float factor, float units) } } +void SceneUtil::MWShadowTechnique::setShadowFadeStart(float shadowFadeStart) +{ + _shadowFadeStart = shadowFadeStart; +} + void SceneUtil::MWShadowTechnique::enableFrontFaceCulling() { _useFrontFaceCulling = true; @@ -1493,6 +1498,9 @@ void MWShadowTechnique::createShaders() osg::ref_ptr baseTextureUnit = new osg::Uniform("baseTextureUnit",(int)_baseTextureUnit); _uniforms.push_back(baseTextureUnit.get()); + _uniforms.push_back(new osg::Uniform("maximumShadowMapDistance", (float)settings->getMaximumShadowMapDistance())); + _uniforms.push_back(new osg::Uniform("shadowFadeStart", (float)_shadowFadeStart)); + for(unsigned int sm_i=0; sm_igetNumShadowMapsPerLight(); ++sm_i) { { diff --git a/components/sceneutil/mwshadowtechnique.hpp b/components/sceneutil/mwshadowtechnique.hpp index 74c8661b9..1aa25585f 100644 --- a/components/sceneutil/mwshadowtechnique.hpp +++ b/components/sceneutil/mwshadowtechnique.hpp @@ -76,6 +76,8 @@ namespace SceneUtil { virtual void setPolygonOffset(float factor, float units); + virtual void setShadowFadeStart(float shadowFadeStart); + virtual void enableFrontFaceCulling(); virtual void disableFrontFaceCulling(); @@ -257,6 +259,8 @@ namespace SceneUtil { bool _useFrontFaceCulling = true; + float _shadowFadeStart = 0.0; + class DebugHUD : public osg::Referenced { public: diff --git a/components/sceneutil/shadow.cpp b/components/sceneutil/shadow.cpp index a69fd8090..08581ee5a 100644 --- a/components/sceneutil/shadow.cpp +++ b/components/sceneutil/shadow.cpp @@ -27,6 +27,14 @@ namespace SceneUtil mShadowSettings->setNumShadowMapsPerLight(numberOfShadowMapsPerLight); mShadowSettings->setBaseShadowTextureUnit(8 - numberOfShadowMapsPerLight); + const float maximumShadowMapDistance = Settings::Manager::getFloat("maximum shadow map distance", "Shadows"); + if (maximumShadowMapDistance > 0) + { + const float shadowFadeStart = std::min(std::max(0.f, Settings::Manager::getFloat("shadow fade start", "Shadows")), 1.f); + mShadowSettings->setMaximumShadowMapDistance(maximumShadowMapDistance); + mShadowTechnique->setShadowFadeStart(maximumShadowMapDistance * shadowFadeStart); + } + mShadowSettings->setMinimumShadowMapNearFarRatio(Settings::Manager::getFloat("minimum lispsm near far ratio", "Shadows")); if (Settings::Manager::getBool("compute tight scene bounds", "Shadows")) mShadowSettings->setComputeNearFarModeOverride(osg::CullSettings::COMPUTE_NEAR_FAR_USING_PRIMITIVES); @@ -117,6 +125,8 @@ namespace SceneUtil definesWithShadows["shadowNormalOffset"] = std::to_string(Settings::Manager::getFloat("normal offset distance", "Shadows")); + definesWithShadows["limitShadowMapDistance"] = Settings::Manager::getFloat("maximum shadow map distance", "Shadows") > 0 ? "1" : "0"; + return definesWithShadows; } @@ -138,6 +148,8 @@ namespace SceneUtil definesWithoutShadows["shadowNormalOffset"] = "0.0"; + definesWithoutShadows["limitShadowMapDistance"] = "0"; + return definesWithoutShadows; } diff --git a/docs/source/reference/modding/settings/shadows.rst b/docs/source/reference/modding/settings/shadows.rst index 8bcc0522b..bf160e944 100644 --- a/docs/source/reference/modding/settings/shadows.rst +++ b/docs/source/reference/modding/settings/shadows.rst @@ -16,7 +16,6 @@ Unlike in the original Morrowind engine, 'Shadow Mapping' is used, which can hav Bear in mind that this will force OpenMW to use shaders as if :ref:`force shaders` was enabled. A keen developer may be able to implement compatibility with fixed-function mode using the advice of `this post `_, but it may be more difficult than it seems. - number of shadow maps --------------------- @@ -28,6 +27,28 @@ Control how many shadow maps to use - more of these means each shadow map texel Using too many shadow maps will lead to them overriding texture slots used for other effects, producing unpleasant artefacts. A value of three is recommended in most cases, but other values may produce better results or performance. +maximum shadow map distance +--------------------------- + +:Type: float +:Range: The whole range of 32-bit floating point +:Default: 8192 + +The maximum distance from the camera shadows cover, limiting their overall area coverage +and improving their quality and performance at the cost of removing shadows of distant objects or terrain. +Set this to a non-positive value to remove the limit. + +shadow fade start +------------------- + +:Type: float +:Range: 0.0-1.0 +:Default: 0.9 + +The fraction of the maximum shadow map distance at which the shadows will begin to fade away. +Tweaking it will make the transition proportionally more or less smooth. +This setting has no effect if the maximum shadow map distance is non-positive (infinite). + allow shadow map overlap ------------------------ @@ -213,4 +234,4 @@ minimum lispsm near far ratio Controls the minimum near/far ratio for the Light Space Perspective Shadow Map transformation. Helps prevent too much detail being brought towards the camera at the expense of detail further from the camera. -Increasing this pushes detail further away by moving the frustum apex further from the near plane. \ No newline at end of file +Increasing this pushes detail further away by moving the frustum apex further from the near plane. diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 9695e4158..71d44fdbb 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -746,6 +746,12 @@ enable shadows = false # How many shadow maps to use - more of these means each shadow map texel covers less area, producing better looking shadows, but may decrease performance. number of shadow maps = 3 +# The distance from the camera at which shadows fade away completely. Set to 0 to make the distance infinite. +maximum shadow map distance = 8192 + +# Fraction of the maximum distance at which shadows begin to gradually fade away. +shadow fade start = 0.9 + # If true, allow shadow maps to overlap. Counter-intuitively, will produce better results when the light is behind the camera. When enabled, OpenMW uses Cascaded Shadow Maps and when disabled, it uses Parallel Split Shadow Maps. allow shadow map overlap = true diff --git a/files/shaders/objects_fragment.glsl b/files/shaders/objects_fragment.glsl index ad2837b88..eb605d421 100644 --- a/files/shaders/objects_fragment.glsl +++ b/files/shaders/objects_fragment.glsl @@ -114,7 +114,7 @@ void main() gl_FragData[0].xyz = mix(gl_FragData[0].xyz, decalTex.xyz, decalTex.a); #endif - float shadowing = unshadowedLightRatio(); + float shadowing = unshadowedLightRatio(depth); #if !PER_PIXEL_LIGHTING diff --git a/files/shaders/shadows_fragment.glsl b/files/shaders/shadows_fragment.glsl index a925b2f07..614370991 100644 --- a/files/shaders/shadows_fragment.glsl +++ b/files/shaders/shadows_fragment.glsl @@ -1,6 +1,8 @@ #define SHADOWS @shadows_enabled #if SHADOWS + uniform float maximumShadowMapDistance; + uniform float shadowFadeStart; @foreach shadow_texture_unit_index @shadow_texture_unit_list uniform sampler2DShadow shadowTexture@shadow_texture_unit_index; varying vec4 shadowSpaceCoords@shadow_texture_unit_index; @@ -11,10 +13,15 @@ @endforeach #endif // SHADOWS -float unshadowedLightRatio() +float unshadowedLightRatio(float distance) { float shadowing = 1.0; #if SHADOWS +#if @limitShadowMapDistance + float fade = clamp((distance - shadowFadeStart) / (maximumShadowMapDistance - shadowFadeStart), 0.0, 1.0); + if (fade == 1.0) + return shadowing; +#endif #if @shadowMapsOverlap bool doneShadows = false; @foreach shadow_texture_unit_index @shadow_texture_unit_list @@ -41,6 +48,9 @@ float unshadowedLightRatio() shadowing = min(shadow2DProj(shadowTexture@shadow_texture_unit_index, shadowSpaceCoords@shadow_texture_unit_index).r, shadowing); @endforeach #endif +#if @limitShadowMapDistance + shadowing = mix(shadowing, 1.0, fade); +#endif #endif // SHADOWS return shadowing; } diff --git a/files/shaders/terrain_fragment.glsl b/files/shaders/terrain_fragment.glsl index 10b268c9c..4910788bb 100644 --- a/files/shaders/terrain_fragment.glsl +++ b/files/shaders/terrain_fragment.glsl @@ -66,7 +66,7 @@ void main() gl_FragData[0].a *= texture2D(blendMap, blendMapUV).a; #endif - float shadowing = unshadowedLightRatio(); + float shadowing = unshadowedLightRatio(depth); #if !PER_PIXEL_LIGHTING diff --git a/files/shaders/water_fragment.glsl b/files/shaders/water_fragment.glsl index 2ad4ddeb6..808fab5ee 100644 --- a/files/shaders/water_fragment.glsl +++ b/files/shaders/water_fragment.glsl @@ -160,7 +160,7 @@ void main(void) vec2 UV = worldPos.xy / (8192.0*5.0) * 3.0; UV.y *= -1.0; - float shadow = unshadowedLightRatio(); + float shadow = unshadowedLightRatio(depthPassthrough); vec2 screenCoords = screenCoordsPassthrough.xy / screenCoordsPassthrough.z; screenCoords.y = (1.0-screenCoords.y); diff --git a/files/ui/graphicspage.ui b/files/ui/graphicspage.ui index eba913b91..18d220797 100644 --- a/files/ui/graphicspage.ui +++ b/files/ui/graphicspage.ui @@ -6,191 +6,353 @@ 0 0 - 437 - 343 + 650 + 340 - - - Display + + + 0 - - - - - Vertical Sync - - - - - - - Full Screen - - - - - - - Window Border - - - - - - - Anti-aliasing: - - - - - - - Screen: - - - - - - - Resolution: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - - - Framerate Limit: - - - - - - - - 0 - - - - - 2 - - - - - 4 - - - - - 8 - - - - - 16 - - - - - - - - - - - - - - - 800 - - - - - + + + Display + + + + + + + - x + Anti-aliasing: - - + + + + Framerate Limit: + + + + + + + + + + + 800 + + + + + + + x + + + + + + + 600 + + + + + + + + + Custom: + + + + + + + Standard: + + + true + + + + + + + + + + + + false + + + FPS + + + 1 + - 600 + 1 + + + 1000 + + + 15 + + + 300 + + + + + + + Window Border + + + + + + + Full Screen + + + + + + + Resolution: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + + Screen: + + + + + + + + 0 + + + + + 2 + + + + + 4 + + + + + 8 + + + + + 16 + + + + + + + + Vertical Sync - - - - - Custom: - - - - - - - Standard: - - - true - - - - - - - - - - - - false - - - FPS - - - 1 - - - 1 - - - 1000 - - - 15 - - - 300 - - - - + + + + + + + Shadows + + + + + + + 1 + 1 + + + + + + + <html><head/><body><p>The distance from the camera at which shadows completely disappear.</p></body></html> + + + Shadow Distance Limit: + + + + + + + false + + + <html><head/><body><p>64 game units is 1 real life yard or about 0.9 m</p></body></html> + + + unit(s) + + + 512 + + + 81920 + + + 8192 + + + + + + + <html><head/><body><p>The fraction of the limit above at which shadows begin to gradually fade away.</p></body></html> + + + Fade Start Multiplier: + + + + + + + + 512 + + + + + 1024 + + + + + 2048 + + + + + 4096 + + + + + + + + <html><head/><body><p>Enable shadows for NPCs and creatures besides the player character. May have a minor performance impact.</p></body></html> + + + Enable Actor Shadows + + + + + + + <html><head/><body><p>Enable shadows for the terrain including distant terrain. May have a significant performance and shadow quality impact.</p></body></html> + + + Enable Terrain Shadows + + + + + + + false + + + 2 + + + 0 + + + 1 + + + 0.90 + + + + + + + <html><head/><body><p>Enable shadows exclusively for the player character. May have a very minor performance impact.</p></body></html> + + + Enable Player Shadows + + + + + + + <html><head/><body><p>The resolution of each individual shadow map. Increasing it significantly improves shadow quality but may have a minor performance impact.</p></body></html> + + + Shadow Map Resolution: + + + + + + + <html><head/><body><p>Enable shadows for primarily inanimate objects. May have a significant performance impact.</p></body></html> + + + Enable Object Shadows + + + + + + + <html><head/><body><p>Due to limitations with Morrowind's data, only actors can cast shadows indoors, which some might feel is distracting.</p><p>Has no effect if actor/player shadows are not enabled.</p></body></html> + + + Enable Indoor Shadows + + + + + + + + - - - - Qt::Vertical - - - - 20 - 61 - - - -