Switched to a simpler manual fixed/slope based shadow bias system. Automatic stuff fail most of the time

This commit is contained in:
Olivier Prat 2018-01-31 10:19:17 +01:00
parent 0324f41565
commit f344e44d26
9 changed files with 78 additions and 69 deletions

View file

@ -70,18 +70,6 @@ LightStage::Shadow::Schema::Schema() {
maxDistance = 20.0f;
}
void LightStage::Shadow::Schema::updateCascade(int cascadeIndex, const ViewFrustum& shadowFrustum) {
auto& cascade = cascades[cascadeIndex];
cascade.frustumPosition = shadowFrustum.getPosition();
// The adaptative bias is computing how much depth offset we have to add to
// push back a coarsely sampled surface perpendicularly to the shadow direction,
// to not have shadow acnee. The final computation is done in the shader, based on the
// surface normal.
auto maxWorldFrustumSize = glm::max(shadowFrustum.getWidth(), shadowFrustum.getHeight());
cascade.adaptiveBiasUnitScale = maxWorldFrustumSize * 0.5f * invMapSize;
cascade.adaptiveBiasTransformScale = shadowFrustum.getNearClip() * shadowFrustum.getFarClip() / (shadowFrustum.getFarClip() - shadowFrustum.getNearClip());
}
LightStage::Shadow::Cascade::Cascade() :
_frustum{ std::make_shared<ViewFrustum>() },
_minDistance{ 0.0f },
@ -222,17 +210,14 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
auto position = viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection;
// Update the buffer
auto& schema = _schemaBuffer.edit<Schema>();
auto cascadeIndex = 0;
for (auto& cascade : _cascades) {
cascade._frustum->setOrientation(orientation);
cascade._frustum->setPosition(position);
schema.cascades[cascadeIndex].frustumPosition = position;
cascadeIndex++;
}
}
void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
float nearDepth, float farDepth, float baseBias) {
float nearDepth, float farDepth, float fixedBias, float slopeBias) {
assert(nearDepth < farDepth);
assert(cascadeIndex < _cascades.size());
@ -283,8 +268,8 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co
auto& schema = _schemaBuffer.edit<Schema>();
auto& schemaCascade = schema.cascades[cascadeIndex];
schemaCascade.reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
schemaCascade.fixedBias = baseBias;
schema.updateCascade(cascadeIndex, *cascade._frustum);
schemaCascade.fixedBias = fixedBias;
schemaCascade.slopeBias = slopeBias;
}
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
@ -298,7 +283,6 @@ void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const View
auto& schema = _schemaBuffer.edit<Schema>();
auto& schemaCascade = schema.cascades[cascadeIndex];
schemaCascade.reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
schema.updateCascade(cascadeIndex, shadowFrustum);
}
LightStage::Index LightStage::findLight(const LightPointer& light) const {

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 baseBias = 0.005f);
float nearDepth = 1.0f, float farDepth = 1000.0f, float fixedBias = 0.005f, float slopeBias = 0.005f);
void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
@ -110,8 +110,6 @@ public:
Schema();
void updateCascade(int cascadeIndex, const ViewFrustum& shadowFrustum);
};
UniformBufferView _schemaBuffer = nullptr;
};

View file

@ -256,9 +256,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) {
}
void RenderShadowCascadeSetup::configure(const Config& configuration) {
// I'm not very proud of this empirical adjustment
auto cascadeBias = configuration.bias * powf(1.1f, _cascadeIndex);
_baseBias = cascadeBias * cascadeBias * 0.01f;
_fixedBias = configuration.fixedBias * configuration.fixedBias * configuration.fixedBias * 0.004f;
_slopeBias = configuration.slopeBias * configuration.slopeBias * configuration.slopeBias * 0.01f;
}
void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
@ -274,7 +273,8 @@ 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, _baseBias);
globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR,
_fixedBias, _slopeBias);
// Set the keylight render args
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));

View file

@ -64,10 +64,12 @@ public:
class RenderShadowCascadeSetupConfig : public render::Job::Config {
Q_OBJECT
Q_PROPERTY(float bias MEMBER bias NOTIFY dirty)
Q_PROPERTY(float fixedBias MEMBER fixedBias NOTIFY dirty)
Q_PROPERTY(float slopeBias MEMBER slopeBias NOTIFY dirty)
public:
float bias{ 0.25f };
float fixedBias{ 0.15f };
float slopeBias{ 0.55f };
signals:
void dirty();
@ -86,7 +88,8 @@ public:
private:
unsigned int _cascadeIndex;
float _baseBias{ 0.1f };
float _fixedBias{ 0.1f };
float _slopeBias{ 0.1f };
};
class RenderShadowCascadeTeardown {

View file

@ -83,19 +83,12 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve
return shadowAttenuation;
}
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias,
vec3 worldPosition, vec3 worldLightDir) {
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) {
if (!isShadowCascadeProjectedOnPixel(shadowTexcoord)) {
// If a point is not in the map, do not attenuate
return 1.0;
}
// After this, bias is in view units
bias *= getShadowAdaptiveBiasUnitScale(cascadeIndex);
// Transform to texcoord space (between 0 and 1)
float shadowDepth = abs(dot(worldPosition-getShadowFrustumPosition(cascadeIndex), worldLightDir));
bias = transformShadowAdaptiveBias(cascadeIndex, shadowDepth, bias);
// Add fixed bias
bias += getShadowBias(cascadeIndex);
float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL;
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
}
@ -106,12 +99,11 @@ float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDe
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 = 1.0/(ndotl*ndotl)-1.0;
float oneMinusNdotL = 1.0 - clamp(dot(worldLightDir, worldNormal), 0, 1);
vec2 cascadeAttenuations = vec2(1.0, 1.0);
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], bias, worldPosition.xyz, worldLightDir);
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], oneMinusNdotL);
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], bias, worldPosition.xyz, worldLightDir);
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], oneMinusNdotL);
}
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
// Falloff to max distance

