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,
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>();
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) {

View file

@ -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; }

View file

@ -216,7 +216,9 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
task.addJob<RenderShadowSetup>("ShadowSetup");
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);
// 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<LightStage>();
assert(lightStage);
@ -266,7 +272,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
if (globalShadow && _cascadeIndex<globalShadow->getCascadeCount()) {
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()));

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 {
public:
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 } {}
void configure(const Config& configuration);
void run(const render::RenderContextPointer& renderContext, Outputs& output);
private:
unsigned int _cascadeIndex;
float _baseBias{ 0.1f };
};
class RenderShadowCascadeTeardown {

View file

@ -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

View file

@ -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;

View file

@ -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) {

View file

@ -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
}
}