From 74b0b52edb6877e5d3c288a59d92b2c9741599a0 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 16:57:22 +0100 Subject: [PATCH] Working CSM with smooth blend between cascades. Switched to 4 1024 cascades --- .../render-utils/src/DebugDeferredBuffer.cpp | 23 +++++++++++------- .../render-utils/src/DebugDeferredBuffer.h | 3 ++- libraries/render-utils/src/LightStage.cpp | 7 +++--- libraries/render-utils/src/LightStage.h | 4 +++- .../render-utils/src/RenderDeferredTask.cpp | 2 +- .../render-utils/src/RenderShadowTask.cpp | 15 +++++++----- libraries/render-utils/src/Shadow.slh | 14 +++++++---- libraries/render-utils/src/ShadowCore.slh | 24 ++++++++++++------- libraries/render-utils/src/Shadows_shared.slh | 2 +- .../src/directional_ambient_light_shadow.slf | 2 +- .../src/directional_skybox_light_shadow.slf | 2 +- 11 files changed, 62 insertions(+), 36 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 55c945549c..7d316d6e56 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -38,7 +38,7 @@ void DebugDeferredBufferConfig::setMode(int newMode) { emit dirty(); } -enum Slot { +enum TextureSlot { Albedo = 0, Normal, Specular, @@ -55,7 +55,11 @@ enum Slot { AmbientOcclusionBlurred }; - +enum ParamSlot { + CameraCorrection = 0, + DeferredFrameTransform, + ShadowTransform +}; static const std::string DEFAULT_ALBEDO_SHADER { "vec4 getFragmentColor() {" @@ -147,7 +151,7 @@ static const std::string DEFAULT_SHADOW_SHADER{ }; static const std::string DEFAULT_SHADOW_CASCADE_SHADER{ - "vec3 cascadeColors[4] = vec3[4]( vec3(1,0,0), vec3(0,1,0), vec3(0,0,1), vec3(0,0,0) );" + "vec3 cascadeColors[4] = vec3[4]( vec3(0,1,0), vec3(0,0,1), vec3(1,0,0), vec3(0,0,0) );" "vec4 getFragmentColor() {" " DeferredFrameTransform deferredTransform = getDeferredFrameTransform();" " DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);" @@ -160,7 +164,7 @@ static const std::string DEFAULT_SHADOW_CASCADE_SHADER{ " evalShadowTexcoord(3, worldPosition)" " );" " ivec2 cascadeIndices;" - " float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices);" + " float cascadeMix = evalCascadeIndicesAndMix(-viewPosition.z, cascadeShadowCoords, cascadeIndices);" " return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);" " }" }; @@ -378,6 +382,10 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::str const auto program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("cameraCorrectionBuffer", CameraCorrection)); + slotBindings.insert(gpu::Shader::Binding("deferredFrameTransformBuffer", DeferredFrameTransform)); + slotBindings.insert(gpu::Shader::Binding("shadowTransformBuffer", ShadowTransform)); + slotBindings.insert(gpu::Shader::Binding("albedoMap", Albedo)); slotBindings.insert(gpu::Shader::Binding("normalMap", Normal)); slotBindings.insert(gpu::Shader::Binding("specularMap", Specular)); @@ -429,6 +437,7 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto& linearDepthTarget = inputs.get1(); auto& surfaceGeometryFramebuffer = inputs.get2(); auto& ambientOcclusionFramebuffer = inputs.get3(); + auto& frameTransform = inputs.get4(); gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -465,11 +474,9 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I const auto& globalShadow = lightAndShadow.second; if (globalShadow) { const auto cascadeIndex = glm::clamp(_mode - Mode::ShadowCascade0Mode, 0, (int)globalShadow->getCascadeCount() - 1); - const auto shadowBufferLoc = pipeline->getProgram()->getUniformBuffers().findLocation("shadowTransformBuffer"); batch.setResourceTexture(Shadow, globalShadow->getCascade(cascadeIndex).map); - if (shadowBufferLoc >= 0) { - batch.setUniformBuffer(shadowBufferLoc, globalShadow->getBuffer()); - } + batch.setUniformBuffer(ShadowTransform, globalShadow->getBuffer()); + batch.setUniformBuffer(DeferredFrameTransform, frameTransform->getFrameTransformBuffer()); } if (linearDepthTarget) { diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 104b161720..8227c4f7a3 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -15,6 +15,7 @@ #include #include +#include "DeferredFrameTransform.h" #include "DeferredFramebuffer.h" #include "SurfaceGeometryPass.h" #include "AmbientOcclusionEffect.h" @@ -37,7 +38,7 @@ signals: class DebugDeferredBuffer { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet5; using Config = DebugDeferredBufferConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 1820076545..6808c75914 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -21,7 +21,7 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0 }; -const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 3 }; +const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 4 }; const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { @@ -58,10 +58,11 @@ LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinShadowDistance, float viewMaxShadowDistance, + float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance, float nearDepth, float farDepth) { assert(viewMinShadowDistance < viewMaxShadowDistance); assert(nearDepth < farDepth); + assert(viewOverlapDistance > 0.0f); assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum @@ -124,7 +125,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Update the buffer auto& schemaCascade = _schemaBuffer.edit().cascades[cascadeIndex]; schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); - schemaCascade.minDistance = viewMinShadowDistance; + schemaCascade.invTransitionWidth = 1.0f / viewOverlapDistance; schemaCascade.maxDistance = viewMaxShadowDistance; cascade.minDistance = viewMinShadowDistance; cascade.maxDistance = viewMaxShadowDistance; diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 07ef0c25b6..9f395c905a 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -71,7 +71,9 @@ public: Shadow(model::LightPointer light, unsigned int cascadeCount = 1); - void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); + void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, + float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance, + float nearDepth = 1.0f, float farDepth = 1000.0f); void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); const UniformBufferView& getBuffer() const { return _schemaBuffer; } diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index ff71d54344..277492b238 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -225,7 +225,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Debugging stages { // Debugging Deferred buffer job - const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); + const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, deferredFrameTransform)); task.addJob("DebugDeferredBuffer", debugFramebuffers); const auto debugSubsurfaceScatteringInputs = DebugSubsurfaceScattering::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 0c4e5d4a61..88b3fe9013 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -260,17 +260,20 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O const auto farClip = args->getViewFrustum().getFarClip(); const auto nearDepth = -args->_boomOffset.z; - static const float SHADOW_MAX_DISTANCE = 25.0f; - static const float SHADOW_OVERLAP_DISTANCE = 1.0f; - float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadow->getCascadeCount() - 1 - _cascadeIndex); - float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE; + static const float SHADOW_MAX_DISTANCE = 30.0f; + static const float CASCADE_LEVEL_SCALE = 2.0f; + float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(CASCADE_LEVEL_SCALE, globalShadow->getCascadeCount() - 1 - _cascadeIndex); + float minCascadeDistance = maxCascadeDistance / CASCADE_LEVEL_SCALE; + float shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; if (_cascadeIndex == 0) { minCascadeDistance = nearDepth; - } + } else { + minCascadeDistance -= shadowOverlapDistance; + } minCascadeDistance = std::max(minCascadeDistance, nearDepth); maxCascadeDistance = std::min(maxCascadeDistance, farClip); - globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 18a39782b6..036f1029e6 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -13,6 +13,9 @@ <@include ShadowCore.slh@> +#define SHADOW_NOISE_ENABLED 0 +#define SHADOW_SCREEN_SPACE_DITHER 1 + // the shadow texture uniform sampler2DShadow shadowMaps[SHADOW_CASCADE_MAX_COUNT]; @@ -41,17 +44,20 @@ ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) { float shadowScale = getShadowScale(); ShadowSampleOffsets offsets; -#if 0 +#if SHADOW_SCREEN_SPACE_DITHER // Pattern dithering in screen space ivec2 coords = ivec2(gl_FragCoord.xy); #else // Pattern dithering in world space (mm resolution) ivec2 coords = ivec2(position.x, position.y+position.z); #endif + +#if SHADOW_NOISE_ENABLED // Add some noise to break dithering int index = int(4.0*evalShadowNoise(gl_FragCoord.xyyx))%4; coords.x += index & 1; coords.y += (index & 2) >> 1; +#endif // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html ivec2 offset = coords & ivec2(1,1); @@ -87,7 +93,7 @@ float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord); } -float evalShadowAttenuation(vec4 worldPosition, vec4 viewPosition) { +float evalShadowAttenuation(vec4 worldPosition, float viewDepth) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); vec4 cascadeShadowCoords[4] = vec4[4] ( evalShadowTexcoord(0, worldPosition), @@ -96,11 +102,11 @@ float evalShadowAttenuation(vec4 worldPosition, vec4 viewPosition) { evalShadowTexcoord(3, worldPosition) ); ivec2 cascadeIndices; - float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices); + float cascadeMix = evalCascadeIndicesAndMix(viewDepth, cascadeShadowCoords, cascadeIndices); vec2 cascadeAttenuations = vec2(1.0, 1.0); cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[cascadeIndices.x]); - if (cascadeMix > 0.0) { + if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[cascadeIndices.y]); } return mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 3e2fc3a8b8..6408ad472a 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -13,7 +13,7 @@ <@include Shadows_shared.slh@> -uniform shadowTransformBuffer { +layout(std140) uniform shadowTransformBuffer { ShadowParameters shadow; }; @@ -21,8 +21,12 @@ int getShadowCascadeCount() { return shadow.cascadeCount; } -float getShadowCascadeMinDistance(int cascadeIndex) { - return shadow.cascades[cascadeIndex].minDistance; +float getShadowCascadeInvTransitionWidth(int cascadeIndex) { + return shadow.cascades[cascadeIndex].invTransitionWidth; +} + +float getShadowCascadeMaxDistance(int cascadeIndex) { + return shadow.cascades[cascadeIndex].maxDistance; } mat4 getShadowReprojection(int cascadeIndex) { @@ -58,19 +62,18 @@ int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) { return cascadeIndex; } -float evalCascadeIndicesAndMix(vec4 viewPosition, vec4 cascadeShadowCoords[4], out ivec2 cascadeIndices) { +float evalCascadeIndicesAndMix(float viewDepth, vec4 cascadeShadowCoords[4], out ivec2 cascadeIndices) { + int cascadeCount = getShadowCascadeCount(); #if 0 // Cascade selection based on : // https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx - vec4 currentPixelDepth = viewPosition.zzzz; vec4 cascadeDepthLimits = vec4( getShadowCascadeMinDistance(0), getShadowCascadeMinDistance(1), getShadowCascadeMinDistance(2), getShadowCascadeMinDistance(3) ); - bvec4 comparison = greaterThan( currentPixelDepth, cascadeDepthLimits); - int cascadeCount = getShadowCascadeCount(); + bvec4 comparison = greaterThan( vec4(viewDepth), cascadeDepthLimits); bvec4 cascadeCountMask = greaterThan(ivec4(cascadeCount), ivec4(0,1,2,3)); int cascadeIndex = int(dot(ivec4(cascadeCountMask), ivec4(comparison))); cascadeIndex = min( cascadeIndex, cascadeCount-1 ); @@ -79,8 +82,11 @@ float evalCascadeIndicesAndMix(vec4 viewPosition, vec4 cascadeShadowCoords[4], o #endif cascadeIndices.x = cascadeIndex; - cascadeIndices.y = cascadeIndex; - return 1.0; + cascadeIndices.y = cascadeIndex+1; + + float maxDepth = getShadowCascadeMaxDistance(cascadeIndices.x); + float cascadeMixRatio = (maxDepth-viewDepth) * getShadowCascadeInvTransitionWidth(cascadeIndices.x); + return 1.0 - clamp(cascadeMixRatio, 0.0, 1.0); } <@endif@> diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index d5002df9a8..7230ba6134 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -13,8 +13,8 @@ struct ShadowTransform { MAT4 reprojection; float bias; - float minDistance; float maxDistance; + float invTransitionWidth; float _padding; }; diff --git a/libraries/render-utils/src/directional_ambient_light_shadow.slf b/libraries/render-utils/src/directional_ambient_light_shadow.slf index 5f71809249..b791c9297a 100644 --- a/libraries/render-utils/src/directional_ambient_light_shadow.slf +++ b/libraries/render-utils/src/directional_ambient_light_shadow.slf @@ -28,7 +28,7 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos); + float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z); if (frag.mode == FRAG_MODE_UNLIT) { discard; diff --git a/libraries/render-utils/src/directional_skybox_light_shadow.slf b/libraries/render-utils/src/directional_skybox_light_shadow.slf index 5c549df95d..2bccc68550 100644 --- a/libraries/render-utils/src/directional_skybox_light_shadow.slf +++ b/libraries/render-utils/src/directional_skybox_light_shadow.slf @@ -28,7 +28,7 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos); + float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z); // Light mapped or not ? if (frag.mode == FRAG_MODE_UNLIT) {