From 6b0b17ff633f72bb16171da4f34ad9f672ecefec Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Fri, 26 Jan 2018 17:57:20 +0100 Subject: [PATCH] Working on better adaptive bias algorithm for shadow --- libraries/render-utils/src/LightStage.cpp | 8 ++--- libraries/render-utils/src/LightStage.h | 2 +- .../render-utils/src/RenderShadowTask.cpp | 10 ++++-- libraries/render-utils/src/RenderShadowTask.h | 16 ++++++++- libraries/render-utils/src/Shadow.slh | 17 +++++---- .../src/directional_ambient_light_shadow.slf | 4 ++- .../src/directional_skybox_light_shadow.slf | 4 ++- scripts/developer/utilities/render/shadow.qml | 35 +++++++++++++++++++ 8 files changed, 77 insertions(+), 19 deletions(-) diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index f146cd6e0a..eac9dd7657 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -220,7 +220,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum, } void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float nearDepth, float farDepth) { + float nearDepth, float farDepth, float baseBias) { assert(nearDepth < farDepth); assert(cascadeIndex < _cascades.size()); @@ -270,11 +270,7 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co // 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 = 7.5f; - const auto cascadeTexelDensity = MAP_SIZE / maxShadowFrustumDim; - schema.cascades[cascadeIndex].bias = MAX_BIAS * std::min(1.0f, REFERENCE_TEXEL_DENSITY / cascadeTexelDensity); + schema.cascades[cascadeIndex].bias = baseBias; } void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index d1a8680706..c2c2df8c89 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -80,7 +80,7 @@ public: void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth = 1.0f, float farDepth = 1000.0f); void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, - float nearDepth = 1.0f, float farDepth = 1000.0f); + float nearDepth = 1.0f, float farDepth = 1000.0f, float baseBias = 0.005f); 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 d83dfd73a5..f41860b6de 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -216,7 +216,9 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende task.addJob("ShadowSetup"); for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { - const auto setupOutput = task.addJob("ShadowCascadeSetup", i); + char jobName[64]; + sprintf(jobName, "ShadowCascadeSetup%d", i); + const auto setupOutput = task.addJob(jobName, i); const auto shadowFilter = setupOutput.getN(1); // CPU jobs: @@ -253,6 +255,10 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) { } } +void RenderShadowCascadeSetup::configure(const Config& configuration) { + _baseBias = configuration.bias * configuration.bias * 0.01f; +} + void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) { auto lightStage = renderContext->_scene->getStage(); assert(lightStage); @@ -266,7 +272,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon if (globalShadow && _cascadeIndexgetCascadeCount()) { output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); - globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR); + globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR, _baseBias); // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h index d8d4c624e7..42c279c02a 100644 --- a/libraries/render-utils/src/RenderShadowTask.h +++ b/libraries/render-utils/src/RenderShadowTask.h @@ -62,17 +62,31 @@ public: }; +class RenderShadowCascadeSetupConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float bias MEMBER bias NOTIFY dirty) +public: + + float bias{ 0.5f }; + +signals: + void dirty(); +}; + class RenderShadowCascadeSetup { public: using Outputs = render::VaryingSet3; - using JobModel = render::Job::ModelO; + using Config = RenderShadowCascadeSetupConfig; + using JobModel = render::Job::ModelO; RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} + void configure(const Config& configuration); void run(const render::RenderContextPointer& renderContext, Outputs& output); private: unsigned int _cascadeIndex; + float _baseBias{ 0.1f }; }; class RenderShadowCascadeTeardown { diff --git a/libraries/render-utils/src/Shadow.slh b/libraries/render-utils/src/Shadow.slh index a12dd0f4a4..50ae8864f4 100644 --- a/libraries/render-utils/src/Shadow.slh +++ b/libraries/render-utils/src/Shadow.slh @@ -83,27 +83,30 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve return shadowAttenuation; } -float evalShadowCascadeAttenuation(int cascadeIndex, vec3 viewNormal, ShadowSampleOffsets offsets, vec4 shadowTexcoord) { +float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias) { if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) { // If a point is not in the map, do not attenuate return 1.0; } - // 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); + // Add fixed bias + bias += getShadowBias(cascadeIndex); return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); } -float evalShadowAttenuation(vec4 worldPosition, float viewDepth, vec3 viewNormal) { +float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDepth, vec3 worldNormal) { ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition); vec4 cascadeShadowCoords[2]; ivec2 cascadeIndices; float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices); + // Adjust bias if we are at a grazing angle with light + float ndotl = dot(worldLightDir, worldNormal); + float bias = 0.5*(1.0/(ndotl*ndotl)-1.0); + bias *= getShadowScale(); vec2 cascadeAttenuations = vec2(1.0, 1.0); - cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, viewNormal, offsets, cascadeShadowCoords[0]); + cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], bias); if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) { - cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, viewNormal, offsets, cascadeShadowCoords[1]); + cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], bias); } float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix); // Falloff to max distance diff --git a/libraries/render-utils/src/directional_ambient_light_shadow.slf b/libraries/render-utils/src/directional_ambient_light_shadow.slf index f7ea8c5966..eead392fd8 100644 --- a/libraries/render-utils/src/directional_ambient_light_shadow.slf +++ b/libraries/render-utils/src/directional_ambient_light_shadow.slf @@ -28,7 +28,9 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal); + Light shadowLight = getKeyLight(); + vec3 worldLightDirection = getLightDirection(shadowLight); + float shadowAttenuation = evalShadowAttenuation(worldLightDirection, 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 37c9ae7fba..2a2f8f65a3 100644 --- a/libraries/render-utils/src/directional_skybox_light_shadow.slf +++ b/libraries/render-utils/src/directional_skybox_light_shadow.slf @@ -28,7 +28,9 @@ void main(void) { vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 worldPos = getViewInverse() * viewPos; - float shadowAttenuation = evalShadowAttenuation(worldPos, -viewPos.z, frag.normal); + Light shadowLight = getKeyLight(); + vec3 worldLightDirection = getLightDirection(shadowLight); + float shadowAttenuation = evalShadowAttenuation(worldLightDirection, worldPos, -viewPos.z, frag.normal); // Light mapped or not ? if (frag.mode == FRAG_MODE_UNLIT) { diff --git a/scripts/developer/utilities/render/shadow.qml b/scripts/developer/utilities/render/shadow.qml index a8fcf1aec7..32405a5260 100644 --- a/scripts/developer/utilities/render/shadow.qml +++ b/scripts/developer/utilities/render/shadow.qml @@ -10,6 +10,9 @@ // import QtQuick 2.5 import QtQuick.Controls 1.4 +import "qrc:///qml/styles-uit" +import "qrc:///qml/controls-uit" as HifiControls +import "configSlider" Column { id: root @@ -68,4 +71,36 @@ Column { font.italic: true } } + ConfigSlider { + label: qsTr("Cascade 0 bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup0") + property: "bias" + max: 1.0 + min: 0.01 + } + ConfigSlider { + label: qsTr("Cascade 1 bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup1") + property: "bias" + max: 1.0 + min: 0.01 + } + ConfigSlider { + label: qsTr("Cascade 2 bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup2") + property: "bias" + max: 1.0 + min: 0.01 + } + ConfigSlider { + label: qsTr("Cascade 3 bias") + integral: false + config: Render.getConfig("RenderMainView.ShadowCascadeSetup3") + property: "bias" + max: 1.0 + min: 0.01 + } }