View file

@ -37,21 +37,14 @@ float getShadowScale() {
return shadow.invMapSize;
}
float getShadowBias(int cascadeIndex) {
float getShadowFixedBias(int cascadeIndex) {
return shadow.cascades[cascadeIndex].fixedBias;
}
vec3 getShadowFrustumPosition(int cascadeIndex) {
return shadow.cascades[cascadeIndex].frustumPosition;
float getShadowSlopeBias(int cascadeIndex) {
return shadow.cascades[cascadeIndex].slopeBias;
}
float getShadowAdaptiveBiasUnitScale(int cascadeIndex) {
return shadow.cascades[cascadeIndex].adaptiveBiasUnitScale;
}
float transformShadowAdaptiveBias(int cascadeIndex, float shadowDepth, float depthBias) {
return shadow.cascades[cascadeIndex].adaptiveBiasTransformScale * depthBias / (shadowDepth*shadowDepth);
}
// Compute the texture coordinates from world coordinates
vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {

View file

@ -11,10 +11,8 @@
struct ShadowTransform {
MAT4 reprojection;
VEC3 frustumPosition;
float fixedBias;
float adaptiveBiasUnitScale;
float adaptiveBiasTransformScale;
float slopeBias;
float _padding1;
float _padding2;
};

View file

@ -14,7 +14,7 @@ var qml = Script.resolvePath('shadow.qml');
var window = new OverlayWindow({
title: 'Shadow Debug',
source: qml,
width: 200,
height: 90
width: 250,
height: 300
});
window.closed.connect(function() { Script.stop(); });

View file

@ -36,6 +36,14 @@ Column {
shadow1Config.enabled = false;
shadow2Config.enabled = false;
shadow3Config.enabled = false;
shadow0Config.isFrozen = false;
shadow1Config.isFrozen = false;
shadow2Config.isFrozen = false;
shadow3Config.isFrozen = false;
shadow0BoundConfig.isFrozen = false;
shadow1BoundConfig.isFrozen = false;
shadow2BoundConfig.isFrozen = false;
shadow3BoundConfig.isFrozen = false;
}
CheckBox {
@ -72,35 +80,68 @@ Column {
}
}
ConfigSlider {
label: qsTr("Cascade 0 bias")
label: qsTr("Cascade 0 fixed bias")
integral: false
config: Render.getConfig("RenderMainView.ShadowCascadeSetup0")
property: "bias"
property: "fixedBias"
max: 1.0
min: 0.01
min: 0.0
}
ConfigSlider {
label: qsTr("Cascade 1 bias")
label: qsTr("Cascade 1 fixed bias")
integral: false
config: Render.getConfig("RenderMainView.ShadowCascadeSetup1")
property: "bias"
property: "fixedBias"
max: 1.0
min: 0.01
min: 0.0
}
ConfigSlider {
label: qsTr("Cascade 2 bias")
label: qsTr("Cascade 2 fixed bias")
integral: false
config: Render.getConfig("RenderMainView.ShadowCascadeSetup2")
property: "bias"
property: "fixedBias"
max: 1.0
min: 0.01
min: 0.0
}
ConfigSlider {
label: qsTr("Cascade 3 bias")
label: qsTr("Cascade 3 fixed bias")
integral: false
config: Render.getConfig("RenderMainView.ShadowCascadeSetup3")
property: "bias"
property: "fixedBias"
max: 1.0
min: 0.01
min: 0.0
}
ConfigSlider {
label: qsTr("Cascade 0 slope bias")
integral: false
config: Render.getConfig("RenderMainView.ShadowCascadeSetup0")
property: "slopeBias"
max: 1.0
min: 0.0
}
ConfigSlider {
label: qsTr("Cascade 1 slope bias")
integral: false
config: Render.getConfig("RenderMainView.ShadowCascadeSetup1")
property: "slopeBias"
max: 1.0
min: 0.0
}
ConfigSlider {
label: qsTr("Cascade 2 slope bias")
integral: false
config: Render.getConfig("RenderMainView.ShadowCascadeSetup2")
property: "slopeBias"
max: 1.0
min: 0.0
}
ConfigSlider {
label: qsTr("Cascade 3 slope bias")
integral: false
config: Render.getConfig("RenderMainView.ShadowCascadeSetup3")
property: "slopeBias"
max: 1.0
min: 0.0
}
}