From cf5a7531dfb08afc88cc9c793031732f101bee86 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 11:10:31 +0100 Subject: [PATCH 01/34] Fixed dithered PCF filter in shadow --- libraries/render-utils/src/Shadow.slh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index e844db43dd..6d3b717032 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -68,13 +68,11 @@ vec2 PCFkernel[4] = vec2[4]( ); float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { - // PCF is buggy so disable it for the time being -#if 0 - float pcfRadius = 3.0; +#if 1 float shadowScale = getShadowScale(); // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html - vec2 offset = pcfRadius * step(fract(position.xy), vec2(0.5, 0.5)); + vec2 offset = step(fract(position.xy), vec2(0.5, 0.5)); float shadowAttenuation = (0.25 * ( fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + From 6cd4da877e7c58c116b04fc5b7886158e9bb510c Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 11:11:04 +0100 Subject: [PATCH 02/34] Fixed dithered PCF filter in shadow --- libraries/render-utils/src/Shadow.slh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 6d3b717032..300fa5e7b9 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -68,7 +68,6 @@ vec2 PCFkernel[4] = vec2[4]( ); float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { -#if 1 float shadowScale = getShadowScale(); // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html @@ -80,9 +79,6 @@ float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) )); -#else - float shadowAttenuation = fetchShadow(shadowTexcoord.xyz); -#endif return shadowAttenuation; } From cbd28775246409df2e70b7f8ab2151d34ad061ba Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 11:26:20 +0100 Subject: [PATCH 03/34] Optimized shadow shader evaluation by computing concatenated shadow reprojection matrix on CPU --- libraries/render-utils/src/LightStage.cpp | 11 +++++++---- libraries/render-utils/src/LightStage.h | 6 ++++-- libraries/render-utils/src/Shadow.slh | 18 ++++-------------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 411f179d49..eb712b075c 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -14,6 +14,11 @@ #include "LightStage.h" std::string LightStage::_stageName { "LIGHT_STAGE"}; +const glm::mat4 LightStage::Shadow::_biasMatrix{ + 0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.5, 0.5, 0.5, 1.0 }; LightStage::LightStage() { } @@ -92,8 +97,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, _frustum->calculate(); // Update the buffer - _schemaBuffer.edit().projection = ortho; - _schemaBuffer.edit().viewInverse = viewInverse.getMatrix(); + _schemaBuffer.edit().reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); } void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { @@ -102,8 +106,7 @@ void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { *_frustum = shadowFrustum; // Update the buffer - _schemaBuffer.edit().projection = shadowFrustum.getProjection(); - _schemaBuffer.edit().viewInverse = viewInverse.getMatrix(); + _schemaBuffer.edit().reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); } const glm::mat4& LightStage::Shadow::getView() const { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 3a2a77055f..c45627c89b 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -62,6 +62,9 @@ public: gpu::TexturePointer map; protected: + + static const glm::mat4 _biasMatrix; + model::LightPointer _light; std::shared_ptr _frustum; @@ -70,8 +73,7 @@ public: Schema(); - glm::mat4 projection; - glm::mat4 viewInverse; + glm::mat4 reprojection; glm::float32 bias; glm::float32 scale; diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 300fa5e7b9..8abbc1de3e 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -15,8 +15,7 @@ uniform sampler2DShadow shadowMap; struct ShadowTransform { - mat4 projection; - mat4 viewInverse; + mat4 reprojection; float bias; float scale; @@ -26,12 +25,8 @@ uniform shadowTransformBuffer { ShadowTransform _shadowTransform; }; -mat4 getShadowViewInverse() { - return _shadowTransform.viewInverse; -} - -mat4 getShadowProjection() { - return _shadowTransform.projection; +mat4 getShadowReprojection() { + return _shadowTransform.reprojection; } float getShadowScale() { @@ -44,14 +39,9 @@ float getShadowBias() { // Compute the texture coordinates from world coordinates vec4 evalShadowTexcoord(vec4 position) { - mat4 biasMatrix = mat4( - 0.5, 0.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 0.0, - 0.0, 0.0, 0.5, 0.0, - 0.5, 0.5, 0.5, 1.0); float bias = -getShadowBias(); - vec4 shadowCoord = biasMatrix * getShadowProjection() * getShadowViewInverse() * position; + vec4 shadowCoord = getShadowReprojection() * position; return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); } From 7fd03bed7e1c71276f4f3e8c3f785215701cf4f4 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 14:28:35 +0100 Subject: [PATCH 04/34] Added cascades in schema buffer but still only first used --- libraries/render-utils/src/LightStage.cpp | 13 ++++---- libraries/render-utils/src/LightStage.h | 8 ++--- libraries/render-utils/src/Shadow.slh | 15 ++++------ libraries/render-utils/src/Shadows_shared.slh | 30 +++++++++++++++++++ 4 files changed, 45 insertions(+), 21 deletions(-) create mode 100644 libraries/render-utils/src/Shadows_shared.slh diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index eb712b075c..d5dfe92b1d 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -23,10 +23,11 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ LightStage::LightStage() { } -LightStage::Shadow::Schema::Schema() : - bias{ 0.005f }, - scale{ 1.0f / MAP_SIZE } { - +LightStage::Shadow::Schema::Schema() { + ShadowTransform defaultTransform; + defaultTransform.bias = 0.005f; + std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); + invMapSize = 1.0f / MAP_SIZE; } LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared() } { @@ -97,7 +98,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, _frustum->calculate(); // Update the buffer - _schemaBuffer.edit().reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + _schemaBuffer.edit().cascades[0].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); } void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { @@ -106,7 +107,7 @@ void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { *_frustum = shadowFrustum; // Update the buffer - _schemaBuffer.edit().reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); + _schemaBuffer.edit().cascades[0].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); } const glm::mat4& LightStage::Shadow::getView() const { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index c45627c89b..3324cfbe99 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -68,15 +68,13 @@ public: model::LightPointer _light; std::shared_ptr _frustum; - class Schema { +#include "Shadows_shared.slh" + + class Schema : public ShadowParameters { public: Schema(); - glm::mat4 reprojection; - - glm::float32 bias; - glm::float32 scale; }; UniformBufferView _schemaBuffer = nullptr; diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 8abbc1de3e..1f819e95c9 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -14,27 +14,22 @@ // the shadow texture uniform sampler2DShadow shadowMap; -struct ShadowTransform { - mat4 reprojection; - - float bias; - float scale; -}; +<@include Shadows_shared.slh@> uniform shadowTransformBuffer { - ShadowTransform _shadowTransform; + ShadowParameters shadow; }; mat4 getShadowReprojection() { - return _shadowTransform.reprojection; + return shadow.cascades[0].reprojection; } float getShadowScale() { - return _shadowTransform.scale; + return shadow.invMapSize; } float getShadowBias() { - return _shadowTransform.bias; + return shadow.cascades[0].bias; } // Compute the texture coordinates from world coordinates diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh new file mode 100644 index 0000000000..9e462568ad --- /dev/null +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -0,0 +1,30 @@ +// glsl / C++ compatible source as interface for Shadows +#ifdef __cplusplus +# define MAT4 glm::mat4 +# define VEC3 glm::vec3 +#else +# define MAT4 mat4 +# define VEC3 ve3 +#endif + +#define SHADOW_CASCADE_MAX_COUNT 4 + +struct ShadowTransform { + MAT4 reprojection; + + float bias; + vec3 _padding; +}; + +struct ShadowParameters { + int cascadeCount; + float invMapSize; + float _padding1; + float _padding2; + ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT]; +}; + +// <@if 1@> +// Trigger Scribe include +// <@endif@> +// From e0b36b800f151584c0267f9d1125a658c044125b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 14:55:49 +0100 Subject: [PATCH 05/34] Added Cascade subclass in LightStage Shadow --- .../render-utils/src/DebugDeferredBuffer.cpp | 2 +- .../src/DeferredLightingEffect.cpp | 2 +- libraries/render-utils/src/LightStage.cpp | 53 +++++++++++-------- libraries/render-utils/src/LightStage.h | 34 ++++++++---- .../render-utils/src/RenderDeferredTask.cpp | 2 +- .../render-utils/src/RenderShadowTask.cpp | 8 +-- 6 files changed, 64 insertions(+), 37 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index d334a53fa1..f8bb7b7b54 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -438,7 +438,7 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); const auto& globalShadow = lightAndShadow.second; if (globalShadow) { - batch.setResourceTexture(Shadow, globalShadow->map); + batch.setResourceTexture(Shadow, globalShadow->getCascade(0).map); } if (linearDepthTarget) { diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index dcb16c08f8..a99eb5ffed 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -503,7 +503,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, // Bind the shadow buffer if (globalShadow) { - batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->map); + batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->getCascade(0).map); } auto& program = deferredLightingEffect->_directionalSkyboxLight; diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index d5dfe92b1d..e53fe27836 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -28,20 +28,34 @@ LightStage::Shadow::Schema::Schema() { defaultTransform.bias = 0.005f; std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); invMapSize = 1.0f / MAP_SIZE; + cascadeCount = 1; } -LightStage::Shadow::Shadow(model::LightPointer light) : _light{ light}, _frustum{ std::make_shared() } { +LightStage::Shadow::Cascade::Cascade() : _frustum{ std::make_shared() } { framebuffer = gpu::FramebufferPointer(gpu::Framebuffer::createShadowmap(MAP_SIZE)); map = framebuffer->getDepthStencilBuffer(); - Schema schema; - _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); } -void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, +const glm::mat4& LightStage::Shadow::Cascade::getView() const { + return _frustum->getView(); +} + +const glm::mat4& LightStage::Shadow::Cascade::getProjection() const { + return _frustum->getProjection(); +} + +LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } { + Schema schema; + _schemaBuffer = std::make_shared(sizeof(Schema), (const gpu::Byte*) &schema); + _cascades.resize(cascadeCount); +} + +void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth, float farDepth) { assert(viewMinShadowDistance < viewMaxShadowDistance); assert(nearDepth < farDepth); + assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum const auto& direction = glm::normalize(_light->getDirection()); @@ -55,12 +69,15 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, auto up = glm::normalize(glm::cross(side, direction)); orientation = glm::quat_cast(glm::mat3(side, up, -direction)); } - _frustum->setOrientation(orientation); + + auto& cascade = _cascades[cascadeIndex]; + + cascade._frustum->setOrientation(orientation); // Position the keylight frustum - _frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*direction); + cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*direction); - const Transform view{ _frustum->getView()}; + const Transform view{ cascade._frustum->getView()}; const Transform viewInverse{ view.getInverseMatrix() }; auto nearCorners = viewFrustum.getCorners(viewMinShadowDistance); @@ -92,30 +109,24 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, auto near = glm::max(max.z, -nearDepth); auto far = -min.z; glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, near, far); - _frustum->setProjection(ortho); + cascade._frustum->setProjection(ortho); // Calculate the frustum's internal state - _frustum->calculate(); + cascade._frustum->calculate(); // Update the buffer - _schemaBuffer.edit().cascades[0].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + _schemaBuffer.edit().cascades[cascadeIndex].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); } -void LightStage::Shadow::setFrustum(const ViewFrustum& shadowFrustum) { +void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { + assert(cascadeIndex < _cascades.size()); const Transform view{ shadowFrustum.getView() }; const Transform viewInverse{ view.getInverseMatrix() }; + auto& cascade = _cascades[cascadeIndex]; - *_frustum = shadowFrustum; + *cascade._frustum = shadowFrustum; // Update the buffer - _schemaBuffer.edit().cascades[0].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); -} - -const glm::mat4& LightStage::Shadow::getView() const { - return _frustum->getView(); -} - -const glm::mat4& LightStage::Shadow::getProjection() const { - return _frustum->getProjection(); + _schemaBuffer.edit().cascades[cascadeIndex].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); } LightStage::Index LightStage::findLight(const LightPointer& light) const { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 3324cfbe99..2f5699c28d 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -46,27 +46,43 @@ public: using UniformBufferView = gpu::BufferView; static const int MAP_SIZE = 1024; - Shadow(model::LightPointer light); + class Cascade { + friend Shadow; + public: - void setKeylightFrustum(const ViewFrustum& viewFrustum, float viewMinShadowDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); + Cascade(); - void setFrustum(const ViewFrustum& shadowFrustum); - const std::shared_ptr getFrustum() const { return _frustum; } + gpu::FramebufferPointer framebuffer; + gpu::TexturePointer map; - const glm::mat4& getView() const; - const glm::mat4& getProjection() const; + const std::shared_ptr& getFrustum() const { return _frustum; } + + const glm::mat4& getView() const; + const glm::mat4& getProjection() const; + + private: + + std::shared_ptr _frustum; + }; + + 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 setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); const UniformBufferView& getBuffer() const { return _schemaBuffer; } - gpu::FramebufferPointer framebuffer; - gpu::TexturePointer map; + unsigned int getCascadeCount() const { return (unsigned int)_cascades.size(); } + const Cascade& getCascade(unsigned int index) const { return _cascades[index]; } protected: + using Cascades = std::vector; + static const glm::mat4 _biasMatrix; model::LightPointer _light; - std::shared_ptr _frustum; + Cascades _cascades; #include "Shadows_shared.slh" diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 75af8506a2..0a80ca4ade 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -545,7 +545,7 @@ void DrawFrustums::run(const render::RenderContextPointer& renderContext) { const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { - updateFrustum(*globalShadow->getFrustum(), _shadowFrustumMeshVertices); + updateFrustum(*globalShadow->getCascade(0).getFrustum(), _shadowFrustumMeshVertices); } } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 7a6e3dc74f..ce6e78093d 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -139,7 +139,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con auto shadow = lightStage->getCurrentKeyShadow(); if (!shadow) return; - const auto& fbo = shadow->framebuffer; + const auto& fbo = shadow->getCascade(0).framebuffer; RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; @@ -149,7 +149,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con // the minimal Z range. adjustNearFar(inShapeBounds, adjustedShadowFrustum); // Reapply the frustum as it has been adjusted - shadow->setFrustum(adjustedShadowFrustum); + shadow->setFrustum(0, adjustedShadowFrustum); args->popViewFrustum(); args->pushViewFrustum(adjustedShadowFrustum); @@ -252,10 +252,10 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O auto nearClip = args->getViewFrustum().getNearClip(); float nearDepth = -args->_boomOffset.z; const float SHADOW_MAX_DISTANCE = 20.0f; - globalShadow->setKeylightFrustum(args->getViewFrustum(), nearDepth, nearClip + SHADOW_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightFrustum(0, args->getViewFrustum(), nearDepth, nearClip + SHADOW_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args - args->pushViewFrustum(*(globalShadow->getFrustum())); + args->pushViewFrustum(*(globalShadow->getCascade(0).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; } } From 8f84e5fbed3c592a73621e01723dca3b4bb7f81c Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 15:39:26 +0100 Subject: [PATCH 06/34] Added sampler array in shader to support future cascades --- .../src/DeferredLightingEffect.cpp | 15 ++++--- libraries/render-utils/src/Shadow.slh | 44 ++++++++++--------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index a99eb5ffed..831dc86452 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -58,7 +58,7 @@ enum DeferredShader_MapSlot { DEFERRED_BUFFER_DEPTH_UNIT = 3, DEFERRED_BUFFER_OBSCURANCE_UNIT = 4, SHADOW_MAP_UNIT = 5, - SKYBOX_MAP_UNIT = 6, + SKYBOX_MAP_UNIT = SHADOW_MAP_UNIT + SHADOW_CASCADE_MAX_COUNT, DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, DEFERRED_BUFFER_CURVATURE_UNIT, DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, @@ -156,7 +156,7 @@ static gpu::ShaderPointer makeLightProgram(const char* vertSource, const char* f slotBindings.insert(gpu::Shader::Binding(std::string("specularMap"), DEFERRED_BUFFER_EMISSIVE_UNIT)); slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), DEFERRED_BUFFER_DEPTH_UNIT)); slotBindings.insert(gpu::Shader::Binding(std::string("obscuranceMap"), DEFERRED_BUFFER_OBSCURANCE_UNIT)); - slotBindings.insert(gpu::Shader::Binding(std::string("shadowMap"), SHADOW_MAP_UNIT)); + slotBindings.insert(gpu::Shader::Binding(std::string("shadowMaps"), SHADOW_MAP_UNIT)); slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), SKYBOX_MAP_UNIT)); slotBindings.insert(gpu::Shader::Binding(std::string("linearZeyeMap"), DEFERRED_BUFFER_LINEAR_DEPTH_UNIT)); @@ -501,9 +501,11 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); const auto& globalShadow = lightAndShadow.second; - // Bind the shadow buffer + // Bind the shadow buffers if (globalShadow) { - batch.setResourceTexture(SHADOW_MAP_UNIT, globalShadow->getCascade(0).map); + for (auto i = 0; i < globalShadow->getCascadeCount(); i++) { + batch.setResourceTexture(SHADOW_MAP_UNIT+i, globalShadow->getCascade(i).map); + } } auto& program = deferredLightingEffect->_directionalSkyboxLight; @@ -567,8 +569,9 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, deferredLightingEffect->unsetKeyLightBatch(batch, locations->lightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT); - - batch.setResourceTexture(SHADOW_MAP_UNIT, nullptr); + for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { + batch.setResourceTexture(SHADOW_MAP_UNIT+i, nullptr); + } } } diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 1f819e95c9..46661b3b8a 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -11,38 +11,38 @@ <@if not SHADOW_SLH@> <@def SHADOW_SLH@> -// the shadow texture -uniform sampler2DShadow shadowMap; - <@include Shadows_shared.slh@> +// the shadow texture +uniform sampler2DShadow shadowMaps[SHADOW_CASCADE_MAX_COUNT]; + uniform shadowTransformBuffer { ShadowParameters shadow; }; -mat4 getShadowReprojection() { - return shadow.cascades[0].reprojection; +mat4 getShadowReprojection(int cascadeIndex) { + return shadow.cascades[cascadeIndex].reprojection; } float getShadowScale() { return shadow.invMapSize; } -float getShadowBias() { - return shadow.cascades[0].bias; +float getShadowBias(int cascadeIndex) { + return shadow.cascades[cascadeIndex].bias; } // Compute the texture coordinates from world coordinates -vec4 evalShadowTexcoord(vec4 position) { - float bias = -getShadowBias(); +vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { + float bias = -getShadowBias(cascadeIndex); - vec4 shadowCoord = getShadowReprojection() * position; + vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); } // Sample the shadowMap with PCF (built-in) -float fetchShadow(vec3 shadowTexcoord) { - return texture(shadowMap, shadowTexcoord); +float fetchShadow(int cascadeIndex, vec3 shadowTexcoord) { + return texture(shadowMaps[cascadeIndex], shadowTexcoord); } vec2 PCFkernel[4] = vec2[4]( @@ -52,24 +52,24 @@ vec2 PCFkernel[4] = vec2[4]( vec2(0.5, -1.5) ); -float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { +float evalShadowAttenuationPCF(int cascadeIndex, vec4 position, vec4 shadowTexcoord) { float shadowScale = getShadowScale(); // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html vec2 offset = step(fract(position.xy), vec2(0.5, 0.5)); float shadowAttenuation = (0.25 * ( - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) + fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) )); return shadowAttenuation; } -float evalShadowAttenuation(vec4 position) { - vec4 shadowTexcoord = evalShadowTexcoord(position); +float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position) { + vec4 shadowTexcoord = evalShadowTexcoord(cascadeIndex, position); 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) { @@ -77,7 +77,11 @@ float evalShadowAttenuation(vec4 position) { return 1.0; } - return evalShadowAttenuationPCF(position, shadowTexcoord); + return evalShadowAttenuationPCF(cascadeIndex, position, shadowTexcoord); +} + +float evalShadowAttenuation(vec4 position) { + return evalShadowCascadeAttenuation(0, position); } <@endif@> From 08b06281f4592d472f55cee1e0be394e17706a5e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 16:17:09 +0100 Subject: [PATCH 07/34] Min / Max cascade distance computation --- libraries/render-utils/src/LightStage.cpp | 5 +++- libraries/render-utils/src/LightStage.h | 2 ++ .../render-utils/src/RenderShadowTask.cpp | 23 ++++++++++++++----- libraries/render-utils/src/RenderShadowTask.h | 6 +++++ libraries/render-utils/src/Shadows_shared.slh | 4 +++- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index e53fe27836..a52e5bb1ed 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -115,7 +115,10 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie cascade._frustum->calculate(); // Update the buffer - _schemaBuffer.edit().cascades[cascadeIndex].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + auto& schemaCascade = _schemaBuffer.edit().cascades[cascadeIndex]; + schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + schemaCascade.minDistance = viewMinShadowDistance; + schemaCascade.maxDistance = viewMaxShadowDistance; } 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 2f5699c28d..ab0a9ed8a0 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -94,6 +94,8 @@ public: }; UniformBufferView _schemaBuffer = nullptr; + void setupCascades(); + friend class Light; }; using ShadowPointer = std::shared_ptr; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index ce6e78093d..5da25cb860 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -215,7 +215,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } - const auto cachedMode = task.addJob("ShadowSetup"); + const auto cachedMode = task.addJob("ShadowSetup", 0); // CPU jobs: // Fetch and cull the items from the scene @@ -249,13 +249,24 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O RenderArgs* args = renderContext->args; output = args->_renderMode; - auto nearClip = args->getViewFrustum().getNearClip(); - float nearDepth = -args->_boomOffset.z; - const float SHADOW_MAX_DISTANCE = 20.0f; - globalShadow->setKeylightFrustum(0, args->getViewFrustum(), nearDepth, nearClip + SHADOW_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + const auto nearClip = args->getViewFrustum().getNearClip(); + 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; + + if (_cascadeIndex == 0) { + minCascadeDistance = nearDepth; + } + minCascadeDistance = std::max(minCascadeDistance, nearDepth); + maxCascadeDistance = std::min(maxCascadeDistance, farClip); + globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args - args->pushViewFrustum(*(globalShadow->getCascade(0).getFrustum())); + args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; } } diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 7b2bbeb306..99f6e1ecc1 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -56,7 +56,13 @@ class RenderShadowSetup { public: using Output = RenderArgs::RenderMode; using JobModel = render::Job::ModelO; + + RenderShadowSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} void run(const render::RenderContextPointer& renderContext, Output& output); + +private: + + unsigned int _cascadeIndex; }; class RenderShadowTeardown { diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index 9e462568ad..d5002df9a8 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -13,7 +13,9 @@ struct ShadowTransform { MAT4 reprojection; float bias; - vec3 _padding; + float minDistance; + float maxDistance; + float _padding; }; struct ShadowParameters { From 103e036b70f50dc63295f9b16cbac5ecc5f030f0 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 10 Nov 2017 18:30:37 +0100 Subject: [PATCH 08/34] Shadow maps rendered in cascades --- .../src/RenderableZoneEntityItem.cpp | 2 +- .../render-utils/src/DebugDeferredBuffer.cpp | 8 +- .../render-utils/src/DebugDeferredBuffer.h | 5 +- libraries/render-utils/src/LightStage.cpp | 5 +- libraries/render-utils/src/LightStage.h | 4 +- .../render-utils/src/RenderDeferredTask.cpp | 33 ++- .../render-utils/src/RenderDeferredTask.h | 6 +- .../render-utils/src/RenderShadowTask.cpp | 58 +++-- libraries/render-utils/src/RenderShadowTask.h | 11 +- libraries/render/src/render/CullTask.cpp | 235 +++++++++--------- libraries/render/src/render/CullTask.h | 22 +- libraries/render/src/render/Item.h | 4 + .../src/render/RenderFetchCullSortTask.cpp | 6 +- .../utilities/render/deferredLighting.qml | 5 +- 14 files changed, 226 insertions(+), 178 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 54cc888c35..7e57eea53f 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -116,7 +116,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); + _shadowIndex = _stage->addShadow(_sunIndex, LightStage::SUN_SHADOW_CASCADE_COUNT); } else { _stage->updateLightArrayBuffer(_sunIndex); } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index f8bb7b7b54..1a95329724 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -284,7 +284,10 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust return DEFAULT_SCATTERING_SHADER; case LightingMode: return DEFAULT_LIGHTING_SHADER; - case ShadowMode: + case Shadow0Mode: + case Shadow1Mode: + case Shadow2Mode: + case Shadow3Mode: return DEFAULT_SHADOW_SHADER; case LinearDepthMode: return DEFAULT_LINEAR_DEPTH_SHADER; @@ -438,7 +441,8 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); const auto& globalShadow = lightAndShadow.second; if (globalShadow) { - batch.setResourceTexture(Shadow, globalShadow->getCascade(0).map); + const auto cascadeIndex = glm::clamp(_mode - Mode::Shadow0Mode, 0, (int)globalShadow->getCascadeCount() - 1); + batch.setResourceTexture(Shadow, globalShadow->getCascade(cascadeIndex).map); } if (linearDepthTarget) { diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index bd5618f5be..49e53c4617 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -64,7 +64,10 @@ protected: LightmapMode, ScatteringMode, LightingMode, - ShadowMode, + Shadow0Mode, + Shadow1Mode, + Shadow2Mode, + Shadow3Mode, LinearDepthMode, HalfLinearDepthMode, HalfNormalMode, diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index a52e5bb1ed..c3a4ebf137 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -19,6 +19,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 }; LightStage::LightStage() { } @@ -168,11 +169,11 @@ LightStage::Index LightStage::addLight(const LightPointer& light) { } } -LightStage::Index LightStage::addShadow(Index lightIndex) { +LightStage::Index LightStage::addShadow(Index lightIndex, unsigned int cascadeCount) { auto light = getLight(lightIndex); Index shadowId = INVALID_INDEX; if (light) { - shadowId = _shadows.newElement(std::make_shared(light)); + shadowId = _shadows.newElement(std::make_shared(light, cascadeCount)); _descs[lightIndex].shadowId = shadowId; } return shadowId; diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index ab0a9ed8a0..0f77977f2b 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -31,6 +31,8 @@ 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 { render::indexed_container::INVALID_INDEX }; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } @@ -109,7 +111,7 @@ public: Index findLight(const LightPointer& light) const; Index addLight(const LightPointer& light); - Index addShadow(Index lightIndex); + Index addShadow(Index lightIndex, unsigned int cascadeCount = 1U); LightPointer removeLight(Index index); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 0a80ca4ade..53d280b146 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -533,19 +533,25 @@ void DrawFrustums::run(const render::RenderContextPointer& renderContext) { _frustumMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX)); _viewFrustumMeshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); _viewFrustumMeshStream.addBuffer(_viewFrustumMeshVertices._buffer, _viewFrustumMeshVertices._offset, _viewFrustumMeshVertices._stride); - _shadowFrustumMeshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); - _shadowFrustumMeshStream.addBuffer(_shadowFrustumMeshVertices._buffer, _shadowFrustumMeshVertices._offset, _shadowFrustumMeshVertices._stride); + for (auto i = 0; i < MAX_SHADOW_FRUSTUM_COUNT; i++) { + _shadowFrustumMeshVertices[i] = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); + _shadowFrustumMeshStream[i].addBuffer(_shadowFrustumMeshVertices[i]._buffer, _shadowFrustumMeshVertices[i]._offset, _shadowFrustumMeshVertices[i]._stride); + } } + auto lightStage = renderContext->_scene->getStage(); + assert(lightStage); + + const auto globalShadow = lightStage->getCurrentKeyShadow(); + if (_updateFrustums) { updateFrustum(args->getViewFrustum(), _viewFrustumMeshVertices); - auto lightStage = renderContext->_scene->getStage(); - assert(lightStage); - - const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow) { - updateFrustum(*globalShadow->getCascade(0).getFrustum(), _shadowFrustumMeshVertices); + const auto cascadeCount = std::min(MAX_SHADOW_FRUSTUM_COUNT, (int)globalShadow->getCascadeCount()); + for (auto i = 0; i < cascadeCount; i++) { + updateFrustum(*globalShadow->getCascade(i).getFrustum(), _shadowFrustumMeshVertices[i]); + } } } @@ -583,9 +589,16 @@ void DrawFrustums::run(const render::RenderContextPointer& renderContext) { batch.setInputStream(0, _viewFrustumMeshStream); batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); - batch._glUniform4f(0, 1.0f, 0.0f, 0.0f, 1.0f); - batch.setInputStream(0, _shadowFrustumMeshStream); - batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); + if (globalShadow) { + const auto cascadeCount = std::min(MAX_SHADOW_FRUSTUM_COUNT, (int)globalShadow->getCascadeCount()); + for (auto i = 0; i < cascadeCount; i++) { + float cascadeTint = i / (float)(globalShadow->getCascadeCount() - 1); + + batch._glUniform4f(0, 1.0f, 0.0f, cascadeTint, 1.0f); + batch.setInputStream(0, _shadowFrustumMeshStream[i]); + batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); + } + } args->_batch = nullptr; }); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index e8dd22359d..8f969f5225 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -193,13 +193,15 @@ public: private: + static const int MAX_SHADOW_FRUSTUM_COUNT{ 4 }; + bool _updateFrustums{ true }; gpu::PipelinePointer _pipeline; gpu::BufferView _frustumMeshIndices; gpu::BufferView _viewFrustumMeshVertices; - gpu::BufferView _shadowFrustumMeshVertices; gpu::BufferStream _viewFrustumMeshStream; - gpu::BufferStream _shadowFrustumMeshStream; + gpu::BufferView _shadowFrustumMeshVertices[MAX_SHADOW_FRUSTUM_COUNT]; + gpu::BufferStream _shadowFrustumMeshStream[MAX_SHADOW_FRUSTUM_COUNT]; static void updateFrustum(const ViewFrustum& frustum, gpu::BufferView& vertexBuffer); }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 5da25cb860..d9af9659e5 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -137,9 +137,11 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con assert(lightStage); auto shadow = lightStage->getCurrentKeyShadow(); - if (!shadow) return; + if (!shadow || _cascadeIndex >= shadow->getCascadeCount()) { + return; + } - const auto& fbo = shadow->getCascade(0).framebuffer; + const auto& fbo = shadow->getCascade(_cascadeIndex).framebuffer; RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; @@ -149,7 +151,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con // the minimal Z range. adjustNearFar(inShapeBounds, adjustedShadowFrustum); // Reapply the frustum as it has been adjusted - shadow->setFrustum(0, adjustedShadowFrustum); + shadow->setFrustum(_cascadeIndex, adjustedShadowFrustum); args->popViewFrustum(); args->pushViewFrustum(adjustedShadowFrustum); @@ -215,22 +217,25 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } - const auto cachedMode = task.addJob("ShadowSetup", 0); + for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { + const auto setupOutput = task.addJob("ShadowSetup", i); + const auto shadowFilter = setupOutput.getN(1); - // CPU jobs: - // Fetch and cull the items from the scene - auto shadowFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - const auto shadowSelection = task.addJob("FetchShadowSelection", shadowFilter); - const auto culledShadowSelection = task.addJob("CullShadowSelection", shadowSelection, cullFunctor, RenderDetails::SHADOW, shadowFilter); + // CPU jobs: + // Fetch and cull the items from the scene + const auto shadowSelection = task.addJob("FetchShadowSelection", shadowFilter); + const auto cullInputs = CullSpatialSelection::Inputs(shadowSelection, shadowFilter).asVarying(); + const auto culledShadowSelection = task.addJob("CullShadowSelection", cullInputs, cullFunctor, RenderDetails::SHADOW); - // Sort - const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection); - const auto sortedShapesAndBounds = task.addJob("DepthSortShadowMap", sortedPipelines, true); + // Sort + const auto sortedPipelines = task.addJob("PipelineSortShadowSort", culledShadowSelection); + const auto sortedShapesAndBounds = task.addJob("DepthSortShadowMap", sortedPipelines, true); - // GPU jobs: Render to shadow map - task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber); + // GPU jobs: Render to shadow map + task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i); - task.addJob("ShadowTeardown", cachedMode); + task.addJob("ShadowTeardown", setupOutput); + } } void RenderShadowTask::configure(const Config& configuration) { @@ -239,15 +244,18 @@ void RenderShadowTask::configure(const Config& configuration) { // Task::configure(configuration); } -void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Output& output) { +void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); + // Cache old render args + RenderArgs* args = renderContext->args; + + output.edit0() = args->_renderMode; const auto globalShadow = lightStage->getCurrentKeyShadow(); - if (globalShadow) { - // Cache old render args - RenderArgs* args = renderContext->args; - output = args->_renderMode; + const auto globalShadowCascadeCount = globalShadow->getCascadeCount(); + if (globalShadow && _cascadeIndexgetViewFrustum().getNearClip(); const auto farClip = args->getViewFrustum().getFarClip(); @@ -255,7 +263,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O 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 maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadowCascadeCount - 1 - _cascadeIndex); float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE; if (_cascadeIndex == 0) { @@ -268,6 +276,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; + } else { + output.edit1() = ItemFilter::Builder::nothing(); } } @@ -275,6 +285,8 @@ void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext RenderArgs* args = renderContext->args; // Reset the render args - args->popViewFrustum(); - args->_renderMode = input; + args->_renderMode = input.get0(); + if (!input.get1().selectsNothing()) { + args->popViewFrustum(); + } }; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 99f6e1ecc1..3dcdfdd9b1 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -24,11 +24,12 @@ public: using Inputs = render::VaryingSet2; using JobModel = render::Job::ModelI; - RenderShadowMap(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + RenderShadowMap(render::ShapePlumberPointer shapePlumber, unsigned int cascadeIndex) : _shapePlumber{ shapePlumber }, _cascadeIndex{ cascadeIndex } {} void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: render::ShapePlumberPointer _shapePlumber; + unsigned int _cascadeIndex; }; class RenderShadowTaskConfig : public render::Task::Config::Persistent { @@ -54,11 +55,11 @@ public: class RenderShadowSetup { public: - using Output = RenderArgs::RenderMode; - using JobModel = render::Job::ModelO; + using Outputs = render::VaryingSet2; + using JobModel = render::Job::ModelO; RenderShadowSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} - void run(const render::RenderContextPointer& renderContext, Output& output); + void run(const render::RenderContextPointer& renderContext, Outputs& output); private: @@ -67,7 +68,7 @@ private: class RenderShadowTeardown { public: - using Input = RenderArgs::RenderMode; + using Input = RenderShadowSetup::Outputs; using JobModel = render::Job::ModelI; void run(const render::RenderContextPointer& renderContext, const Input& input); }; diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp index 4fc53d99f9..70331cdb47 100644 --- a/libraries/render/src/render/CullTask.cpp +++ b/libraries/render/src/render/CullTask.cpp @@ -82,28 +82,30 @@ void FetchSpatialTree::configure(const Config& config) { _lodAngle = config.lodAngle; } -void FetchSpatialTree::run(const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection) { - assert(renderContext->args); - assert(renderContext->args->hasViewFrustum()); - RenderArgs* args = renderContext->args; - auto& scene = renderContext->_scene; - +void FetchSpatialTree::run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection) { // start fresh outSelection.clear(); - // Eventually use a frozen frustum - auto queryFrustum = args->getViewFrustum(); - if (_freezeFrustum) { - if (_justFrozeFrustum) { - _justFrozeFrustum = false; - _frozenFrutstum = args->getViewFrustum(); - } - queryFrustum = _frozenFrutstum; - } + if (!filter.selectsNothing()) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + RenderArgs* args = renderContext->args; + auto& scene = renderContext->_scene; - // Octree selection! - float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); - scene->getSpatialTree().selectCellItems(outSelection, _filter, queryFrustum, angle); + // Eventually use a frozen frustum + auto queryFrustum = args->getViewFrustum(); + if (_freezeFrustum) { + if (_justFrozeFrustum) { + _justFrozeFrustum = false; + _frozenFrustum = args->getViewFrustum(); + } + queryFrustum = _frozenFrustum; + } + + // Octree selection! + float angle = glm::degrees(getAccuracyAngle(args->_sizeScale, args->_boundaryLevelAdjust)); + scene->getSpatialTree().selectCellItems(outSelection, filter, queryFrustum, angle); + } } void CullSpatialSelection::configure(const Config& config) { @@ -113,11 +115,12 @@ void CullSpatialSelection::configure(const Config& config) { } void CullSpatialSelection::run(const RenderContextPointer& renderContext, - const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems) { + const Inputs& inputs, ItemBounds& outItems) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; auto& scene = renderContext->_scene; + auto& inSelection = inputs.get0(); auto& details = args->_details.edit(_detailType); details._considered += (int)inSelection.numItems(); @@ -126,9 +129,9 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, if (_freezeFrustum) { if (_justFrozeFrustum) { _justFrozeFrustum = false; - _frozenFrutstum = args->getViewFrustum(); + _frozenFrustum = args->getViewFrustum(); } - args->pushViewFrustum(_frozenFrutstum); // replace the true view frustum by the frozen one + args->pushViewFrustum(_frozenFrustum); // replace the true view frustum by the frozen one } // Culling Frustum / solidAngle test helper class @@ -181,122 +184,124 @@ void CullSpatialSelection::run(const RenderContextPointer& renderContext, outItems.clear(); outItems.reserve(inSelection.numItems()); - // Now get the bound, and - // filter individually against the _filter - // visibility cull if partially selected ( octree cell contianing it was partial) - // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) + const auto filter = inputs.get1(); + if (!filter.selectsNothing()) { + // Now get the bound, and + // filter individually against the _filter + // visibility cull if partially selected ( octree cell contianing it was partial) + // distance cull if was a subcell item ( octree cell is way bigger than the item bound itself, so now need to test per item) - if (_skipCulling) { - // inside & fit items: filter only, culling is disabled - { - PerformanceTimer perfTimer("insideFitItems"); - for (auto id : inSelection.insideItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - // inside & subcell items: filter only, culling is disabled - { - PerformanceTimer perfTimer("insideSmallItems"); - for (auto id : inSelection.insideSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - // partial & fit items: filter only, culling is disabled - { - PerformanceTimer perfTimer("partialFitItems"); - for (auto id : inSelection.partialItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - // partial & subcell items: filter only, culling is disabled - { - PerformanceTimer perfTimer("partialSmallItems"); - for (auto id : inSelection.partialSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - } else { - - // inside & fit items: easy, just filter - { - PerformanceTimer perfTimer("insideFitItems"); - for (auto id : inSelection.insideItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - outItems.emplace_back(itemBound); - } - } - } - - // inside & subcell items: filter & distance cull - { - PerformanceTimer perfTimer("insideSmallItems"); - for (auto id : inSelection.insideSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.solidAngleTest(itemBound.bound)) { + if (_skipCulling) { + // inside & fit items: filter only, culling is disabled + { + PerformanceTimer perfTimer("insideFitItems"); + for (auto id : inSelection.insideItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); } } } - } - // partial & fit items: filter & frustum cull - { - PerformanceTimer perfTimer("partialFitItems"); - for (auto id : inSelection.partialItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { + // inside & subcell items: filter only, culling is disabled + { + PerformanceTimer perfTimer("insideSmallItems"); + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); outItems.emplace_back(itemBound); } } } - } - // partial & subcell items:: filter & frutum cull & solidangle cull - { - PerformanceTimer perfTimer("partialSmallItems"); - for (auto id : inSelection.partialSubcellItems) { - auto& item = scene->getItem(id); - if (_filter.test(item.getKey())) { - ItemBound itemBound(id, item.getBound()); - if (test.frustumTest(itemBound.bound)) { + // partial & fit items: filter only, culling is disabled + { + PerformanceTimer perfTimer("partialFitItems"); + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // partial & subcell items: filter only, culling is disabled + { + PerformanceTimer perfTimer("partialSmallItems"); + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + } else { + + // inside & fit items: easy, just filter + { + PerformanceTimer perfTimer("insideFitItems"); + for (auto id : inSelection.insideItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + outItems.emplace_back(itemBound); + } + } + } + + // inside & subcell items: filter & distance cull + { + PerformanceTimer perfTimer("insideSmallItems"); + for (auto id : inSelection.insideSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); if (test.solidAngleTest(itemBound.bound)) { outItems.emplace_back(itemBound); } } } } + + // partial & fit items: filter & frustum cull + { + PerformanceTimer perfTimer("partialFitItems"); + for (auto id : inSelection.partialItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } + } + } + } + + // partial & subcell items:: filter & frutum cull & solidangle cull + { + PerformanceTimer perfTimer("partialSmallItems"); + for (auto id : inSelection.partialSubcellItems) { + auto& item = scene->getItem(id); + if (filter.test(item.getKey())) { + ItemBound itemBound(id, item.getBound()); + if (test.frustumTest(itemBound.bound)) { + if (test.solidAngleTest(itemBound.bound)) { + outItems.emplace_back(itemBound); + } + } + } + } + } } } details._rendered += (int)outItems.size(); - // Restore frustum if using the frozen one: if (_freezeFrustum) { args->popViewFrustum(); diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h index fae2a342a1..486c4f4cdf 100644 --- a/libraries/render/src/render/CullTask.h +++ b/libraries/render/src/render/CullTask.h @@ -51,19 +51,16 @@ namespace render { class FetchSpatialTree { bool _freezeFrustum{ false }; // initialized by Config bool _justFrozeFrustum{ false }; - ViewFrustum _frozenFrutstum; + ViewFrustum _frozenFrustum; float _lodAngle; public: using Config = FetchSpatialTreeConfig; - using JobModel = Job::ModelO; + using JobModel = Job::ModelIO; FetchSpatialTree() {} - FetchSpatialTree(const ItemFilter& filter) : _filter(filter) {} - - ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; void configure(const Config& config); - void run(const RenderContextPointer& renderContext, ItemSpatialTree::ItemSelection& outSelection); + void run(const RenderContextPointer& renderContext, const ItemFilter& filter, ItemSpatialTree::ItemSelection& outSelection); }; class CullSpatialSelectionConfig : public Job::Config { @@ -88,25 +85,24 @@ namespace render { bool _freezeFrustum{ false }; // initialized by Config bool _justFrozeFrustum{ false }; bool _skipCulling{ false }; - ViewFrustum _frozenFrutstum; + ViewFrustum _frozenFrustum; public: using Config = CullSpatialSelectionConfig; - using JobModel = Job::ModelIO; + using Inputs = render::VaryingSet2; + using JobModel = Job::ModelIO; - CullSpatialSelection(CullFunctor cullFunctor, RenderDetails::Type type, const ItemFilter& filter) : + CullSpatialSelection(CullFunctor cullFunctor, RenderDetails::Type type) : _cullFunctor{ cullFunctor }, - _detailType(type), - _filter(filter) {} + _detailType(type) {} CullSpatialSelection(CullFunctor cullFunctor) : _cullFunctor{ cullFunctor } {} CullFunctor _cullFunctor; RenderDetails::Type _detailType{ RenderDetails::OTHER }; - ItemFilter _filter{ ItemFilter::Builder::opaqueShape().withoutLayered() }; void configure(const Config& config); - void run(const RenderContextPointer& renderContext, const ItemSpatialTree::ItemSelection& inSelection, ItemBounds& outItems); + void run(const RenderContextPointer& renderContext, const Inputs& inputs, ItemBounds& outItems); }; } diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 2b02db81f9..77f5910b9e 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -182,6 +182,8 @@ public: Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); } + Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); } + // Convenient standard keys that we will keep on using all over the place static Builder visibleWorldItems() { return Builder().withVisible().withWorldSpace(); } static Builder opaqueShape() { return Builder().withTypeShape().withOpaque().withWorldSpace(); } @@ -191,12 +193,14 @@ public: static Builder background() { return Builder().withViewSpace().withLayered(); } static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().withWorldSpace().withLayered(); } static Builder transparentShapeLayered() { return Builder().withTypeShape().withTransparent().withWorldSpace().withLayered(); } + static Builder nothing() { return Builder().withNothing(); } }; ItemFilter(const Builder& builder) : ItemFilter(builder._value, builder._mask) {} // Item Filter operator testing if a key pass the filter bool test(const ItemKey& key) const { return (key._flags & _mask) == (_value & _mask); } + bool selectsNothing() const { return !_mask.any(); } class Less { public: diff --git a/libraries/render/src/render/RenderFetchCullSortTask.cpp b/libraries/render/src/render/RenderFetchCullSortTask.cpp index b9f65f48a0..d7294fa2bd 100644 --- a/libraries/render/src/render/RenderFetchCullSortTask.cpp +++ b/libraries/render/src/render/RenderFetchCullSortTask.cpp @@ -22,9 +22,11 @@ void RenderFetchCullSortTask::build(JobModel& task, const Varying& input, Varyin // CPU jobs: // Fetch and cull the items from the scene - auto spatialFilter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); + const ItemFilter filter = ItemFilter::Builder::visibleWorldItems().withoutLayered(); + const auto spatialFilter = render::Varying(filter); const auto spatialSelection = task.addJob("FetchSceneSelection", spatialFilter); - const auto culledSpatialSelection = task.addJob("CullSceneSelection", spatialSelection, cullFunctor, RenderDetails::ITEM, spatialFilter); + const auto cullInputs = CullSpatialSelection::Inputs(spatialSelection, spatialFilter).asVarying(); + const auto culledSpatialSelection = task.addJob("CullSceneSelection", cullInputs, cullFunctor, RenderDetails::ITEM); // Overlays are not culled const auto nonspatialSelection = task.addJob("FetchOverlaySelection"); diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 86a16d9a25..def8b1fcd1 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -184,7 +184,10 @@ Rectangle { ListElement { text: "Lightmap"; color: "White" } ListElement { text: "Scattering"; color: "White" } ListElement { text: "Lighting"; color: "White" } - ListElement { text: "Shadow"; color: "White" } + ListElement { text: "Shadow Cascade 0"; color: "White" } + ListElement { text: "Shadow Cascade 1"; color: "White" } + ListElement { text: "Shadow Cascade 2"; color: "White" } + ListElement { text: "Shadow Cascade 3"; color: "White" } ListElement { text: "Linear Depth"; color: "White" } ListElement { text: "Half Linear Depth"; color: "White" } ListElement { text: "Half Normal"; color: "White" } From 70847a95135a4d5a09166695713c0e9447c29665 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 08:49:45 +0100 Subject: [PATCH 09/34] Wrong texture map assigned to cascade slots --- libraries/render-utils/src/LightStage.cpp | 3 +++ libraries/render-utils/src/Shadow.slh | 30 +++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index c3a4ebf137..0699838836 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -46,9 +46,12 @@ 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; _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, diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 46661b3b8a..8196decd6d 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -20,6 +20,14 @@ uniform shadowTransformBuffer { ShadowParameters shadow; }; +int getShadowCascadeCount() { + return shadow.cascadeCount; +} + +float getShadowCascadeMinDistance(int cascadeIndex) { + return shadow.cascades[cascadeIndex].minDistance; +} + mat4 getShadowReprojection(int cascadeIndex) { return shadow.cascades[cascadeIndex].reprojection; } @@ -76,12 +84,30 @@ float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position) { // If a point is not in the map, do not attenuate return 1.0; } - return evalShadowAttenuationPCF(cascadeIndex, position, shadowTexcoord); } float evalShadowAttenuation(vec4 position) { - return evalShadowCascadeAttenuation(0, position); + // Cascade selection based on : + // https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx + vec4 currentPixelDepth = position.zzzz; + vec4 cascadeDepthLimits = vec4( + getShadowCascadeMinDistance(0), + getShadowCascadeMinDistance(1), + getShadowCascadeMinDistance(2), + getShadowCascadeMinDistance(3) + ); + vec4 comparison = vec4( currentPixelDepth > cascadeDepthLimits); + int cascadeCount = getShadowCascadeCount(); + float fIndex = dot( + vec4( cascadeCount > 0, cascadeCount > 1, cascadeCount > 2, cascadeCount > 3), + comparison + ); + + fIndex = min( fIndex, cascadeCount-1 ); + int currentCascadeIndex = int(fIndex); + + return evalShadowCascadeAttenuation(currentCascadeIndex, position); } <@endif@> From dd0eaafc0da40fea29c1cd08139ec6c30da94ea5 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 12:33:46 +0100 Subject: [PATCH 10/34] Screen space dithering for PCF with some noise added --- libraries/render-utils/src/Shadow.slh | 29 ++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 8abbc1de3e..3021ea87f9 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -57,17 +57,32 @@ vec2 PCFkernel[4] = vec2[4]( vec2(0.5, -1.5) ); -float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { +float evalShadowNoise(vec4 seed) { + float dot_product = dot(seed, vec4(12.9898,78.233,45.164,94.673)); + return fract(sin(dot_product) * 43758.5453); +} + +float evalShadowAttenuationPCF(vec4 shadowTexcoord) { float shadowScale = getShadowScale(); + // Pattern dithering in screen space // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html - vec2 offset = step(fract(position.xy), vec2(0.5, 0.5)); + ivec2 coords = ivec2(gl_FragCoord.xy); +#if 1 + // 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 + ivec2 offset = coords & ivec2(1,1); + offset.y = (offset.x+offset.y) & 1; + vec2 offsetF = vec2(offset); float shadowAttenuation = (0.25 * ( - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + - fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offsetF + PCFkernel[0], 0.0)) + + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offsetF + PCFkernel[1], 0.0)) + + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offsetF + PCFkernel[2], 0.0)) + + fetchShadow(shadowTexcoord.xyz + shadowScale * vec3(offsetF + PCFkernel[3], 0.0)) )); return shadowAttenuation; @@ -82,7 +97,7 @@ float evalShadowAttenuation(vec4 position) { return 1.0; } - return evalShadowAttenuationPCF(position, shadowTexcoord); + return evalShadowAttenuationPCF(shadowTexcoord); } <@endif@> From 6611b28e2df468d8a8bd3cb8622a778f1060db5e Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 13:54:51 +0100 Subject: [PATCH 11/34] Switched to world space dithering for more visual stability --- libraries/render-utils/src/LightStage.cpp | 2 ++ libraries/render-utils/src/Shadow.slh | 14 +++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index eb712b075c..92c1e3211b 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -13,6 +13,8 @@ #include "LightStage.h" +#include + std::string LightStage::_stageName { "LIGHT_STAGE"}; const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.5, 0.0, 0.0, 0.0, diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 3021ea87f9..ceea4d1d32 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -62,18 +62,22 @@ float evalShadowNoise(vec4 seed) { return fract(sin(dot_product) * 43758.5453); } -float evalShadowAttenuationPCF(vec4 shadowTexcoord) { +float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { float shadowScale = getShadowScale(); +#if 0 // Pattern dithering in screen space - // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html ivec2 coords = ivec2(gl_FragCoord.xy); -#if 1 +#else + // Pattern dithering in world space (mm resolution) + ivec2 coords = ivec2(position.x, position.y+position.z); +#endif // 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); offset.y = (offset.x+offset.y) & 1; vec2 offsetF = vec2(offset); @@ -97,7 +101,7 @@ float evalShadowAttenuation(vec4 position) { return 1.0; } - return evalShadowAttenuationPCF(shadowTexcoord); + return evalShadowAttenuationPCF(position, shadowTexcoord); } <@endif@> From 28d46dc4a5530698c763c886c5eec745b6d1ca45 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 15:56:11 +0100 Subject: [PATCH 12/34] First alpha version of cascade selection code in shader --- libraries/render-utils/src/Shadow.slh | 52 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index e4b41bf60f..95d7b5193e 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -65,8 +65,13 @@ float evalShadowNoise(vec4 seed) { return fract(sin(dot_product) * 43758.5453); } -float evalShadowAttenuationPCF(int cascadeIndex, vec4 position, vec4 shadowTexcoord) { +struct ShadowSampleOffsets { + vec3 points[4]; +}; + +ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) { float shadowScale = getShadowScale(); + ShadowSampleOffsets offsets; #if 0 // Pattern dithering in screen space @@ -83,19 +88,28 @@ float evalShadowAttenuationPCF(int cascadeIndex, vec4 position, vec4 shadowTexco // Offset for efficient PCF, see http://http.developer.nvidia.com/GPUGems/gpugems_ch11.html ivec2 offset = coords & ivec2(1,1); offset.y = (offset.x+offset.y) & 1; - vec2 offsetF = vec2(offset); - float shadowAttenuation = (0.25 * ( - fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[0], 0.0)) + - fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[1], 0.0)) + - fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[2], 0.0)) + - fetchShadow(cascadeIndex, shadowTexcoord.xyz + shadowScale * vec3(offset + PCFkernel[3], 0.0)) - )); + offsets.points[0] = shadowScale * vec3(offset + PCFkernel[0], 0.0); + offsets.points[1] = shadowScale * vec3(offset + PCFkernel[1], 0.0); + offsets.points[2] = shadowScale * vec3(offset + PCFkernel[2], 0.0); + offsets.points[3] = shadowScale * vec3(offset + PCFkernel[3], 0.0); + + return offsets; +} + +float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { + + float shadowAttenuation = 0.25 * ( + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[0]) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[1]) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[2]) + + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[3]) + ); return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position) { +float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position, ShadowSampleOffsets offsets) { vec4 shadowTexcoord = evalShadowTexcoord(cascadeIndex, position); if (shadowTexcoord.x < 0.0 || shadowTexcoord.x > 1.0 || shadowTexcoord.y < 0.0 || shadowTexcoord.y > 1.0 || @@ -103,10 +117,12 @@ float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position) { // If a point is not in the map, do not attenuate return 1.0; } - return evalShadowAttenuationPCF(cascadeIndex, position, shadowTexcoord); + return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord); } float evalShadowAttenuation(vec4 position) { + ShadowSampleOffsets offsets = evalShadowFilterOffsets(position); + // Cascade selection based on : // https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx vec4 currentPixelDepth = position.zzzz; @@ -116,17 +132,13 @@ float evalShadowAttenuation(vec4 position) { getShadowCascadeMinDistance(2), getShadowCascadeMinDistance(3) ); - vec4 comparison = vec4( currentPixelDepth > cascadeDepthLimits); + bvec4 comparison = greaterThan( currentPixelDepth, cascadeDepthLimits); int cascadeCount = getShadowCascadeCount(); - float fIndex = dot( - vec4( cascadeCount > 0, cascadeCount > 1, cascadeCount > 2, cascadeCount > 3), - comparison - ); - - fIndex = min( fIndex, cascadeCount-1 ); - int currentCascadeIndex = int(fIndex); - - return evalShadowCascadeAttenuation(currentCascadeIndex, position); + bvec4 cascadeCountMask = greaterThan(ivec4(cascadeCount), ivec4(0,1,2,3)); + int cascadeIndex = int(dot(ivec4(cascadeCountMask), ivec4(comparison))); + cascadeIndex = min( cascadeIndex, cascadeCount-1 ); + + return evalShadowCascadeAttenuation(cascadeIndex, position, offsets); } <@endif@> From ac0e816f8cc4f68ca79f1dd8c5c816c74d2deae1 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 13 Nov 2017 18:42:34 +0100 Subject: [PATCH 13/34] Cascade selection working on shadow but not in Luci debug mode --- .../gpu-gl/src/gpu/gl/GLBackendShader.cpp | 5 +- .../render-utils/src/DebugDeferredBuffer.cpp | 43 ++++++++-- .../render-utils/src/DebugDeferredBuffer.h | 9 +- libraries/render-utils/src/LightStage.cpp | 2 + libraries/render-utils/src/LightStage.h | 4 +- .../render-utils/src/RenderShadowTask.cpp | 5 +- libraries/render-utils/src/Shadow.slh | 69 ++++----------- libraries/render-utils/src/ShadowCore.slh | 86 +++++++++++++++++++ .../src/debug_deferred_buffer.slf | 3 +- .../src/directional_ambient_light_shadow.slf | 5 +- .../src/directional_skybox_light_shadow.slf | 5 +- .../utilities/render/deferredLighting.qml | 1 + 12 files changed, 162 insertions(+), 75 deletions(-) create mode 100644 libraries/render-utils/src/ShadowCore.slh diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp index 0c1b6880cb..9adfd550ef 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendShader.cpp @@ -318,7 +318,10 @@ int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slot if (requestedBinding != slotBindings.end()) { if (binding != (*requestedBinding)._location) { binding = (*requestedBinding)._location; - glProgramUniform1i(glprogram, location, binding); + for (auto i = 0; i < size; i++) { + // If we are working with an array of textures, reserve for each elemet + glProgramUniform1i(glprogram, location+i, binding+i); + } } } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 1a95329724..55c945549c 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -126,12 +126,14 @@ static const std::string DEFAULT_DEPTH_SHADER { " return vec4(vec3(texture(depthMap, uv).x), 1.0);" " }" }; + static const std::string DEFAULT_LIGHTING_SHADER { "vec4 getFragmentColor() {" " return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);" " }" }; -static const std::string DEFAULT_SHADOW_SHADER { + +static const std::string DEFAULT_SHADOW_SHADER{ "uniform sampler2DShadow shadowMap;" "vec4 getFragmentColor() {" " for (int i = 255; i >= 0; --i) {" @@ -144,6 +146,25 @@ 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) );" + "vec4 getFragmentColor() {" + " DeferredFrameTransform deferredTransform = getDeferredFrameTransform();" + " DeferredFragment frag = unpackDeferredFragment(deferredTransform, uv);" + " vec4 viewPosition = vec4(frag.position.xyz, 1.0);" + " vec4 worldPosition = getViewInverse() * viewPosition;" + " vec4 cascadeShadowCoords[4] = vec4[4](" + " evalShadowTexcoord(0, worldPosition)," + " evalShadowTexcoord(1, worldPosition)," + " evalShadowTexcoord(2, worldPosition)," + " evalShadowTexcoord(3, worldPosition)" + " );" + " ivec2 cascadeIndices;" + " float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices);" + " return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);" + " }" +}; + static const std::string DEFAULT_LINEAR_DEPTH_SHADER { "vec4 getFragmentColor() {" " return vec4(vec3(1.0 - texture(linearDepthMap, uv).x * 0.01), 1.0);" @@ -284,11 +305,13 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust return DEFAULT_SCATTERING_SHADER; case LightingMode: return DEFAULT_LIGHTING_SHADER; - case Shadow0Mode: - case Shadow1Mode: - case Shadow2Mode: - case Shadow3Mode: + case ShadowCascade0Mode: + case ShadowCascade1Mode: + case ShadowCascade2Mode: + case ShadowCascade3Mode: return DEFAULT_SHADOW_SHADER; + case ShadowCascadeIndicesMode: + return DEFAULT_SHADOW_CASCADE_SHADER; case LinearDepthMode: return DEFAULT_LINEAR_DEPTH_SHADER; case HalfLinearDepthMode: @@ -424,8 +447,8 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I // TODO REMOVE: Temporary until UI auto first = _customPipelines.begin()->first; - - batch.setPipeline(getPipeline(_mode, first)); + auto pipeline = getPipeline(_mode, first); + batch.setPipeline(pipeline); if (deferredFramebuffer) { batch.setResourceTexture(Albedo, deferredFramebuffer->getDeferredColorTexture()); @@ -441,8 +464,12 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto lightAndShadow = lightStage->getCurrentKeyLightAndShadow(); const auto& globalShadow = lightAndShadow.second; if (globalShadow) { - const auto cascadeIndex = glm::clamp(_mode - Mode::Shadow0Mode, 0, (int)globalShadow->getCascadeCount() - 1); + 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()); + } } if (linearDepthTarget) { diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 49e53c4617..104b161720 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -64,10 +64,11 @@ protected: LightmapMode, ScatteringMode, LightingMode, - Shadow0Mode, - Shadow1Mode, - Shadow2Mode, - Shadow3Mode, + ShadowCascade0Mode, + ShadowCascade1Mode, + ShadowCascade2Mode, + ShadowCascade3Mode, + ShadowCascadeIndicesMode, LinearDepthMode, HalfLinearDepthMode, HalfNormalMode, diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index bfc4805413..1820076545 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -126,6 +126,8 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie schemaCascade.reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); schemaCascade.minDistance = viewMinShadowDistance; schemaCascade.maxDistance = viewMaxShadowDistance; + cascade.minDistance = viewMinShadowDistance; + cascade.maxDistance = viewMaxShadowDistance; } 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 60c4a49678..07ef0c25b6 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -56,6 +56,8 @@ public: gpu::FramebufferPointer framebuffer; gpu::TexturePointer map; + float minDistance; + float maxDistance; const std::shared_ptr& getFrustum() const { return _frustum; } @@ -95,8 +97,6 @@ public: }; UniformBufferView _schemaBuffer = nullptr; - - void setupCascades(); friend class Light; }; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index d9af9659e5..0c4e5d4a61 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -253,8 +253,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O output.edit0() = args->_renderMode; const auto globalShadow = lightStage->getCurrentKeyShadow(); - const auto globalShadowCascadeCount = globalShadow->getCascadeCount(); - if (globalShadow && _cascadeIndexgetCascadeCount()) { output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); const auto nearClip = args->getViewFrustum().getNearClip(); @@ -263,7 +262,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O static const float SHADOW_MAX_DISTANCE = 25.0f; static const float SHADOW_OVERLAP_DISTANCE = 1.0f; - float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadowCascadeCount - 1 - _cascadeIndex); + float maxCascadeDistance = SHADOW_MAX_DISTANCE / powf(2.0f, globalShadow->getCascadeCount() - 1 - _cascadeIndex); float minCascadeDistance = maxCascadeDistance / 2.0f - SHADOW_OVERLAP_DISTANCE; if (_cascadeIndex == 0) { diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index 95d7b5193e..18a39782b6 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -11,43 +11,11 @@ <@if not SHADOW_SLH@> <@def SHADOW_SLH@> -<@include Shadows_shared.slh@> +<@include ShadowCore.slh@> // the shadow texture uniform sampler2DShadow shadowMaps[SHADOW_CASCADE_MAX_COUNT]; -uniform shadowTransformBuffer { - ShadowParameters shadow; -}; - -int getShadowCascadeCount() { - return shadow.cascadeCount; -} - -float getShadowCascadeMinDistance(int cascadeIndex) { - return shadow.cascades[cascadeIndex].minDistance; -} - -mat4 getShadowReprojection(int cascadeIndex) { - return shadow.cascades[cascadeIndex].reprojection; -} - -float getShadowScale() { - return shadow.invMapSize; -} - -float getShadowBias(int cascadeIndex) { - return shadow.cascades[cascadeIndex].bias; -} - -// Compute the texture coordinates from world coordinates -vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { - float bias = -getShadowBias(cascadeIndex); - - vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; - return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); -} - // Sample the shadowMap with PCF (built-in) float fetchShadow(int cascadeIndex, vec3 shadowTexcoord) { return texture(shadowMaps[cascadeIndex], shadowTexcoord); @@ -109,8 +77,7 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position, ShadowSampleOffsets offsets) { - vec4 shadowTexcoord = evalShadowTexcoord(cascadeIndex, position); +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) { @@ -120,25 +87,23 @@ float evalShadowCascadeAttenuation(int cascadeIndex, vec4 position, ShadowSample return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord); } -float evalShadowAttenuation(vec4 position) { - ShadowSampleOffsets offsets = evalShadowFilterOffsets(position); - - // Cascade selection based on : - // https://msdn.microsoft.com/en-us/library/windows/desktop/ee416307(v=vs.85).aspx - vec4 currentPixelDepth = position.zzzz; - vec4 cascadeDepthLimits = vec4( - getShadowCascadeMinDistance(0), - getShadowCascadeMinDistance(1), - getShadowCascadeMinDistance(2), - getShadowCascadeMinDistance(3) +float evalShadowAttenuation(vec4 worldPosition, vec4 viewPosition) { + ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); + vec4 cascadeShadowCoords[4] = vec4[4] ( + evalShadowTexcoord(0, worldPosition), + evalShadowTexcoord(1, worldPosition), + evalShadowTexcoord(2, worldPosition), + evalShadowTexcoord(3, worldPosition) ); - bvec4 comparison = greaterThan( currentPixelDepth, cascadeDepthLimits); - int cascadeCount = getShadowCascadeCount(); - bvec4 cascadeCountMask = greaterThan(ivec4(cascadeCount), ivec4(0,1,2,3)); - int cascadeIndex = int(dot(ivec4(cascadeCountMask), ivec4(comparison))); - cascadeIndex = min( cascadeIndex, cascadeCount-1 ); + ivec2 cascadeIndices; + float cascadeMix = evalCascadeIndicesAndMix(viewPosition, cascadeShadowCoords, cascadeIndices); - return evalShadowCascadeAttenuation(cascadeIndex, position, offsets); + vec2 cascadeAttenuations = vec2(1.0, 1.0); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[cascadeIndices.x]); + if (cascadeMix > 0.0) { + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[cascadeIndices.y]); + } + return mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); } <@endif@> diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh new file mode 100644 index 0000000000..3e2fc3a8b8 --- /dev/null +++ b/libraries/render-utils/src/ShadowCore.slh @@ -0,0 +1,86 @@ + +<@if not SHADOW_CORE_SLH@> +<@def SHADOW_CORE_SLH@> + +<@include Shadows_shared.slh@> + +uniform shadowTransformBuffer { + ShadowParameters shadow; +}; + +int getShadowCascadeCount() { + return shadow.cascadeCount; +} + +float getShadowCascadeMinDistance(int cascadeIndex) { + return shadow.cascades[cascadeIndex].minDistance; +} + +mat4 getShadowReprojection(int cascadeIndex) { + return shadow.cascades[cascadeIndex].reprojection; +} + +float getShadowScale() { + return shadow.invMapSize; +} + +float getShadowBias(int cascadeIndex) { + return shadow.cascades[cascadeIndex].bias; +} + +// Compute the texture coordinates from world coordinates +vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { + float bias = -getShadowBias(cascadeIndex); + + vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; + return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); +} + +int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) { + int cascadeIndex; + for (cascadeIndex=0 ; cascadeIndex diff --git a/libraries/render-utils/src/debug_deferred_buffer.slf b/libraries/render-utils/src/debug_deferred_buffer.slf index e9750f0054..426de623a1 100644 --- a/libraries/render-utils/src/debug_deferred_buffer.slf +++ b/libraries/render-utils/src/debug_deferred_buffer.slf @@ -16,7 +16,6 @@ <@include gpu/Color.slh@> <$declareColorWheel()$> - uniform sampler2D linearDepthMap; uniform sampler2D halfLinearDepthMap; uniform sampler2D halfNormalMap; @@ -24,6 +23,8 @@ uniform sampler2D occlusionMap; uniform sampler2D occlusionBlurredMap; uniform sampler2D scatteringMap; +<@include ShadowCore.slh@> + <$declareDeferredCurvature()$> float curvatureAO(float k) { diff --git a/libraries/render-utils/src/directional_ambient_light_shadow.slf b/libraries/render-utils/src/directional_ambient_light_shadow.slf index d3778c9228..5f71809249 100644 --- a/libraries/render-utils/src/directional_ambient_light_shadow.slf +++ b/libraries/render-utils/src/directional_ambient_light_shadow.slf @@ -26,8 +26,9 @@ void main(void) { DeferredFrameTransform deferredTransform = getDeferredFrameTransform(); DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - vec4 worldPos = getViewInverse() * vec4(frag.position.xyz, 1.0); - float shadowAttenuation = evalShadowAttenuation(worldPos); + vec4 viewPos = vec4(frag.position.xyz, 1.0); + vec4 worldPos = getViewInverse() * viewPos; + float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos); 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 3ca0f71df5..5c549df95d 100644 --- a/libraries/render-utils/src/directional_skybox_light_shadow.slf +++ b/libraries/render-utils/src/directional_skybox_light_shadow.slf @@ -26,8 +26,9 @@ void main(void) { DeferredFrameTransform deferredTransform = getDeferredFrameTransform(); DeferredFragment frag = unpackDeferredFragment(deferredTransform, _texCoord0); - vec4 worldPos = getViewInverse() * vec4(frag.position.xyz, 1.0); - float shadowAttenuation = evalShadowAttenuation(worldPos); + vec4 viewPos = vec4(frag.position.xyz, 1.0); + vec4 worldPos = getViewInverse() * viewPos; + float shadowAttenuation = evalShadowAttenuation(worldPos, viewPos); // Light mapped or not ? if (frag.mode == FRAG_MODE_UNLIT) { diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index def8b1fcd1..1da7871172 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -188,6 +188,7 @@ Rectangle { ListElement { text: "Shadow Cascade 1"; color: "White" } ListElement { text: "Shadow Cascade 2"; color: "White" } ListElement { text: "Shadow Cascade 3"; color: "White" } + ListElement { text: "Shadow Cascade Indices"; color: "White" } ListElement { text: "Linear Depth"; color: "White" } ListElement { text: "Half Linear Depth"; color: "White" } ListElement { text: "Half Normal"; color: "White" } From 010104d42aeb8d82548a9e330ee78c8ac59a4e1b Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 10:11:39 +0100 Subject: [PATCH 14/34] Switched off noise and switched back to screen space dither --- libraries/render-utils/src/Shadow.slh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index ceea4d1d32..66d2937deb 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -11,6 +11,9 @@ <@if not SHADOW_SLH@> <@def SHADOW_SLH@> +#define SHADOW_NOISE_ENABLED 0 +#define SHADOW_SCREEN_SPACE_DITHER 1 + // the shadow texture uniform sampler2DShadow shadowMap; @@ -65,17 +68,20 @@ float evalShadowNoise(vec4 seed) { float evalShadowAttenuationPCF(vec4 position, vec4 shadowTexcoord) { float shadowScale = getShadowScale(); -#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); From 74b0b52edb6877e5d3c288a59d92b2c9741599a0 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 16:57:22 +0100 Subject: [PATCH 15/34] 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) { From d14ebdc0e137341e00c7eed7155def480a5a9b44 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 18:53:50 +0100 Subject: [PATCH 16/34] Adjusted cascade partitions --- .../render-utils/src/DebugDeferredBuffer.cpp | 1 - .../render-utils/src/RenderShadowTask.cpp | 21 ++++++++++++------- libraries/render-utils/src/ShadowCore.slh | 5 ++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 7d316d6e56..eda6c69f88 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -497,7 +497,6 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I const glm::vec2 topRight(_size.z, _size.w); geometryBuffer->renderQuad(batch, bottomLeft, topRight, color, _geometryId); - batch.setResourceTexture(Albedo, nullptr); batch.setResourceTexture(Normal, nullptr); batch.setResourceTexture(Specular, nullptr); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 88b3fe9013..c140d85560 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -260,16 +260,23 @@ 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 = 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; + static const float HIGH_CASCADE_MAX_DISTANCE = 20.0f; + float maxCascadeDistance = HIGH_CASCADE_MAX_DISTANCE; + float minCascadeDistance = nearClip; + float shadowOverlapDistance = 0.0f; + if (globalShadow->getCascadeCount() > 1) { + static const float LOW_CASCADE_MAX_DISTANCE = 2.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); + minCascadeDistance = maxCascadeDistance / cascadeLevelScale; + } + + shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; + maxCascadeDistance += shadowOverlapDistance; if (_cascadeIndex == 0) { minCascadeDistance = nearDepth; - } else { - minCascadeDistance -= shadowOverlapDistance; } minCascadeDistance = std::max(minCascadeDistance, nearDepth); maxCascadeDistance = std::min(maxCascadeDistance, farClip); diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 6408ad472a..e548af6a79 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -43,10 +43,9 @@ float getShadowBias(int cascadeIndex) { // Compute the texture coordinates from world coordinates vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { - float bias = -getShadowBias(cascadeIndex); - vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; - return vec4(shadowCoord.xy, shadowCoord.z + bias, 1.0); + float bias = getShadowBias(cascadeIndex); + return vec4(shadowCoord.xy, shadowCoord.z - bias, 1.0); } int getFirstValidShadowTexcoord(vec4 cascadeShadowCoords[4]) { From 2a192475304a1894af9164b4f1c9a27749c8afce Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 14 Nov 2017 19:09:35 +0100 Subject: [PATCH 17/34] Switched back to 3 cascades --- libraries/render-utils/src/LightStage.cpp | 4 +++- libraries/render-utils/src/LightStage.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 6808c75914..7c5ea817b1 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -21,7 +21,9 @@ 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{ 4 }; +const int LightStage::Shadow::MAP_SIZE = 1024; + +const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 3 }; const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 9f395c905a..41a6ce945e 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -46,7 +46,7 @@ public: class Shadow { public: using UniformBufferView = gpu::BufferView; - static const int MAP_SIZE = 1024; + static const int MAP_SIZE; class Cascade { friend Shadow; From e574be785573c84a60ff486e969403cac522f031 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 16 Nov 2017 10:00:32 +0100 Subject: [PATCH 18/34] Fixed potential mismatch between push/popViewFrustum in shadow task --- libraries/render-utils/src/RenderShadowTask.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 7a6e3dc74f..2dd6c5d44f 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -263,7 +263,10 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { RenderArgs* args = renderContext->args; - // Reset the render args - args->popViewFrustum(); + if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE) { + // Reset the render args + args->popViewFrustum(); + } + assert(args->hasViewFrustum()); args->_renderMode = input; }; From d2c28c3c2163be31fc5660ee9aba47627a0e28a8 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 16 Nov 2017 10:49:59 +0100 Subject: [PATCH 19/34] Fixed disappearing shadow bug each time a domain was automatically reloaded --- libraries/render-utils/src/LightStage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 188cb46157..f0aa029646 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -147,7 +147,7 @@ LightStage::Index LightStage::addLight(const LightPointer& light) { _descs.emplace_back(Desc()); } else { assert(_descs[lightId].shadowId == INVALID_INDEX); - _descs.emplace(_descs.begin() + lightId, Desc()); + _descs[lightId] = Desc(); } // INsert the light and its index in the reverese map From 917ce7165c6e028009eea93c3a8f73f4ffad3f35 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 16 Nov 2017 15:36:48 +0100 Subject: [PATCH 20/34] Working shadow cascade debugging view in Luci app --- .../render-utils/src/DebugDeferredBuffer.cpp | 4 +-- .../render-utils/src/RenderDeferredTask.cpp | 32 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index eda6c69f88..3902f4d9d4 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -166,13 +166,13 @@ static const std::string DEFAULT_SHADOW_CASCADE_SHADER{ " ivec2 cascadeIndices;" " float cascadeMix = evalCascadeIndicesAndMix(-viewPosition.z, cascadeShadowCoords, cascadeIndices);" " return vec4(mix(cascadeColors[cascadeIndices.x], cascadeColors[cascadeIndices.y], cascadeMix), 1.0);" - " }" + "}" }; static const std::string DEFAULT_LINEAR_DEPTH_SHADER { "vec4 getFragmentColor() {" " return vec4(vec3(1.0 - texture(linearDepthMap, uv).x * 0.01), 1.0);" - " }" + "}" }; static const std::string DEFAULT_HALF_LINEAR_DEPTH_SHADER{ diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 978af72ad5..d2606bf42f 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -211,22 +211,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawSelectionBounds", selectedItems); } - // Layered Overlays - const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT); - const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT); - const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN(0); - const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN(0); - - const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel).asVarying(); - const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel).asVarying(); - task.addJob("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true); - task.addJob("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false); - - { // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer - task.addJob("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque); - task.addJob("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent); - } - // Debugging stages { // Debugging Deferred buffer job @@ -257,6 +241,22 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawZoneStack", deferredFrameTransform); } + // Layered Overlays + const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT); + const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT); + const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN(0); + const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN(0); + + const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel).asVarying(); + const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel).asVarying(); + task.addJob("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true); + task.addJob("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false); + + { // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer + task.addJob("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque); + task.addJob("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent); + } + // AA job to be revisited task.addJob("Antialiasing", primaryFramebuffer); From b246c479e36d2d5ee4addbba037780d6242e9425 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 17 Nov 2017 11:25:15 +0100 Subject: [PATCH 21/34] 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@> From d98dfff0a840d4c4392185b66c067b45edbb4618 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 17 Nov 2017 19:25:21 +0100 Subject: [PATCH 22/34] Still working on solving that underestimated shadow far plane on the first cascade. I have finally understood the problem (see TODO in LightStage) --- libraries/render-utils/src/LightStage.cpp | 5 +- .../render-utils/src/RenderDeferredTask.cpp | 4 +- .../render-utils/src/RenderShadowTask.cpp | 9 +- libraries/render-utils/src/ShadowCore.slh | 4 +- libraries/render/src/render/DrawTask.cpp | 185 +++++++++++++----- libraries/render/src/render/DrawTask.h | 54 +++-- libraries/shared/src/GeometryUtil.cpp | 2 +- scripts/developer/utilities/render/shadow.qml | 29 ++- 8 files changed, 212 insertions(+), 80 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 38bbc2ceb9..e9cc7d93b1 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -116,9 +116,12 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie fitFrustum(farCorners.bottomRight); fitFrustum(farCorners.topLeft); fitFrustum(farCorners.topRight); + + // TODO: Far distance should be extended to the intersection of the exteruded shadow frustum far plane + // with the view frustum. // Re-adjust near shadow distance - auto near = glm::max(max.z, -nearDepth); + auto near = glm::min(-max.z, nearDepth); auto far = -min.z; glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, near, far); cascade._frustum->setProjection(ortho); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index d2606bf42f..8bd5ae8447 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -204,7 +204,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren for (auto i = 0; i < ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT; i++) { const auto shadowFrustum = frustums.getN(ExtractFrustums::SHADOW_CASCADE0_FRUSTUM+i); float tint = 1.0f - i / float(ExtractFrustums::SHADOW_CASCADE_FRUSTUM_COUNT - 1); - task.addJob("DrawShadowFrustum", shadowFrustum, glm::vec3(0.0f, tint, 1.0f)); + char jobName[64]; + sprintf(jobName, "DrawShadowFrustum%d", i); + task.addJob(jobName, shadowFrustum, glm::vec3(0.0f, tint, 1.0f)); } // Render.getConfig("RenderMainView.DrawSelectionBounds").enabled = true diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index fef5cdddbc..6b00a14d23 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -112,8 +112,7 @@ static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum float far = 0.0f; computeNearFar(sceneBoundVertices, shadowClipPlanes, near, far); - // Limit the far range to the one used originally. There's no point in rendering objects - // that are not in the view frustum. + // Limit the far range to the one used originally. far = glm::min(far, shadowFrustum.getFarClip()); const auto depthEpsilon = 0.1f; @@ -233,7 +232,6 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende // GPU jobs: Render to shadow map task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i); - task.addJob("ShadowTeardown", setupOutput); } } @@ -272,12 +270,13 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O minCascadeDistance = maxCascadeDistance / cascadeLevelScale; } - shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; + shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 4.0f; maxCascadeDistance += shadowOverlapDistance; if (_cascadeIndex == 0) { minCascadeDistance = nearClip; + } else { + minCascadeDistance = std::max(minCascadeDistance, nearClip); } - 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/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 8ad972316a..0a34cafe6f 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -49,8 +49,8 @@ vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { } bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { - bvec3 greaterThanZero = greaterThanEqual(cascadeTexCoords.xyz, vec3(0)); - bvec3 lessThanOne = lessThanEqual(cascadeTexCoords.xyz, vec3(1)); + bvec2 greaterThanZero = greaterThanEqual(cascadeTexCoords.xy, vec2(0)); + bvec2 lessThanOne = lessThanEqual(cascadeTexCoords.xy, vec2(1)); return all(greaterThanZero) && all(lessThanOne); } diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 0f4137e38d..629cc55ccb 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -215,79 +215,158 @@ void DrawBounds::run(const RenderContextPointer& renderContext, }); } -gpu::PipelinePointer DrawFrustum::_pipeline; +DrawQuadVolume::DrawQuadVolume(const glm::vec3& color) : + _color{ color } { + _meshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); + _meshStream.addBuffer(_meshVertices._buffer, _meshVertices._offset, _meshVertices._stride); +} + +void DrawQuadVolume::configure(const Config& configuration) { + _isUpdateEnabled = !configuration.isFrozen; +} + +void DrawQuadVolume::run(const render::RenderContextPointer& renderContext, const glm::vec3 vertices[8], + const gpu::BufferView& indices, int indexCount) { + assert(renderContext->args); + assert(renderContext->args->_context); + if (_isUpdateEnabled) { + auto& streamVertices = _meshVertices.edit >(); + std::copy(vertices, vertices + 8, streamVertices.begin()); + } + + RenderArgs* args = renderContext->args; + gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + batch.setPipeline(getPipeline()); + batch.setIndexBuffer(indices); + + batch._glUniform4f(0, _color.x, _color.y, _color.z, 1.0f); + batch.setInputStream(0, _meshStream); + batch.drawIndexed(gpu::LINES, indexCount, 0U); + + args->_batch = nullptr; + }); +} + +gpu::PipelinePointer DrawQuadVolume::getPipeline() { + static gpu::PipelinePointer pipeline; + + if (!pipeline) { + auto vs = gpu::StandardShaderLib::getDrawTransformVertexPositionVS(); + auto ps = gpu::StandardShaderLib::getDrawColorPS(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding("color", 0)); + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + state->setDepthTest(gpu::State::DepthTest(true, false)); + pipeline = gpu::Pipeline::create(program, state); + } + return pipeline; +} + +gpu::BufferView DrawAABox::_cubeMeshIndices; + +DrawAABox::DrawAABox(const glm::vec3& color) : + DrawQuadVolume{ color } { +} + +void DrawAABox::run(const render::RenderContextPointer& renderContext, const Inputs& box) { + if (!box.isNull()) { + static const uint8_t indexData[] = { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + 4, 5, + 5, 6, + 6, 7, + 7, 4, + 0, 4, + 1, 5, + 3, 7, + 2, 6 + }; + + if (!_cubeMeshIndices._buffer) { + auto indices = std::make_shared(sizeof(indexData), indexData); + _cubeMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX)); + } + + glm::vec3 vertices[8]; + + getVertices(box, vertices); + + DrawQuadVolume::run(renderContext, vertices, _cubeMeshIndices, sizeof(indexData) / sizeof(indexData[0])); + } +} + +void DrawAABox::getVertices(const AABox& box, glm::vec3 vertices[8]) { + vertices[0] = box.getVertex(TOP_LEFT_NEAR); + vertices[1] = box.getVertex(TOP_RIGHT_NEAR); + vertices[2] = box.getVertex(BOTTOM_RIGHT_NEAR); + vertices[3] = box.getVertex(BOTTOM_LEFT_NEAR); + vertices[4] = box.getVertex(TOP_LEFT_FAR); + vertices[5] = box.getVertex(TOP_RIGHT_FAR); + vertices[6] = box.getVertex(BOTTOM_RIGHT_FAR); + vertices[7] = box.getVertex(BOTTOM_LEFT_FAR); +} + gpu::BufferView DrawFrustum::_frustumMeshIndices; DrawFrustum::DrawFrustum(const glm::vec3& color) : - _color{ color } { - _frustumMeshVertices = gpu::BufferView(std::make_shared(sizeof(glm::vec3) * 8, nullptr), gpu::Element::VEC3F_XYZ); - _frustumMeshStream.addBuffer(_frustumMeshVertices._buffer, _frustumMeshVertices._offset, _frustumMeshVertices._stride); -} - -void DrawFrustum::configure(const Config& configuration) { - _updateFrustum = !configuration.isFrozen; + DrawQuadVolume{ color } { } void DrawFrustum::run(const render::RenderContextPointer& renderContext, const Input& input) { - assert(renderContext->args); - assert(renderContext->args->_context); - - RenderArgs* args = renderContext->args; if (input) { const auto& frustum = *input; - static uint8_t indexData[] = { 0, 1, 2, 3, 0, 4, 5, 6, 7, 4, 5, 1, 2, 6, 7, 3 }; + static const uint8_t indexData[] = { + 0, 1, + 1, 2, + 2, 3, + 3, 0, + 0, 2, + 3, 1, + 4, 5, + 5, 6, + 6, 7, + 7, 4, + 4, 6, + 7, 5, + 0, 4, + 1, 5, + 3, 7, + 2, 6 + }; if (!_frustumMeshIndices._buffer) { auto indices = std::make_shared(sizeof(indexData), indexData); _frustumMeshIndices = gpu::BufferView(indices, gpu::Element(gpu::SCALAR, gpu::UINT8, gpu::INDEX)); } - if (!_pipeline) { - auto vs = gpu::StandardShaderLib::getDrawTransformVertexPositionVS(); - auto ps = gpu::StandardShaderLib::getDrawColorPS(); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + glm::vec3 vertices[8]; - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding("color", 0)); - gpu::Shader::makeProgram(*program, slotBindings); + getVertices(frustum, vertices); - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - state->setDepthTest(gpu::State::DepthTest(true, false)); - _pipeline = gpu::Pipeline::create(program, state); - } - - if (_updateFrustum) { - updateFrustum(frustum); - } - - // Render the frustums in wireframe - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - batch.setPipeline(_pipeline); - batch.setIndexBuffer(_frustumMeshIndices); - - batch._glUniform4f(0, _color.x, _color.y, _color.z, 1.0f); - batch.setInputStream(0, _frustumMeshStream); - batch.drawIndexed(gpu::LINE_STRIP, sizeof(indexData) / sizeof(indexData[0]), 0U); - - args->_batch = nullptr; - }); + DrawQuadVolume::run(renderContext, vertices, _frustumMeshIndices, sizeof(indexData) / sizeof(indexData[0])); } } -void DrawFrustum::updateFrustum(const ViewFrustum& frustum) { - auto& vertices = _frustumMeshVertices.edit >(); +void DrawFrustum::getVertices(const ViewFrustum& frustum, glm::vec3 vertices[8]) { vertices[0] = frustum.getNearTopLeft(); vertices[1] = frustum.getNearTopRight(); vertices[2] = frustum.getNearBottomRight(); diff --git a/libraries/render/src/render/DrawTask.h b/libraries/render/src/render/DrawTask.h index 5d98c37c21..6f98e3bef1 100755 --- a/libraries/render/src/render/DrawTask.h +++ b/libraries/render/src/render/DrawTask.h @@ -70,12 +70,12 @@ private: int _colorLocation { -1 }; }; -class DrawFrustumConfig : public render::JobConfig { +class DrawQuadVolumeConfig : public render::JobConfig { Q_OBJECT Q_PROPERTY(bool isFrozen MEMBER isFrozen NOTIFY dirty) public: - DrawFrustumConfig(bool enabled = false) : JobConfig(enabled) {} + DrawQuadVolumeConfig(bool enabled = false) : JobConfig(enabled) {} bool isFrozen{ false }; signals: @@ -83,30 +83,58 @@ signals: }; -class DrawFrustum { +class DrawQuadVolume { +public: + + using Config = DrawQuadVolumeConfig; + + void configure(const Config& configuration); + +protected: + DrawQuadVolume(const glm::vec3& color); + + void run(const render::RenderContextPointer& renderContext, const glm::vec3 vertices[8], + const gpu::BufferView& indices, int indexCount); + + gpu::BufferView _meshVertices; + gpu::BufferStream _meshStream; + glm::vec3 _color; + bool _isUpdateEnabled{ true }; + + static gpu::PipelinePointer getPipeline(); +}; + +class DrawAABox : public DrawQuadVolume { +public: + using Inputs = AABox; + using JobModel = render::Job::ModelI; + + DrawAABox(const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f)); + + void run(const render::RenderContextPointer& renderContext, const Inputs& box); + +protected: + + static gpu::BufferView _cubeMeshIndices; + + static void getVertices(const AABox& box, glm::vec3 vertices[8]); +}; + +class DrawFrustum : public DrawQuadVolume { public: - using Config = DrawFrustumConfig; using Input = ViewFrustumPointer; using JobModel = render::Job::ModelI; DrawFrustum(const glm::vec3& color = glm::vec3(1.0f, 1.0f, 1.0f)); - void configure(const Config& configuration); void run(const render::RenderContextPointer& renderContext, const Input& input); private: - static gpu::PipelinePointer _pipeline; static gpu::BufferView _frustumMeshIndices; - bool _updateFrustum{ true }; - gpu::BufferView _frustumMeshVertices; - gpu::BufferStream _frustumMeshStream; - glm::vec3 _color; - - void updateFrustum(const ViewFrustum& frustum); + static void getVertices(const ViewFrustum& frustum, glm::vec3 vertices[8]); }; - } #endif // hifi_render_DrawTask_h diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 2ee761a1f7..956c61deaf 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -422,7 +422,7 @@ int clipTriangleWithPlanes(const Triangle& triangle, const Plane* planes, int pl *clippedTriangles = triangle; - while (planes < planesEnd) { + while (planes < planesEnd && triangleCount) { int clippedSubTriangleCount; trianglesToTest.clear(); diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index 8548ba4119..a8fcf1aec7 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -15,15 +15,24 @@ Column { id: root spacing: 8 property var viewConfig: Render.getConfig("RenderMainView.DrawViewFrustum"); - property var shadowConfig: Render.getConfig("RenderMainView.DrawShadowFrustum"); + property var shadow0Config: Render.getConfig("RenderMainView.DrawShadowFrustum0"); + property var shadow1Config: Render.getConfig("RenderMainView.DrawShadowFrustum1"); + property var shadow2Config: Render.getConfig("RenderMainView.DrawShadowFrustum2"); + property var shadow3Config: Render.getConfig("RenderMainView.DrawShadowFrustum3"); Component.onCompleted: { viewConfig.enabled = true; - shadowConfig.enabled = true; + shadow0Config.enabled = true; + shadow1Config.enabled = true; + shadow2Config.enabled = true; + shadow3Config.enabled = true; } Component.onDestruction: { viewConfig.enabled = false; - shadowConfig.enabled = false; + shadow0Config.enabled = false; + shadow1Config.enabled = false; + shadow2Config.enabled = false; + shadow3Config.enabled = false; } CheckBox { @@ -31,7 +40,14 @@ Column { checked: false onCheckedChanged: { viewConfig.isFrozen = checked; - shadowConfig.isFrozen = checked; + shadow0Config.isFrozen = checked; + shadow1Config.isFrozen = checked; + shadow2Config.isFrozen = checked; + shadow3Config.isFrozen = checked; + shadow0BoundConfig.isFrozen = checked; + shadow1BoundConfig.isFrozen = checked; + shadow2BoundConfig.isFrozen = checked; + shadow3BoundConfig.isFrozen = checked; } } Row { @@ -46,5 +62,10 @@ Column { color: "blue" font.italic: true } + Label { + text: "Items" + color: "magenta" + font.italic: true + } } } From c0ca7a129d2cff060ad73b7e8ed367fa28159dc1 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 5 Dec 2017 17:33:11 +0100 Subject: [PATCH 23/34] Fixed issue with far distance of cascades being underestimated, especially first cascade --- libraries/render-utils/src/LightStage.cpp | 68 +++++++++---- libraries/render-utils/src/LightStage.h | 6 +- .../render-utils/src/RenderShadowTask.cpp | 22 +---- libraries/shared/src/ViewFrustum.cpp | 97 ++++++++++++++++++- libraries/shared/src/ViewFrustum.h | 14 ++- 5 files changed, 168 insertions(+), 39 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 5562e0fa2d..28a54cefca 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -53,6 +53,41 @@ const glm::mat4& LightStage::Shadow::Cascade::getProjection() const { return _frustum->getProjection(); } +float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse, + float left, float right, float bottom, float top, float viewMaxShadowDistance) const { + // Far distance should be extended to the intersection of the infinitely extruded shadow frustum + // with the view frustum side planes. To do so, we generate 10 triangles in shadow space which are the result of + // tesselating the side and far faces of the view frustum and clip them with the 4 side planes of the + // shadow frustum. The resulting clipped triangle vertices with the farthest Z gives the desired + // shadow frustum far distance. + std::array viewFrustumTriangles; + Plane shadowClipPlanes[4] = { + Plane(glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, top, 0.0f)), + Plane(glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, bottom, 0.0f)), + Plane(glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(left, 0.0f, 0.0f)), + Plane(glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(right, 0.0f, 0.0f)) + }; + + viewFrustum.tesselateSidesAndFar(shadowViewInverse, viewFrustumTriangles.data(), viewMaxShadowDistance); + + static const int MAX_TRIANGLE_COUNT = 16; + auto far = 0.0f; + + for (auto& triangle : viewFrustumTriangles) { + Triangle clippedTriangles[MAX_TRIANGLE_COUNT]; + auto clippedTriangleCount = clipTriangleWithPlanes(triangle, shadowClipPlanes, 4, clippedTriangles, MAX_TRIANGLE_COUNT); + + for (auto i = 0; i < clippedTriangleCount; i++) { + const auto& clippedTriangle = clippedTriangles[i]; + far = glm::max(far, -clippedTriangle.v0.z); + far = glm::max(far, -clippedTriangle.v1.z); + far = glm::max(far, -clippedTriangle.v2.z); + } + } + + return far; +} + LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) : _light{ light } { cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); Schema schema; @@ -62,11 +97,12 @@ LightStage::Shadow::Shadow(model::LightPointer light, unsigned int cascadeCount) } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance, + float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance, + float viewCascadeOverlapDistance, float viewMaxShadowDistance, float nearDepth, float farDepth) { - assert(viewMinShadowDistance < viewMaxShadowDistance); + assert(viewMinCascadeShadowDistance < viewMaxCascadeShadowDistance); assert(nearDepth < farDepth); - assert(viewOverlapDistance > 0.0f); + assert(viewCascadeOverlapDistance > 0.0f); assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum @@ -89,17 +125,17 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Position the keylight frustum cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*direction); - const Transform view{ cascade._frustum->getView()}; - const Transform viewInverse{ view.getInverseMatrix() }; + const Transform shadowView{ cascade._frustum->getView()}; + const Transform shadowViewInverse{ shadowView.getInverseMatrix() }; - auto nearCorners = viewFrustum.getCorners(viewMinShadowDistance); - auto farCorners = viewFrustum.getCorners(viewMaxShadowDistance); + auto nearCorners = viewFrustum.getCorners(viewMinCascadeShadowDistance); + auto farCorners = viewFrustum.getCorners(viewMaxCascadeShadowDistance); - vec3 min{ viewInverse.transform(nearCorners.bottomLeft) }; + vec3 min{ shadowViewInverse.transform(nearCorners.bottomLeft) }; vec3 max{ min }; // Expand keylight frustum to fit view frustum - auto fitFrustum = [&min, &max, &viewInverse](const vec3& viewCorner) { - const auto corner = viewInverse.transform(viewCorner); + auto fitFrustum = [&min, &max, &shadowViewInverse](const vec3& viewCorner) { + const auto corner = shadowViewInverse.transform(viewCorner); min.x = glm::min(min.x, corner.x); min.y = glm::min(min.y, corner.y); @@ -116,13 +152,11 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie fitFrustum(farCorners.bottomRight); fitFrustum(farCorners.topLeft); fitFrustum(farCorners.topRight); - - // TODO: Far distance should be extended to the intersection of the exteruded shadow frustum far plane - // with the view frustum. // Re-adjust near shadow distance auto near = glm::min(-max.z, nearDepth); - auto far = -min.z; + auto far = cascade.computeFarDistance(viewFrustum, shadowViewInverse, min.x, max.x, min.y, max.y, viewMaxShadowDistance); + glm::mat4 ortho = glm::ortho(min.x, max.x, min.y, max.y, near, far); cascade._frustum->setProjection(ortho); @@ -132,10 +166,10 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Update the buffer auto& schema = _schemaBuffer.edit(); if (cascadeIndex == getCascadeCount() - 1) { - schema.maxDistance = viewMaxShadowDistance; - schema.invFalloffDistance = 1.0f / viewOverlapDistance; + schema.maxDistance = viewMaxCascadeShadowDistance; + schema.invFalloffDistance = 1.0f / viewCascadeOverlapDistance; } - schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * viewInverse.getMatrix(); + schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.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 0a0a67f591..7cf0961e3a 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -65,12 +65,16 @@ public: private: std::shared_ptr _frustum; + + 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); void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float viewMinShadowDistance, float viewMaxShadowDistance, float viewOverlapDistance, + float viewMinCascadeShadowDistance, float viewMaxCascadeShadowDistance, + float viewCascadeOverlapDistance, float viewMaxShadowDistance, float nearDepth = 1.0f, float farDepth = 1000.0f); void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 6b00a14d23..c5ed26823a 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -89,24 +89,7 @@ static void adjustNearFar(const AABox& inShapeBounds, ViewFrustum& shadowFrustum for (i = 0; i < 8; i++) { sceneBoundVertices[i] = shadowViewInverse.transform(inShapeBounds.getVertex(static_cast(i))); } - // This indirection array is just a protection in case the ViewFrustum::PlaneIndex enum - // changes order especially as we don't need to test the NEAR and FAR planes. - static const ViewFrustum::PlaneIndex planeIndices[4] = { - ViewFrustum::TOP_PLANE, - ViewFrustum::BOTTOM_PLANE, - ViewFrustum::LEFT_PLANE, - ViewFrustum::RIGHT_PLANE - }; - // Same goes for the shadow frustum planes. - for (i = 0; i < 4; i++) { - const auto& worldPlane = shadowFrustum.getPlanes()[planeIndices[i]]; - // We assume the transform doesn't have a non uniform scale component to apply the - // transform to the normal without using the correct transpose of inverse, which should be the - // case for a view matrix. - auto planeNormal = shadowViewInverse.transformDirection(worldPlane.getNormal()); - auto planePoint = shadowViewInverse.transform(worldPlane.getPoint()); - shadowClipPlanes[i].setNormalAndPoint(planeNormal, planePoint); - } + shadowFrustum.getUniformlyTransformedSidePlanes(shadowViewInverse, shadowClipPlanes); float near = std::numeric_limits::max(); float far = 0.0f; @@ -278,7 +261,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O minCascadeDistance = std::max(minCascadeDistance, nearClip); } maxCascadeDistance = std::min(maxCascadeDistance, farClip); - globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, shadowOverlapDistance, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), minCascadeDistance, maxCascadeDistance, + shadowOverlapDistance, HIGH_CASCADE_MAX_DISTANCE, SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index dcbfd83ec7..37c01510f3 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -691,7 +691,7 @@ void ViewFrustum::getFurthestPointFromCamera(const AACube& box, glm::vec3& furth } } -const ViewFrustum::Corners ViewFrustum::getCorners(const float& depth) const { +const ViewFrustum::Corners ViewFrustum::getCorners(const float depth) const { glm::vec3 normal = glm::normalize(_direction); auto getCorner = [&](enum::BoxVertex nearCorner, enum::BoxVertex farCorner) { @@ -750,3 +750,98 @@ void ViewFrustum::invalidate() { } _centerSphereRadius = -1.0e6f; // -10^6 should be negative enough } + +void ViewFrustum::getSidePlanes(::Plane planes[4]) const { + planes[0] = _planes[TOP_PLANE]; + planes[1] = _planes[BOTTOM_PLANE]; + planes[2] = _planes[LEFT_PLANE]; + planes[3] = _planes[RIGHT_PLANE]; +} + +void ViewFrustum::getTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const { + glm::mat4 normalTransform; + transform.getInverseTransposeMatrix(normalTransform); + getSidePlanes(planes); + for (auto i = 0; i < 4; i++) { + // We assume the transform doesn't have a non uniform scale component to apply the + // transform to the normal without using the correct transpose of inverse. + auto transformedNormal = normalTransform * Transform::Vec4(planes[i].getNormal(), 0.0f); + auto planePoint = transform.transform(planes[i].getPoint()); + glm::vec3 planeNormal(transformedNormal.x, transformedNormal.y, transformedNormal.z); + planes[i].setNormalAndPoint(planeNormal, planePoint); + } +} + +void ViewFrustum::getUniformlyTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const { + getSidePlanes(planes); + for (auto i = 0; i < 4; i++) { + // We assume the transform doesn't have a non uniform scale component to apply the + // transform to the normal without using the correct transpose of inverse. + auto planeNormal = transform.transformDirection(planes[i].getNormal()); + auto planePoint = transform.transform(planes[i].getPoint()); + planes[i].setNormalAndPoint(planeNormal, planePoint); + } +} + +void ViewFrustum::tesselateSides(Triangle triangles[8]) const { + tesselateSides(_cornersWorld, triangles); +} + +void ViewFrustum::tesselateSides(const Transform& transform, Triangle triangles[8]) const { + glm::vec3 points[8]; + + for (auto i = 0; i < 8; i++) { + points[i] = transform.transform(_cornersWorld[i]); + } + + tesselateSides(points, triangles); +} + +void ViewFrustum::tesselateSidesAndFar(const Transform& transform, Triangle triangles[10], float farDistance) const { + glm::vec3 points[8]; + + // First 4 points are at near + for (auto i = 0; i < 4; i++) { + points[i] = transform.transform(_cornersWorld[i]); + } + auto farCorners = getCorners(farDistance); + + points[BOTTOM_LEFT_FAR] = transform.transform(farCorners.bottomLeft); + points[BOTTOM_RIGHT_FAR] = transform.transform(farCorners.bottomRight); + points[TOP_LEFT_FAR] = transform.transform(farCorners.topLeft); + points[TOP_RIGHT_FAR] = transform.transform(farCorners.topRight); + + tesselateSides(points, triangles); + // Add far side + triangles[8].v0 = points[BOTTOM_LEFT_FAR]; + triangles[8].v1 = points[BOTTOM_RIGHT_FAR]; + triangles[8].v2 = points[TOP_RIGHT_FAR]; + + triangles[9].v0 = points[BOTTOM_LEFT_FAR]; + triangles[9].v1 = points[TOP_LEFT_FAR]; + triangles[9].v2 = points[TOP_RIGHT_FAR]; +} + +void ViewFrustum::tesselateSides(const glm::vec3 points[8], Triangle triangles[8]) { + static_assert(BOTTOM_RIGHT_NEAR == (BOTTOM_LEFT_NEAR + 1), "Assuming a certain sequence in corners"); + static_assert(TOP_RIGHT_NEAR == (BOTTOM_RIGHT_NEAR + 1), "Assuming a certain sequence in corners"); + static_assert(TOP_LEFT_NEAR == (TOP_RIGHT_NEAR + 1), "Assuming a certain sequence in corners"); + static_assert(BOTTOM_RIGHT_FAR == (BOTTOM_LEFT_FAR + 1), "Assuming a certain sequence in corners"); + static_assert(TOP_RIGHT_FAR == (BOTTOM_RIGHT_FAR + 1), "Assuming a certain sequence in corners"); + static_assert(TOP_LEFT_FAR == (TOP_RIGHT_FAR + 1), "Assuming a certain sequence in corners"); + static const int triangleVertexIndices[8][3] = { + { BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, BOTTOM_RIGHT_FAR },{ BOTTOM_LEFT_NEAR, BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR }, + { BOTTOM_RIGHT_NEAR, TOP_RIGHT_NEAR, TOP_RIGHT_FAR },{ BOTTOM_RIGHT_NEAR, BOTTOM_RIGHT_FAR, TOP_RIGHT_FAR }, + { TOP_RIGHT_NEAR, TOP_LEFT_NEAR, TOP_RIGHT_FAR },{ TOP_LEFT_NEAR, TOP_RIGHT_FAR, TOP_LEFT_FAR }, + { BOTTOM_LEFT_NEAR, TOP_LEFT_NEAR, TOP_LEFT_FAR },{ BOTTOM_LEFT_NEAR, BOTTOM_LEFT_FAR, TOP_LEFT_FAR } + }; + + for (auto i = 0; i < 8; i++) { + auto& triangle = triangles[i]; + auto vertexIndices = triangleVertexIndices[i]; + + triangle.v0 = points[vertexIndices[0]]; + triangle.v1 = points[vertexIndices[1]]; + triangle.v2 = points[vertexIndices[2]]; + } +} diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 98f666d666..d711e2a80a 100644 --- a/libraries/shared/src/ViewFrustum.h +++ b/libraries/shared/src/ViewFrustum.h @@ -74,7 +74,7 @@ public: glm::vec3 bottomRight; // Get the corners depth units from frustum position, along frustum orientation }; - const Corners getCorners(const float& depth) const; + const Corners getCorners(const float depth) const; // getters for corners const glm::vec3& getFarTopLeft() const { return _cornersWorld[TOP_LEFT_FAR]; } @@ -90,6 +90,10 @@ public: void setCenterRadius(float radius) { _centerSphereRadius = radius; } float getCenterRadius() const { return _centerSphereRadius; } + void tesselateSides(Triangle triangles[8]) const; + void tesselateSides(const Transform& transform, Triangle triangles[8]) const; + void tesselateSidesAndFar(const Transform& transform, Triangle triangles[10], float farDistance) const; + void calculate(); typedef enum { OUTSIDE = 0, INTERSECT, INSIDE } intersection; @@ -134,6 +138,12 @@ public: enum PlaneIndex { TOP_PLANE = 0, BOTTOM_PLANE, LEFT_PLANE, RIGHT_PLANE, NEAR_PLANE, FAR_PLANE, NUM_PLANES }; const ::Plane* getPlanes() const { return _planes; } + void getSidePlanes(::Plane planes[4]) const; + // Transform can have a different scale value in X,Y,Z components + void getTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const; + // Transform is assumed to have the same scale value in all three X,Y,Z components, which + // allows for a faster computation. + void getUniformlyTransformedSidePlanes(const Transform& transform, ::Plane planes[4]) const; void invalidate(); // causes all reasonable intersection tests to fail @@ -175,6 +185,8 @@ private: template CubeProjectedPolygon computeProjectedPolygon(const TBOX& box) const; + static void tesselateSides(const glm::vec3 points[8], Triangle triangles[8]); + }; using ViewFrustumPointer = std::shared_ptr; From e16b427ab698da2b68d8c80f5965f19efeac8833 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Tue, 5 Dec 2017 18:57:04 +0100 Subject: [PATCH 24/34] Switched to 4 cascades for key light and working on a better distribution --- libraries/render-utils/src/LightStage.cpp | 2 +- .../render-utils/src/RenderShadowTask.cpp | 21 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 28a54cefca..59ad63890a 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -23,7 +23,7 @@ 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{ 3 }; +const unsigned int LightStage::SUN_SHADOW_CASCADE_COUNT{ 4 }; const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index c5ed26823a..24757496c8 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -240,26 +240,33 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O const auto nearClip = args->getViewFrustum().getNearClip(); const auto farClip = args->getViewFrustum().getFarClip(); - static const float HIGH_CASCADE_MAX_DISTANCE = 20.0f; + // 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) { - 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)); + 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 = HIGH_CASCADE_MAX_DISTANCE / powf(cascadeLevelScale, globalShadow->getCascadeCount() - 1 - _cascadeIndex); - minCascadeDistance = maxCascadeDistance / cascadeLevelScale; + maxCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * maxAlpha; + minCascadeDistance = LOW_CASCADE_MAX_DISTANCE + deltaCascadeMaxDistance * minAlpha; } - shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 4.0f; - maxCascadeDistance += shadowOverlapDistance; 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); From 1d8d8335c5b27844f8007b61ef95f2ac8c01ef62 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 10:05:39 +0100 Subject: [PATCH 25/34] 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())); From f9641afcd0e111ac4a10861e0d726bc2fa545101 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 10:48:04 +0100 Subject: [PATCH 26/34] Modified cascade distance distribution to blend between a desired resolution and an automatic logarithm distribution --- libraries/render-utils/src/LightStage.cpp | 55 +++++++++++++---------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index a0cb5b3f2a..54154da2ea 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -102,41 +102,50 @@ LightStage::Shadow::Shadow(model::LightPointer light, float maxDistance, unsigne } void LightStage::Shadow::setMaxDistance(float value) { - _maxDistance = std::max(0.0f, value); + static const auto OVERLAP_FACTOR = 1.0f / 4.0f; - // 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; + _maxDistance = std::max(0.0f, 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 + + // 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 own 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; + for (auto cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { + float blendFactor = cascadeIndex / float(_cascades.size() - 1); 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; + if (cascadeIndex == _cascades.size() - 1) { + maxCascadeDistance = _maxDistance; } else { - minCascadeDistance = std::max(minCascadeDistance, 0.0f); + maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor; } - shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) / 3.0f; - maxCascadeDistance += shadowOverlapDistance; + + float shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) * OVERLAP_FACTOR; _cascades[cascadeIndex].setMinDistance(minCascadeDistance); - _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance); + _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance); + + // Compute distances for next cascade + minCascadeDistance = maxCascadeDistance; + maxCascadeUserDistance = maxCascadeUserDistance * userDistanceScale; + maxCascadeOptimalDistance = maxCascadeOptimalDistance * optimalDistanceScale; + maxCascadeUserDistance = std::min(maxCascadeUserDistance, _maxDistance); } } @@ -144,7 +153,7 @@ void LightStage::Shadow::setMaxDistance(float value) { const auto& lastCascade = _cascades.back(); auto& schema = _schemaBuffer.edit(); schema.maxDistance = _maxDistance; - schema.invFalloffDistance = 3.0f / (lastCascade.getMaxDistance() - lastCascade.getMinDistance()); + schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*(lastCascade.getMaxDistance() - lastCascade.getMinDistance())); } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, From ff39558d27ed22e5885b3b68b7bf927e5fe94226 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 11:22:15 +0100 Subject: [PATCH 27/34] Automatic shadow bias computation from cascade texel density --- libraries/render-utils/src/LightStage.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 54154da2ea..4ac5cb058d 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -23,6 +23,8 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{ 0.5, 0.5, 0.5, 1.0 }; const int LightStage::Shadow::MAP_SIZE = 1024; +static const auto MAX_BIAS = 0.006f; + const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { @@ -30,7 +32,7 @@ LightStage::LightStage() { LightStage::Shadow::Schema::Schema() { ShadowTransform defaultTransform; - defaultTransform.bias = 0.005f; + defaultTransform.bias = MAX_BIAS; std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform); invMapSize = 1.0f / MAP_SIZE; cascadeCount = 1; @@ -225,6 +227,11 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie // Update the buffer auto& schema = _schemaBuffer.edit(); schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); + // Adapt shadow bias to shadow resolution with a totally empirical formula + const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y)); + const auto REFERENCE_TEXEL_DENSITY = 20.0f; + const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; + schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); } void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { From 89b1ef2e1901508a283a423bf589acdba87287da Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 12:07:30 +0100 Subject: [PATCH 28/34] Tweaked bias once more --- libraries/render-utils/src/LightStage.cpp | 6 +++--- libraries/render-utils/src/LightStage.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 4ac5cb058d..5a640e1105 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -114,7 +114,7 @@ void LightStage::Shadow::setMaxDistance(float value) { } 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 LOW_MAX_DISTANCE = 1.5f; 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 @@ -214,7 +214,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie fitFrustum(farCorners.topLeft); fitFrustum(farCorners.topRight); - // Re-adjust near shadow distance + // Re-adjust near and far shadow distance auto near = glm::min(-max.z, nearDepth); auto far = cascade.computeFarDistance(viewFrustum, shadowViewInverse, min.x, max.x, min.y, max.y, viewMaxShadowDistance); @@ -229,7 +229,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); // Adapt shadow bias to shadow resolution with a totally empirical formula const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y)); - const auto REFERENCE_TEXEL_DENSITY = 20.0f; + const auto REFERENCE_TEXEL_DENSITY = 10.0f; const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); } diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index ed9d330934..f632354dd1 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -118,7 +118,7 @@ public: Index findLight(const LightPointer& light) const; Index addLight(const LightPointer& light); - Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U); + Index addShadow(Index lightIndex, float maxDistance = 16.0f, unsigned int cascadeCount = 1U); LightPointer removeLight(Index index); From c9c93370daf6b48384431fe087dba96b8079775f Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 15:26:44 +0100 Subject: [PATCH 29/34] Fixed weird bug with objects suddenly poping out of the shadow map. Was due to objects with own pipeline corrupting the render state. Don't know why though --- libraries/render-utils/src/LightStage.cpp | 4 ++-- libraries/render-utils/src/LightStage.h | 2 +- .../render-utils/src/RenderShadowTask.cpp | 18 ++++++++++++++++-- libraries/render-utils/src/RenderViewTask.cpp | 14 ++++++++++---- libraries/shared/src/ViewFrustum.cpp | 2 ++ 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 5a640e1105..89dc729185 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -119,7 +119,7 @@ void LightStage::Shadow::setMaxDistance(float value) { // 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 own based on the global min and max shadow distance, all cascades considered. The final + // 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)); @@ -229,7 +229,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); // Adapt shadow bias to shadow resolution with a totally empirical formula const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y)); - const auto REFERENCE_TEXEL_DENSITY = 10.0f; + const auto REFERENCE_TEXEL_DENSITY = 12.0f; const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); } diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index f632354dd1..ed9d330934 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -118,7 +118,7 @@ public: Index findLight(const LightPointer& light) const; Index addLight(const LightPointer& light); - Index addShadow(Index lightIndex, float maxDistance = 16.0f, 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 a0d691b10e..d362c14df8 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -22,6 +22,8 @@ #include "DeferredLightingEffect.h" #include "FramebufferCache.h" +#include "RenderUtilsLogging.h" + // These values are used for culling the objects rendered in the shadow map // but are readjusted afterwards #define SHADOW_FRUSTUM_NEAR 1.0f @@ -123,7 +125,8 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con return; } - const auto& fbo = shadow->getCascade(_cascadeIndex).framebuffer; + auto& cascade = shadow->getCascade(_cascadeIndex); + auto& fbo = cascade.framebuffer; RenderArgs* args = renderContext->args; ShapeKey::Builder defaultKeyBuilder; @@ -162,6 +165,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con auto shadowSkinnedPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder.withSkinned()); std::vector skinnedShapeKeys{}; + std::vector ownPipelineShapeKeys{}; // Iterate through all inShapes and render the unskinned args->_shapePipeline = shadowPipeline; @@ -169,8 +173,10 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con for (auto items : inShapes) { if (items.first.isSkinned()) { skinnedShapeKeys.push_back(items.first); - } else { + } else if (!items.first.hasOwnPipeline()) { renderItems(renderContext, items.second); + } else { + ownPipelineShapeKeys.push_back(items.first); } } @@ -181,7 +187,15 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con renderItems(renderContext, inShapes.at(key)); } + // Finally render the items with their own pipeline last to prevent them from breaking the + // render state. This is probably a temporary code as there is probably something better + // to do in the render call of objects that have their own pipeline. args->_shapePipeline = nullptr; + for (const auto& key : ownPipelineShapeKeys) { + args->_itemShapeKey = key._flags.to_ulong(); + renderItems(renderContext, inShapes.at(key)); + } + args->_batch = nullptr; }); } diff --git a/libraries/render-utils/src/RenderViewTask.cpp b/libraries/render-utils/src/RenderViewTask.cpp index 1085a1148c..dc6c66e058 100644 --- a/libraries/render-utils/src/RenderViewTask.cpp +++ b/libraries/render-utils/src/RenderViewTask.cpp @@ -14,15 +14,21 @@ #include "RenderDeferredTask.h" #include "RenderForwardTask.h" - - void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, bool isDeferred) { // auto items = input.get(); // Shadows use an orthographic projection because they are linked to sunlights // but the cullFunctor passed is probably tailored for perspective projection and culls too much. - // TODO : create a special cull functor for this. - task.addJob("RenderShadowTask", nullptr); + task.addJob("RenderShadowTask", [](const RenderArgs* args, const AABox& bounds) { + // Cull only objects that are too small relatively to shadow frustum + auto& frustum = args->getViewFrustum(); + auto frustumSize = std::max(frustum.getHeight(), frustum.getWidth()); + const auto boundsRadius = bounds.getDimensions().length(); + const auto relativeBoundRadius = boundsRadius / frustumSize; + const auto threshold = 1e-3f; + return relativeBoundRadius > threshold; + return true; + }); const auto items = task.addJob("FetchCullSort", cullFunctor); assert(items.canCast()); diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp index 37c01510f3..5b016d4e91 100644 --- a/libraries/shared/src/ViewFrustum.cpp +++ b/libraries/shared/src/ViewFrustum.cpp @@ -71,6 +71,8 @@ void ViewFrustum::setProjection(const glm::mat4& projection) { glm::vec4 top = inverseProjection * vec4(0.0f, 1.0f, -1.0f, 1.0f); top /= top.w; _fieldOfView = abs(glm::degrees(2.0f * abs(glm::angle(vec3(0.0f, 0.0f, -1.0f), glm::normalize(vec3(top)))))); + _height = _corners[TOP_RIGHT_NEAR].y - _corners[BOTTOM_RIGHT_NEAR].y; + _width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x; } // ViewFrustum::calculate() From 014e81b2f479955eed71979af529622ab2335cc9 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 17:06:48 +0100 Subject: [PATCH 30/34] Added normal based adaptive depth bias --- libraries/render-utils/src/LightStage.cpp | 34 +++++++++++-------- libraries/render-utils/src/Shadow.slh | 17 ++++++---- libraries/render-utils/src/ShadowCore.slh | 7 ++-- libraries/render-utils/src/Shadows_shared.slh | 3 +- .../src/directional_ambient_light_shadow.slf | 2 +- .../src/directional_skybox_light_shadow.slf | 2 +- 6 files changed, 39 insertions(+), 26 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 89dc729185..b49408a1f4 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -36,7 +36,7 @@ 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; + invCascadeBlendWidth = 1.0f / 0.2f; invFalloffDistance = 1.0f / 2.0f; maxDistance = 20.0f; } @@ -104,7 +104,10 @@ LightStage::Shadow::Shadow(model::LightPointer light, float maxDistance, unsigne } void LightStage::Shadow::setMaxDistance(float value) { - static const auto OVERLAP_FACTOR = 1.0f / 4.0f; + // 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; _maxDistance = std::max(0.0f, value); @@ -114,7 +117,7 @@ void LightStage::Shadow::setMaxDistance(float value) { } 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 = 1.5f; + 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 @@ -135,10 +138,10 @@ void LightStage::Shadow::setMaxDistance(float value) { if (cascadeIndex == _cascades.size() - 1) { maxCascadeDistance = _maxDistance; } else { - maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor; + maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor; } - float shadowOverlapDistance = (maxCascadeDistance - minCascadeDistance) * OVERLAP_FACTOR; + float shadowOverlapDistance = maxCascadeDistance * OVERLAP_FACTOR; _cascades[cascadeIndex].setMinDistance(minCascadeDistance); _cascades[cascadeIndex].setMaxDistance(maxCascadeDistance + shadowOverlapDistance); @@ -155,7 +158,7 @@ void LightStage::Shadow::setMaxDistance(float value) { const auto& lastCascade = _cascades.back(); auto& schema = _schemaBuffer.edit(); schema.maxDistance = _maxDistance; - schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*(lastCascade.getMaxDistance() - lastCascade.getMinDistance())); + schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance()); } void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, @@ -164,16 +167,16 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie assert(cascadeIndex < _cascades.size()); // Orient the keylight frustum - const auto& direction = glm::normalize(_light->getDirection()); + const auto& lightDirection = glm::normalize(_light->getDirection()); glm::quat orientation; - if (direction == IDENTITY_UP) { + if (lightDirection == IDENTITY_UP) { orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FORWARD, -IDENTITY_UP)); - } else if (direction == -IDENTITY_UP) { + } else if (lightDirection == -IDENTITY_UP) { orientation = glm::quat(glm::mat3(IDENTITY_RIGHT, IDENTITY_FORWARD, IDENTITY_UP)); } else { - auto side = glm::normalize(glm::cross(direction, IDENTITY_UP)); - auto up = glm::normalize(glm::cross(side, direction)); - orientation = glm::quat_cast(glm::mat3(side, up, -direction)); + auto side = glm::normalize(glm::cross(lightDirection, IDENTITY_UP)); + auto up = glm::normalize(glm::cross(side, lightDirection)); + orientation = glm::quat_cast(glm::mat3(side, up, -lightDirection)); } auto& cascade = _cascades[cascadeIndex]; @@ -184,7 +187,7 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie cascade._frustum->setOrientation(orientation); // Position the keylight frustum - cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*direction); + cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection); const Transform shadowView{ cascade._frustum->getView()}; const Transform shadowViewInverse{ shadowView.getInverseMatrix() }; @@ -229,9 +232,12 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); // Adapt shadow bias to shadow resolution with a totally empirical formula const auto maxShadowFrustumDim = std::max(fabsf(min.x - max.x), fabsf(min.y - max.y)); - const auto REFERENCE_TEXEL_DENSITY = 12.0f; + const auto REFERENCE_TEXEL_DENSITY = 7.5f; const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); + // TODO: this isn't the best place to do this as this is common for all cascades and this is called for + // each cascade at each frame. + schema.lightDirInViewSpace = Transform(Transform(viewFrustum.getView()).getInverseMatrix()).transformDirection(lightDirection); } void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index c4b7828688..a12dd0f4a4 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -71,8 +71,8 @@ ShadowSampleOffsets evalShadowFilterOffsets(vec4 position) { return offsets; } -float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { - +float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias) { + shadowTexcoord.z -= bias; float shadowAttenuation = 0.25 * ( fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[0]) + fetchShadow(cascadeIndex, shadowTexcoord.xyz + offsets.points[1]) + @@ -83,24 +83,27 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { +float evalShadowCascadeAttenuation(int cascadeIndex, vec3 viewNormal, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) { // If a point is not in the map, do not attenuate return 1.0; } - return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord); + // Multiply bias if we are at a grazing angle with light + float tangentFactor = abs(dot(getShadowDirInViewSpace(), viewNormal)); + float bias = getShadowBias(cascadeIndex) * (5.0-4.0*tangentFactor); + return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); } -float evalShadowAttenuation(vec4 worldPosition, float viewDepth) { +float evalShadowAttenuation(vec4 worldPosition, float viewDepth, vec3 viewNormal) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); vec4 cascadeShadowCoords[2]; ivec2 cascadeIndices; float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices); vec2 cascadeAttenuations = vec2(1.0, 1.0); - cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0]); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, viewNormal, offsets, cascadeShadowCoords[0]); if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { - cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1]); + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, viewNormal, offsets, cascadeShadowCoords[1]); } float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); // Falloff to max distance diff --git a/libraries/render-utils/src/ShadowCore.slh b/libraries/render-utils/src/ShadowCore.slh index 0a34cafe6f..9b3b086598 100644 --- a/libraries/render-utils/src/ShadowCore.slh +++ b/libraries/render-utils/src/ShadowCore.slh @@ -41,11 +41,14 @@ float getShadowBias(int cascadeIndex) { return shadow.cascades[cascadeIndex].bias; } +vec3 getShadowDirInViewSpace() { + return shadow.lightDirInViewSpace; +} + // Compute the texture coordinates from world coordinates vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) { vec4 shadowCoord = getShadowReprojection(cascadeIndex) * position; - float bias = getShadowBias(cascadeIndex); - return vec4(shadowCoord.xy, shadowCoord.z - bias, 1.0); + return vec4(shadowCoord.xyz, 1.0); } bool isShadowCascadeProjectedOnPixel(vec4 cascadeTexCoords) { diff --git a/libraries/render-utils/src/Shadows_shared.slh b/libraries/render-utils/src/Shadows_shared.slh index 2797f4e962..abb226421c 100644 --- a/libraries/render-utils/src/Shadows_shared.slh +++ b/libraries/render-utils/src/Shadows_shared.slh @@ -4,7 +4,7 @@ # define VEC3 glm::vec3 #else # define MAT4 mat4 -# define VEC3 ve3 +# define VEC3 vec3 #endif #define SHADOW_CASCADE_MAX_COUNT 4 @@ -20,6 +20,7 @@ struct ShadowTransform { struct ShadowParameters { ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT]; + VEC3 lightDirInViewSpace; int cascadeCount; float invMapSize; float invCascadeBlendWidth; diff --git a/libraries/render-utils/src/directional_ambient_light_shadow.slf b/libraries/render-utils/src/directional_ambient_light_shadow.slf index b791c9297a..f7ea8c5966 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.z); + float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal); 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 2bccc68550..37c9ae7fba 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.z); + float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal); // Light mapped or not ? if (frag.mode == FRAG_MODE_UNLIT) { From 8d710c8f73abb3587cd31002288e59165b1ae0a2 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 18:08:27 +0100 Subject: [PATCH 31/34] Factorised out some computation needed for all cascades --- libraries/render-utils/src/LightStage.cpp | 32 +++++++++++-------- libraries/render-utils/src/LightStage.h | 6 ++-- .../render-utils/src/RenderShadowTask.cpp | 28 ++++++++++++---- libraries/render-utils/src/RenderShadowTask.h | 21 ++++++++---- 4 files changed, 59 insertions(+), 28 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index b49408a1f4..95629b5c3d 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -161,13 +161,11 @@ void LightStage::Shadow::setMaxDistance(float value) { schema.invFalloffDistance = 1.0f / (OVERLAP_FACTOR*lastCascade.getMaxDistance()); } -void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, +void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth, float farDepth) { assert(nearDepth < farDepth); - assert(cascadeIndex < _cascades.size()); - // Orient the keylight frustum - const auto& lightDirection = glm::normalize(_light->getDirection()); + auto lightDirection = glm::normalize(_light->getDirection()); glm::quat orientation; if (lightDirection == IDENTITY_UP) { orientation = glm::quat(glm::mat3(-IDENTITY_RIGHT, IDENTITY_FORWARD, -IDENTITY_UP)); @@ -179,16 +177,27 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie orientation = glm::quat_cast(glm::mat3(side, up, -lightDirection)); } + // Position the keylight frustum + auto position = viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection; + for (auto& cascade : _cascades) { + cascade._frustum->setOrientation(orientation); + cascade._frustum->setPosition(position); + } + // Update the buffer + auto& schema = _schemaBuffer.edit(); + schema.lightDirInViewSpace = Transform(Transform(viewFrustum.getView()).getInverseMatrix()).transformDirection(lightDirection); +} + +void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, + float nearDepth, float farDepth) { + assert(nearDepth < farDepth); + assert(cascadeIndex < _cascades.size()); + 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); - - // Position the keylight frustum - cascade._frustum->setPosition(viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection); - const Transform shadowView{ cascade._frustum->getView()}; const Transform shadowViewInverse{ shadowView.getInverseMatrix() }; @@ -235,12 +244,9 @@ void LightStage::Shadow::setKeylightFrustum(unsigned int cascadeIndex, const Vie const auto REFERENCE_TEXEL_DENSITY = 7.5f; const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); - // TODO: this isn't the best place to do this as this is common for all cascades and this is called for - // each cascade at each frame. - schema.lightDirInViewSpace = Transform(Transform(viewFrustum.getView()).getInverseMatrix()).transformDirection(lightDirection); } -void LightStage::Shadow::setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { +void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { assert(cascadeIndex < _cascades.size()); const Transform view{ shadowFrustum.getView() }; const Transform viewInverse{ view.getInverseMatrix() }; diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index ed9d330934..508e67ec17 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -77,9 +77,11 @@ public: Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount = 1); - void setKeylightFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, + void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth = 1.0f, float farDepth = 1000.0f); - void setFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); + void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, + float nearDepth = 1.0f, float farDepth = 1000.0f); + void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum); const UniformBufferView& getBuffer() const { return _schemaBuffer; } diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index d362c14df8..b83f2e87e9 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -136,7 +136,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con // the minimal Z range. adjustNearFar(inShapeBounds, adjustedShadowFrustum); // Reapply the frustum as it has been adjusted - shadow->setFrustum(_cascadeIndex, adjustedShadowFrustum); + shadow->setCascadeFrustum(_cascadeIndex, adjustedShadowFrustum); args->popViewFrustum(); args->pushViewFrustum(adjustedShadowFrustum); @@ -213,9 +213,11 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende initZPassPipelines(*shapePlumber, state); } + task.addJob("ShadowSetup"); + for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { - const auto setupOutput = task.addJob("ShadowSetup", i); - const auto shadowFilter = setupOutput.getN(1); + const auto setupOutput = task.addJob("ShadowCascadeSetup", i); + const auto shadowFilter = setupOutput.getN(1); // CPU jobs: // Fetch and cull the items from the scene @@ -229,7 +231,7 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende // GPU jobs: Render to shadow map task.addJob("RenderShadowMap", sortedShapesAndBounds, shapePlumber, i); - task.addJob("ShadowTeardown", setupOutput); + task.addJob("ShadowCascadeTeardown", setupOutput); } } @@ -239,7 +241,19 @@ void RenderShadowTask::configure(const Config& configuration) { // Task::configure(configuration); } -void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { +void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) { + auto lightStage = renderContext->_scene->getStage(); + assert(lightStage); + // Cache old render args + RenderArgs* args = renderContext->args; + + const auto globalShadow = lightStage->getCurrentKeyShadow(); + if (globalShadow) { + globalShadow->setKeylightFrustum(args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + } +} + +void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); // Cache old render args @@ -251,7 +265,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O if (globalShadow && _cascadeIndexgetCascadeCount()) { output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - globalShadow->setKeylightFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); @@ -261,7 +275,7 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext, O } } -void RenderShadowTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { +void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& renderContext, const Input& input) { RenderArgs* args = renderContext->args; if (args->_renderMode == RenderArgs::SHADOW_RENDER_MODE && !input.get1().selectsNothing()) { diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index 3dcdfdd9b1..a723a529f0 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -55,10 +55,19 @@ public: class RenderShadowSetup { public: - using Outputs = render::VaryingSet2; - using JobModel = render::Job::ModelO; + using JobModel = render::Job::Model; - RenderShadowSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} + RenderShadowSetup() {} + void run(const render::RenderContextPointer& renderContext); + +}; + +class RenderShadowCascadeSetup { +public: + using Outputs = render::VaryingSet2; + using JobModel = render::Job::ModelO; + + RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} void run(const render::RenderContextPointer& renderContext, Outputs& output); private: @@ -66,10 +75,10 @@ private: unsigned int _cascadeIndex; }; -class RenderShadowTeardown { +class RenderShadowCascadeTeardown { public: - using Input = RenderShadowSetup::Outputs; - using JobModel = render::Job::ModelI; + using Input = RenderShadowCascadeSetup::Outputs; + using JobModel = render::Job::ModelI; void run(const render::RenderContextPointer& renderContext, const Input& input); }; From dc55a168342e2616d584d89d84d82fab63390479 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 6 Dec 2017 18:37:27 +0100 Subject: [PATCH 32/34] Fixed warnings on Mac and Ubuntu --- libraries/render-utils/src/DeferredLightingEffect.cpp | 2 +- libraries/render-utils/src/LightStage.cpp | 4 ++-- libraries/render-utils/src/RenderDeferredTask.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 8bbd212a25..3286531643 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -503,7 +503,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, // Bind the shadow buffers if (globalShadow) { - for (auto i = 0; i < globalShadow->getCascadeCount(); i++) { + for (unsigned int i = 0; i < globalShadow->getCascadeCount(); i++) { batch.setResourceTexture(SHADOW_MAP_UNIT+i, globalShadow->getCascade(i).map); } } diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index 95629b5c3d..b9b774452c 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -131,11 +131,11 @@ void LightStage::Shadow::setMaxDistance(float value) { float maxCascadeOptimalDistance = LOW_MAX_DISTANCE; float minCascadeDistance = 0.0f; - for (auto cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { + for (size_t cascadeIndex = 0; cascadeIndex < _cascades.size(); ++cascadeIndex) { float blendFactor = cascadeIndex / float(_cascades.size() - 1); float maxCascadeDistance; - if (cascadeIndex == _cascades.size() - 1) { + if (cascadeIndex == size_t(_cascades.size() - 1)) { maxCascadeDistance = _maxDistance; } else { maxCascadeDistance = maxCascadeUserDistance + (maxCascadeOptimalDistance - maxCascadeUserDistance)*blendFactor*blendFactor; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index bac28c0ddd..5c9abbabed 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -566,7 +566,7 @@ void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Out if (lightStage) { auto globalShadow = lightStage->getCurrentKeyShadow(); - if (globalShadow && igetCascadeCount()) { + if (globalShadow && i<(int)globalShadow->getCascadeCount()) { auto& cascade = globalShadow->getCascade(i); shadowFrustum = cascade.getFrustum(); } else { From b9d49318e76201774437292659f985065d888661 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Mon, 11 Dec 2017 12:19:10 +0100 Subject: [PATCH 33/34] Fixed bug which culled medium sized objects in shadow maps due to perspective frustum assumed octree selection --- libraries/render-utils/src/RenderShadowTask.cpp | 10 ++++++++++ libraries/render-utils/src/RenderShadowTask.h | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index b83f2e87e9..2e5b7132e4 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -260,6 +260,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon RenderArgs* args = renderContext->args; output.edit0() = args->_renderMode; + output.edit2() = args->_sizeScale; const auto globalShadow = lightStage->getCurrentKeyShadow(); if (globalShadow && _cascadeIndexgetCascadeCount()) { @@ -270,6 +271,14 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; + if (lightStage->getCurrentKeyLight()->getType() == model::Light::SUN) { + const float shadowSizeScale = 1e16f; + // Set the size scale to a ridiculously high value to prevent small object culling which assumes + // the view frustum is a perspective projection. But this isn't the case for the sun which + // is an orthographic projection. + args->_sizeScale = shadowSizeScale; + } + } else { output.edit1() = ItemFilter::Builder::nothing(); } @@ -284,4 +293,5 @@ void RenderShadowCascadeTeardown::run(const render::RenderContextPointer& render assert(args->hasViewFrustum()); // Reset the render args args->_renderMode = input.get0(); + args->_sizeScale = input.get2(); }; diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index a723a529f0..d8d4c624e7 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -64,7 +64,7 @@ public: class RenderShadowCascadeSetup { public: - using Outputs = render::VaryingSet2; + using Outputs = render::VaryingSet3; using JobModel = render::Job::ModelO; RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} From 19e240460dca02c194cc7c3d8d4844ea7c885a4a Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Wed, 13 Dec 2017 15:30:57 +0100 Subject: [PATCH 34/34] Slightly optimized matrix operations in LightStage as samcake's remark --- libraries/render-utils/src/LightStage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index b9b774452c..e568554452 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -185,7 +185,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, } // Update the buffer auto& schema = _schemaBuffer.edit(); - schema.lightDirInViewSpace = Transform(Transform(viewFrustum.getView()).getInverseMatrix()).transformDirection(lightDirection); + schema.lightDirInViewSpace = glm::inverse(viewFrustum.getView()) * glm::vec4(lightDirection, 0.f); } void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,