Working on better adaptive bias algorithm for shadow

This commit is contained in:
Olivier Prat 2018-01-26 17:57:20 +01:00
parent b41129f81b
commit 6b0b17ff63
8 changed files with 77 additions and 19 deletions

View file

@ -220,7 +220,7 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
} }
void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, 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(nearDepth < farDepth);
assert(cascadeIndex < _cascades.size()); assert(cascadeIndex < _cascades.size());
@ -270,11 +270,7 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co
// Update the buffer // Update the buffer
auto& schema = _schemaBuffer.edit<Schema>(); auto& schema = _schemaBuffer.edit<Schema>();
schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix(); schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
// Adapt shadow bias to shadow resolution with a totally empirical formula schema.cascades[cascadeIndex].bias = baseBias;
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);
} }
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) { void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {

View file

@ -80,7 +80,7 @@ public:
void setKeylightFrustum(const ViewFrustum& viewFrustum, void setKeylightFrustum(const ViewFrustum& viewFrustum,
float nearDepth = 1.0f, float farDepth = 1000.0f); float nearDepth = 1.0f, float farDepth = 1000.0f);
void setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum, 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); void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
const UniformBufferView& getBuffer() const { return _schemaBuffer; } const UniformBufferView& getBuffer() const { return _schemaBuffer; }

View file

@ -216,7 +216,9 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
task.addJob<RenderShadowSetup>("ShadowSetup"); task.addJob<RenderShadowSetup>("ShadowSetup");
for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) { for (auto i = 0; i < SHADOW_CASCADE_MAX_COUNT; i++) {
const auto setupOutput = task.addJob<RenderShadowCascadeSetup>("ShadowCascadeSetup", i); char jobName[64];
sprintf(jobName, "ShadowCascadeSetup%d", i);
const auto setupOutput = task.addJob<RenderShadowCascadeSetup>(jobName, i);
const auto shadowFilter = setupOutput.getN<RenderShadowCascadeSetup::Outputs>(1); const auto shadowFilter = setupOutput.getN<RenderShadowCascadeSetup::Outputs>(1);
// CPU jobs: // 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) { void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
auto lightStage = renderContext->_scene->getStage<LightStage>(); auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage); assert(lightStage);
@ -266,7 +272,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) { if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
output.edit1() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered(); 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 // Set the keylight render args
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));

View file

@ -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 { class RenderShadowCascadeSetup {
public: public:
using Outputs = render::VaryingSet3<RenderArgs::RenderMode, render::ItemFilter, float>; using Outputs = render::VaryingSet3<RenderArgs::RenderMode, render::ItemFilter, float>;
using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs>; using Config = RenderShadowCascadeSetupConfig;
using JobModel = render::Job::ModelO<RenderShadowCascadeSetup, Outputs, Config>;
RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {} RenderShadowCascadeSetup(unsigned int cascadeIndex) : _cascadeIndex{ cascadeIndex } {}
void configure(const Config& configuration);
void run(const render::RenderContextPointer& renderContext, Outputs& output); void run(const render::RenderContextPointer& renderContext, Outputs& output);
private: private:
unsigned int _cascadeIndex; unsigned int _cascadeIndex;
float _baseBias{ 0.1f };
}; };
class RenderShadowCascadeTeardown { class RenderShadowCascadeTeardown {

View file

@ -83,27 +83,30 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve
return shadowAttenuation; 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 (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) {
// If a point is not in the map, do not attenuate // If a point is not in the map, do not attenuate
return 1.0; return 1.0;
} }
// Multiply bias if we are at a grazing angle with light // Add fixed bias
float tangentFactor = abs(dot(getShadowDirInViewSpace(), viewNormal)); bias += getShadowBias(cascadeIndex);
float bias = getShadowBias(cascadeIndex) * (5.0-4.0*tangentFactor);
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias); 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); ShadowSampleOffsets offsets = evalShadowFilterOffsets(worldPosition);
vec4 cascadeShadowCoords[2]; vec4 cascadeShadowCoords[2];
ivec2 cascadeIndices; ivec2 cascadeIndices;
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, 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); 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()) { 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); float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
// Falloff to max distance // Falloff to max distance

View file

@ -28,7 +28,9 @@ void main(void) {
vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 viewPos = vec4(frag.position.xyz, 1.0);
vec4 worldPos = getViewInverse() * viewPos; 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) { if (frag.mode == FRAG_MODE_UNLIT) {
discard; discard;

View file

@ -28,7 +28,9 @@ void main(void) {
vec4 viewPos = vec4(frag.position.xyz, 1.0); vec4 viewPos = vec4(frag.position.xyz, 1.0);
vec4 worldPos = getViewInverse() * viewPos; 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 ? // Light mapped or not ?
if (frag.mode == FRAG_MODE_UNLIT) { if (frag.mode == FRAG_MODE_UNLIT) {

View file

@ -10,6 +10,9 @@
// //
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import "qrc:///qml/styles-uit"
import "qrc:///qml/controls-uit" as HifiControls
import "configSlider"
Column { Column {
id: root id: root
@ -68,4 +71,36 @@ Column {
font.italic: true 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
}
} }