From b246c479e36d2d5ee4addbba037780d6242e9425 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 17 Nov 2017 11:25:15 +0100 Subject: [PATCH] Much better cascade blend --- .../render-utils/src/DebugDeferredBuffer.cpp | 20 +++--- libraries/render-utils/src/LightStage.cpp | 18 ++--- libraries/render-utils/src/LightStage.h | 2 - .../render-utils/src/RenderShadowTask.cpp | 7 +- libraries/render-utils/src/Shadow.slh | 21 +++--- libraries/render-utils/src/ShadowCore.slh | 68 ++++++++++--------- libraries/render-utils/src/Shadows_shared.slh | 13 ++-- 7 files changed, 74 insertions(+), 75 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 3902f4d9d4..e2d0838e27 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -151,21 +151,23 @@ static const std::string DEFAULT_SHADOW_SHADER{ }; static const std::string DEFAULT_SHADOW_CASCADE_SHADER{ - "vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(0,0,0) );" + "vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(1) );" "vec4 getFragmentColor() {" " DeferredFrameTransform deferredTransform = getDeferredFrameTransform();" " DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);" " vec4 viewPosition = vec4(frag.position.xyz, 1.0);" + " float viewDepth = -viewPosition.z;" " vec4 worldPosition = getViewInverse() * viewPosition;" - " vec4 cascadeShadowCoords[4] = vec4[4](" - " evalShadowTexcoord(0, worldPosition)," - " evalShadowTexcoord(1, worldPosition)," - " evalShadowTexcoord(2, worldPosition)," - " evalShadowTexcoord(3, worldPosition)" - " );" + " vec4 cascadeShadowCoords[2];" " ivec2 cascadeIndices;" - " float cascadeMix = evalCascadeIndicesAndMix(-viewPosition.z, cascadeShadowCoords, cascadeIndices);" - " return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);" + " float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);" + " vec3 firstCascadeColor = cascadeColors[cascadeIndices.x];" + " vec3 secondCascadeColor = cascadeColors[cascadeIndices.x];" + " if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {" + " secondCascadeColor = cascadeColors[cascadeIndices.y];" + " }" + " vec3 color = mix(firstCascadeColor, secondCascadeColor, cascadeMix);" + " return vec4(mix(vec3(0.0), color, evalShadowFalloff(viewDepth)), 1.0);" "}" }; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 2c4869a9f1..38bbc2ceb9 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -35,6 +35,9 @@ LightStage::Shadow::Schema::Schema() { std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); invMapSize = 1.0f / MAP_SIZE; cascadeCount = 1; + invCascadeBlendWidth = 1.0f / 0.1f; + invFalloffDistance = 1.0f / 2.0f; + maxDistance = 20.0f; } LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared() } { @@ -52,11 +55,10 @@ const glm::mat4& LightStage::Shadow::Cascade::getProjection() const { LightStage::Shadow::Shadow(model::LightPointer light, 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); - _schemaBuffer.edit().cascadeCount = cascadeCount; } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, @@ -125,12 +127,12 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie cascade._frustum->calculate(); // Update the buffer - auto& schemaCascade = _schemaBuffer.edit().cascades[cascadeIndex]; - schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); - schemaCascade.invTransitionWidth = 1.0f / viewOverlapDistance; - schemaCascade.maxDistance = viewMaxShadowDistance; - cascade.minDistance = viewMinShadowDistance; - cascade.maxDistance = viewMaxShadowDistance; + auto& schema = _schemaBuffer.edit(); + if (cascadeIndex == getCascadeCount() - 1) { + schema.maxDistance = viewMaxShadowDistance; + schema.invFalloffDistance = 1.0f / viewOverlapDistance; + } + schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); } void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 0b59ebdb7d..0a0a67f591 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -56,8 +56,6 @@ public: gpu::FramebufferPointer framebuffer; gpu::TexturePointer map; - float minDistance; - float maxDistance; const std::shared_ptr& getFrustum() const { return _frustum; } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index b9a7a99837..fef5cdddbc 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -258,7 +258,6 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O const auto nearClip = args->getViewFrustum().getNearClip(); const auto farClip = args->getViewFrustum().getFarClip(); - const auto nearDepth = -args->_boomOffset.z; static const float HIGH_CASCADE_MAX_DISTANCE = 20.0f; float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE; @@ -266,7 +265,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O float shadowOverlapDistance = 0.0f; if (globalShadow->getCascadeCount() > 1) { - static const float LOW_CASCADE_MAX_DISTANCE = 2.0f; + static const float LOW_CASCADE_MAX_DISTANCE = 3.0f; const float cascadeLevelScale = powf(HIGH_CASCADE_MAX_DISTANCE / LOW_CASCADE_MAX_DISTANCE, 1.0f / (globalShadow->getCascadeCount() - 1)); maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE / powf(cascadeLevelScale, globalShadow->getCascadeCount() - 1 - _cascadeIndex); @@ -276,9 +275,9 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; maxCascadeDistance += shadowOverlapDistance; if (_cascadeIndex == 0) { - minCascadeDistance = nearDepth; + minCascadeDistance = nearClip; } - minCascadeDistance = std::max(minCascadeDistance, nearDepth); + minCascadeDistance = std::max(minCascadeDistance, nearClip); maxCascadeDistance = std::min(maxCascadeDistance, farClip); globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 036f1029e6..c4b7828688 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -84,9 +84,7 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve } float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { - if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 || - shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 || - shadowTexcoord.z < 0.0 || shadowTexcoord.z > 1.0) { + if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) { // If a point is not in the map, do not attenuate return 1.0; } @@ -95,21 +93,18 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets float evalShadowAttenuation(vec4 worldPosition, float viewDepth) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); - vec4 cascadeShadowCoords[4] = vec4[4] ( - evalShadowTexcoord(0, worldPosition), - evalShadowTexcoord(1, worldPosition), - evalShadowTexcoord(2, worldPosition), - evalShadowTexcoord(3, worldPosition) - ); + vec4 cascadeShadowCoords[2]; ivec2 cascadeIndices; - float cascadeMix = evalCascadeIndicesAndMix(viewDepth, cascadeShadowCoords, cascadeIndices); + float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices); vec2 cascadeAttenuations = vec2(1.0, 1.0); - cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[cascadeIndices.x]); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0]); if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { - cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[cascadeIndices.y]); + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1]); } - return mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); + float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); + // Falloff to max distance + return mix(1.0, attenuation, evalShadowFalloff(viewDepth)); } <@endif@> diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index e548af6a79..8ad972316a 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -21,12 +21,12 @@ int getShadowCascadeCount() { return shadow.cascadeCount; } -float getShadowCascadeInvTransitionWidth(int cascadeIndex) { - return shadow.cascades[cascadeIndex].invTransitionWidth; +float getShadowCascadeInvBlendWidth() { + return shadow.invCascadeBlendWidth; } -float getShadowCascadeMaxDistance(int cascadeIndex) { - return shadow.cascades[cascadeIndex].maxDistance; +float evalShadowFalloff(float depth) { + return clamp((shadow.maxDistance-depth) * shadow.invFalloffDistance, 0.0, 1.0); } mat4 getShadowReprojection(int cascadeIndex) { @@ -48,44 +48,46 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { return vec4(shadowCoord.xy, shadowCoord.z - bias, 1.0); } -int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) { +bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { + bvec3 greaterThanZero = greaterThanEqual(cascadeTexCoords.xyz, vec3(0)); + bvec3 lessThanOne = lessThanEqual(cascadeTexCoords.xyz, vec3(1)); + return all(greaterThanZero) && all(lessThanOne); +} + +int getFirstShadowCascadeOnPixel(int startCascadeIndex, vec4 worldPosition, out vec4 cascadeShadowCoords) { int cascadeIndex; - for (cascadeIndex=0 ; cascadeIndex diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index 7230ba6134..2797f4e962 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -13,17 +13,18 @@ struct ShadowTransform { MAT4 reprojection; float bias; - float maxDistance; - float invTransitionWidth; - float _padding; + float _padding1; + float _padding2; + float _padding3; }; struct ShadowParameters { + ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT]; int cascadeCount; float invMapSize; - float _padding1; - float _padding2; - ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT]; + float invCascadeBlendWidth; + float maxDistance; + float invFalloffDistance; }; // <@if 1@>