From 1d8d8335c5b27844f8007b61ef95f2ac8c01ef62 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 10:05:39 +0100 Subject: [PATCH] Moved shadow cascade distances computation to shadow, not shadow task --- .../src/RenderableZoneEntityItem.cpp | 4 +- libraries/render-utils/src/LightStage.cpp | 72 +++++++++++++++---- libraries/render-utils/src/LightStage.h | 19 +++-- .../render-utils/src/RenderShadowTask.cpp | 34 +-------- 4 files changed, 76 insertions(+), 53 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index d9a5382f16..04da70d733 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -27,6 +27,8 @@ // Sphere entities should fit inside a cube entity of the same size, so a sphere that has dimensions 1x1x1 // is a half unit sphere. However, the geometry cache renders a UNIT sphere, so we need to scale down. static const float SPHERE_ENTITY_SCALE = 0.5f; +static const unsigned int SUN_SHADOW_CASCADE_COUNT{ 4 }; +static const float SUN_SHADOW_MAX_DISTANCE{ 40.0f }; using namespace render; using namespace render::entities; @@ -116,7 +118,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { // Do we need to allocate the light in the stage ? if (LightStage::isIndexInvalid(_sunIndex)) { _sunIndex = _stage->addLight(_sunLight); - _shadowIndex = _stage->addShadow(_sunIndex, LightStage::SUN_SHADOW_CASCADE_COUNT); + _shadowIndex = _stage->addShadow(_sunIndex, SUN_SHADOW_MAX_DISTANCE, SUN_SHADOW_CASCADE_COUNT); } else { _stage->updateLightArrayBuffer(_sunIndex); } diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 59ad63890a..a0cb5b3f2a 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -23,7 +23,6 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.5, 0.5, 0.5, 1.0 }; const int LightStage::Shadow::MAP_SIZE = 1024; -const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 4 }; const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { @@ -40,7 +39,10 @@ LightStage::Shadow::Schema::Schema() { maxDistance = 20.0f; } -LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared() } { +LightStage::Shadow::Cascade::Cascade() : + _frustum{ std::make_shared() }, + _minDistance{ 0.0f }, + _maxDistance{ 20.0f } { framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE)); map = framebuffer->getDepthStencilBuffer(); } @@ -88,21 +90,66 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru return far; } -LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } { +LightStage::Shadow::Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount) : + _light{ light } { cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); Schema schema; schema.cascadeCount = cascadeCount; _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); _cascades.resize(cascadeCount); + + setMaxDistance(maxDistance); +} + +void LightStage::Shadow::setMaxDistance(float value) { + _maxDistance = std::max(0.0f, value); + + // 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_CASCADE_MAX_DISTANCE = 1.5f; + // Power distribution. Lower the steepness to 1.0 for a more linear distribution or increase it for a + // tighter distribution around the view position. + static const auto CASCADE_DISTRIBUTION_STEEPNESS = 3.0f; + + if (_cascades.size() == 1) { + _cascades.front().setMinDistance(0.0f); + _cascades.front().setMaxDistance(_maxDistance); + } else { + for (auto cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { + float maxCascadeDistance; + float minCascadeDistance; + float shadowOverlapDistance; + + const auto deltaCascadeMaxDistance = (_maxDistance - LOW_CASCADE_MAX_DISTANCE); + const auto maxAlpha = powf(cascadeIndex / float(_cascades.size() - 1), CASCADE_DISTRIBUTION_STEEPNESS); + const auto minAlpha = powf(std::max(cascadeIndex - 1, 0) / float(_cascades.size() - 1), CASCADE_DISTRIBUTION_STEEPNESS); + + maxCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * maxAlpha; + minCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * minAlpha; + + if (cascadeIndex == 0) { + minCascadeDistance = 0.0f; + } else { + minCascadeDistance = std::max(minCascadeDistance, 0.0f); + } + shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; + maxCascadeDistance += shadowOverlapDistance; + + _cascades[cascadeIndex].setMinDistance(minCascadeDistance); + _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance); + } + } + + // Update the buffer + const auto& lastCascade = _cascades.back(); + auto& schema = _schemaBuffer.edit(); + schema.maxDistance = _maxDistance; + schema.invFalloffDistance = 3.0f / (lastCascade.getMaxDistance() - lastCascade.getMinDistance()); } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance, - float viewCascadeOverlapDistance, float viewMaxShadowDistance, float nearDepth, float farDepth) { - assert(viewMinCascadeShadowDistance < viewMaxCascadeShadowDistance); assert(nearDepth < farDepth); - assert(viewCascadeOverlapDistance > 0.0f); assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum @@ -119,6 +166,9 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie } auto& cascade = _cascades[cascadeIndex]; + const auto viewMinCascadeShadowDistance = std::max(viewFrustum.getNearClip(), cascade.getMinDistance()); + const auto viewMaxCascadeShadowDistance = std::min(viewFrustum.getFarClip(), cascade.getMaxDistance()); + const auto viewMaxShadowDistance = _cascades.back().getMaxDistance(); cascade._frustum->setOrientation(orientation); @@ -165,10 +215,6 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Update the buffer auto& schema = _schemaBuffer.edit(); - if (cascadeIndex == getCascadeCount() - 1) { - schema.maxDistance = viewMaxCascadeShadowDistance; - schema.invFalloffDistance = 1.0f / viewCascadeOverlapDistance; - } schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); } @@ -218,12 +264,12 @@ LightStage::Index LightStage::addLight(const LightPointer& light) { } } -LightStage::Index LightStage::addShadow(Index lightIndex, unsigned int cascadeCount) { +LightStage::Index LightStage::addShadow(Index lightIndex, float maxDistance, unsigned int cascadeCount) { auto light = getLight(lightIndex); Index shadowId = INVALID_INDEX; if (light) { assert(_descs[lightIndex].shadowId == INVALID_INDEX); - shadowId = _shadows.newElement(std::make_shared(light, cascadeCount)); + shadowId = _shadows.newElement(std::make_shared(light, maxDistance, cascadeCount)); _descs[lightIndex].shadowId = shadowId; } return shadowId; diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 7cf0961e3a..ed9d330934 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -31,8 +31,6 @@ public: static std::string _stageName; static const std::string& getName() { return _stageName; } - static const unsigned int SUN_SHADOW_CASCADE_COUNT; - using Index = render::indexed_container::Index; static const Index INVALID_INDEX; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } @@ -62,19 +60,24 @@ public: const glm::mat4& getView() const; const glm::mat4& getProjection() const; + void setMinDistance(float value) { _minDistance = value; } + void setMaxDistance(float value) { _maxDistance = value; } + float getMinDistance() const { return _minDistance; } + float getMaxDistance() const { return _maxDistance; } + private: std::shared_ptr _frustum; + float _minDistance; + float _maxDistance; float computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse, float left, float right, float bottom, float top, float viewMaxShadowDistance) const; }; - Shadow(model::LightPointer light, unsigned int cascadeCount = 1); + Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount = 1); void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance, - float viewCascadeOverlapDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); @@ -83,6 +86,9 @@ public: unsigned int getCascadeCount() const { return (unsigned int)_cascades.size(); } const Cascade& getCascade(unsigned int index) const { return _cascades[index]; } + float getMaxDistance() const { return _maxDistance; } + void setMaxDistance(float value); + const model::LightPointer& getLight() const { return _light; } protected: @@ -94,6 +100,7 @@ public: static const glm::mat4 _biasMatrix; model::LightPointer _light; + float _maxDistance; Cascades _cascades; class Schema : public ShadowParameters { @@ -111,7 +118,7 @@ public: Index findLight(const LightPointer& light) const; Index addLight(const LightPointer& light); - Index addShadow(Index lightIndex, unsigned int cascadeCount = 1U); + Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U); LightPointer removeLight(Index index); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 24757496c8..a0d691b10e 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -237,39 +237,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O if (globalShadow && _cascadeIndexgetCascadeCount()) { output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - const auto nearClip = args->getViewFrustum().getNearClip(); - const auto farClip = args->getViewFrustum().getFarClip(); - - // TODO : these parameters should be exposed to the user as part of the light entity parameters, no? - static const auto HIGH_CASCADE_MAX_DISTANCE = 40.0f; - static const auto LOW_CASCADE_MAX_DISTANCE = 1.5f; - // Power distribution. Lower the steepness to 1.0 for a more linear distribution or increase it for a - // tighter distribution around the view position. - static const auto CASCADE_DISTRIBUTION_STEEPNESS = 3.0f; - - float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE; - float minCascadeDistance = nearClip; - float shadowOverlapDistance = 0.0f; - - if (globalShadow->getCascadeCount() > 1) { - const auto deltaCascadeMaxDistance = (HIGH_CASCADE_MAX_DISTANCE - LOW_CASCADE_MAX_DISTANCE); - const auto maxAlpha = powf(_cascadeIndex / float(globalShadow->getCascadeCount() - 1), CASCADE_DISTRIBUTION_STEEPNESS); - const auto minAlpha = powf(std::max(_cascadeIndex-1, 0) / float(globalShadow->getCascadeCount() - 1), CASCADE_DISTRIBUTION_STEEPNESS); - - maxCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * maxAlpha; - minCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * minAlpha; - } - - if (_cascadeIndex == 0) { - minCascadeDistance = nearClip; - } else { - minCascadeDistance = std::max(minCascadeDistance, nearClip); - } - shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; - maxCascadeDistance += shadowOverlapDistance; - maxCascadeDistance = std::min(maxCascadeDistance, farClip); - globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, - shadowOverlapDistance, HIGH_CASCADE_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));