From 3cb9c518eae445a44c9b5e49f6bd3e7cba38753a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 18 Apr 2019 11:06:12 +0200 Subject: [PATCH] Exposed max shadow distance and a shadow bias scale parameter in graphics::Light --- libraries/graphics/src/graphics/Light.cpp | 16 +++ libraries/graphics/src/graphics/Light.h | 11 +- libraries/render-utils/src/LightStage.cpp | 105 ++++++++++-------- libraries/render-utils/src/LightStage.h | 6 +- .../render-utils/src/RenderShadowTask.cpp | 8 +- 5 files changed, 87 insertions(+), 59 deletions(-) diff --git a/libraries/graphics/src/graphics/Light.cpp b/libraries/graphics/src/graphics/Light.cpp index 76d8a6030a..8a7281880e 100755 --- a/libraries/graphics/src/graphics/Light.cpp +++ b/libraries/graphics/src/graphics/Light.cpp @@ -73,6 +73,22 @@ bool Light::getCastShadows() const { return _castShadows; } +void Light::setShadowsMaxDistance(const float maxDistance) { + _shadowsMaxDistance = std::max(0.0f, maxDistance); +} + +float Light::getShadowsMaxDistance() const { + return _shadowsMaxDistance; +} + +void Light::setShadowsBiasScale(const float scale) { + _shadowsBiasScale = std::max(0.0f, scale); +} + +float Light::getShadowsBiasScale() const { + return _shadowsBiasScale; +} + void Light::setColor(const Color& color) { _lightSchemaBuffer.edit().irradiance.color = color; updateLightRadius(); diff --git a/libraries/graphics/src/graphics/Light.h b/libraries/graphics/src/graphics/Light.h index bb9fb3e5b9..824a9138c0 100755 --- a/libraries/graphics/src/graphics/Light.h +++ b/libraries/graphics/src/graphics/Light.h @@ -106,6 +106,12 @@ public: void setCastShadows(const bool castShadows); bool getCastShadows() const; + void setShadowsMaxDistance(const float maxDistance); + float getShadowsMaxDistance() const; + + void setShadowsBiasScale(const float scale); + float getShadowsBiasScale() const; + void setOrientation(const Quat& orientation); const glm::quat& getOrientation() const { return _transform.getRotation(); } @@ -192,10 +198,11 @@ protected: Type _type { SUN }; float _spotCos { -1.0f }; // stored here to be able to reset the spot angle when turning the type spot on/off - void updateLightRadius(); - + float _shadowsMaxDistance{ 40.0f }; + float _shadowsBiasScale{ 1.0f }; bool _castShadows{ false }; + void updateLightRadius(); }; typedef std::shared_ptr< Light > LightPointer; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 6913949286..d35bc21526 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -120,7 +120,7 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru return far; } -LightStage::Shadow::Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount) : +LightStage::Shadow::Shadow(graphics::LightPointer light, unsigned int cascadeCount) : _light{ light } { cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); Schema schema; @@ -149,70 +149,77 @@ LightStage::Shadow::Shadow(graphics::LightPointer light, float maxDistance, unsi cascade.framebuffer->setDepthBuffer(map, depthFormat, cascadeIndex); } - setMaxDistance(maxDistance); + if (light) { + setMaxDistance(light->getShadowsMaxDistance()); + } } void LightStage::Shadow::setLight(graphics::LightPointer light) { _light = light; + if (light) { + setMaxDistance(light->getShadowsMaxDistance()); + } } - void LightStage::Shadow::setMaxDistance(float value) { - // This overlaping factor isn't really used directly for blending of shadow cascades. It - // just there to be sure the cascades do overlap. The blending width used is relative - // to the UV space and is set in the Schema with invCascadeBlendWidth. - static const auto OVERLAP_FACTOR = 1.0f / 5.0f; + value = std::max(1e-3f, value); + if (value != _maxDistance) { + // This overlaping factor isn't really used directly for blending of shadow cascades. It's + // just there to be sure the cascades do overlap. The blending width used is relative + // to the UV space and is set in the Schema with invCascadeBlendWidth. + static const auto OVERLAP_FACTOR = 1.0f / 5.0f; - _maxDistance = std::max(0.0f, value); + _maxDistance = value; - if (_cascades.size() == 1) { - _cascades.front().setMinDistance(0.0f); - _cascades.front().setMaxDistance(_maxDistance); - } else { - // Distribute the cascades along that distance - // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? - static const auto LOW_MAX_DISTANCE = 2.0f; - static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions + if (_cascades.size() == 1) { + _cascades.front().setMinDistance(0.0f); + _cascades.front().setMaxDistance(_maxDistance); + } else { + // Distribute the cascades along that distance + // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? + static const auto LOW_MAX_DISTANCE = 2.0f; + static const auto MAX_RESOLUTION_LOSS = 0.6f; // Between 0 and 1, 0 giving tighter distributions - // The max cascade distance is computed by multiplying the previous cascade's max distance by a certain - // factor. There is a "user" factor that is computed from a desired max resolution loss in the shadow - // and an optimal one based on the global min and max shadow distance, all cascades considered. The final - // distance is a gradual blend between the two - const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS); - const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1)); + // The max cascade distance is computed by multiplying the previous cascade's max distance by a certain + // factor. There is a "user" factor that is computed from a desired max resolution loss in the shadow + // and an optimal one based on the global min and max shadow distance, all cascades considered. The final + // distance is a gradual blend between the two + const auto userDistanceScale = 1.0f / (1.0f - MAX_RESOLUTION_LOSS); + const auto optimalDistanceScale = powf(_maxDistance / LOW_MAX_DISTANCE, 1.0f / (_cascades.size() - 1)); - float maxCascadeUserDistance = LOW_MAX_DISTANCE; - float maxCascadeOptimalDistance = LOW_MAX_DISTANCE; - float minCascadeDistance = 0.0f; + float maxCascadeUserDistance = LOW_MAX_DISTANCE; + float maxCascadeOptimalDistance = LOW_MAX_DISTANCE; + float minCascadeDistance = 0.0f; - for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { - float blendFactor = cascadeIndex / float(_cascades.size() - 1); - float maxCascadeDistance; + for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { + float blendFactor = cascadeIndex / float(_cascades.size() - 1); + float maxCascadeDistance; - if (cascadeIndex == size_t(_cascades.size() - 1)) { - maxCascadeDistance = _maxDistance; - } else { - maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor; + if (cascadeIndex == size_t(_cascades.size() - 1)) { + maxCascadeDistance = _maxDistance; + } else { + maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor; + } + + float shadowOverlapDistance = maxCascadeDistance * OVERLAP_FACTOR; + + _cascades[cascadeIndex].setMinDistance(minCascadeDistance); + _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance); + + // Compute distances for next cascade + minCascadeDistance = maxCascadeDistance; + maxCascadeUserDistance = maxCascadeUserDistance * userDistanceScale; + maxCascadeOptimalDistance = maxCascadeOptimalDistance * optimalDistanceScale; + maxCascadeUserDistance = std::min(maxCascadeUserDistance, _maxDistance); } - - float shadowOverlapDistance = maxCascadeDistance * OVERLAP_FACTOR; - - _cascades[cascadeIndex].setMinDistance(minCascadeDistance); - _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance); - - // Compute distances for next cascade - minCascadeDistance = maxCascadeDistance; - maxCascadeUserDistance = maxCascadeUserDistance * userDistanceScale; - maxCascadeOptimalDistance = maxCascadeOptimalDistance * optimalDistanceScale; - maxCascadeUserDistance = std::min(maxCascadeUserDistance, _maxDistance); } - } - // Update the buffer - const auto& lastCascade = _cascades.back(); - auto& schema = _schemaBuffer.edit(); - schema.maxDistance = _maxDistance; - schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance()); + // Update the buffer + const auto& lastCascade = _cascades.back(); + auto& schema = _schemaBuffer.edit(); + schema.maxDistance = _maxDistance; + schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance()); + } } void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 1fb1754862..4da66843cc 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -74,7 +74,7 @@ public: float left, float right, float bottom, float top, float viewMaxShadowDistance) const; }; - Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount = 1); + Shadow(graphics::LightPointer light, unsigned int cascadeCount = 1); void setLight(graphics::LightPointer light); @@ -104,16 +104,14 @@ public: }; protected: - using Cascades = std::vector; static const glm::mat4 _biasMatrix; graphics::LightPointer _light; - float _maxDistance; + float _maxDistance{ 0.0f }; Cascades _cascades; - UniformBufferView _schemaBuffer = nullptr; }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index bf564c1c10..5de89a11b5 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -34,7 +34,6 @@ #define SHADOW_FRUSTUM_NEAR 1.0f #define SHADOW_FRUSTUM_FAR 500.0f static const unsigned int SHADOW_CASCADE_COUNT{ 4 }; -static const float SHADOW_MAX_DISTANCE{ 40.0f }; using namespace render; @@ -367,7 +366,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, c output.edit2() = _cameraFrustum; if (!_globalShadowObject) { - _globalShadowObject = std::make_shared(graphics::LightPointer(), SHADOW_MAX_DISTANCE, SHADOW_CASCADE_COUNT); + _globalShadowObject = std::make_shared(currentKeyLight, SHADOW_CASCADE_COUNT); } _globalShadowObject->setLight(currentKeyLight); @@ -378,11 +377,12 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, c unsigned int cascadeIndex; // Adjust each cascade frustum + const auto biasScale = currentKeyLight->getShadowsBiasScale(); for (cascadeIndex = 0; cascadeIndex < _globalShadowObject->getCascadeCount(); ++cascadeIndex) { auto& bias = _bias[cascadeIndex]; _globalShadowObject->setKeylightCascadeFrustum(cascadeIndex, args->getViewFrustum(), - SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, - bias._constant, bias._slope); + SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, + bias._constant, bias._slope * biasScale); } _shadowFrameCache->pushShadow(_globalShadowObject);