mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 17:01:18 +02:00
Switched to a simpler manual fixed/slope based shadow bias system. Automatic stuff fail most of the time
This commit is contained in:
parent
0324f41565
commit
f344e44d26
9 changed files with 78 additions and 69 deletions
|
@ -70,18 +70,6 @@ LightStage::Shadow::Schema::Schema() {
|
||||||
maxDistance = 20.0f;
|
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() :
|
LightStage::Shadow::Cascade::Cascade() :
|
||||||
_frustum{ std::make_shared<ViewFrustum>() },
|
_frustum{ std::make_shared<ViewFrustum>() },
|
||||||
_minDistance{ 0.0f },
|
_minDistance{ 0.0f },
|
||||||
|
@ -222,17 +210,14 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||||
auto position = viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection;
|
auto position = viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection;
|
||||||
// Update the buffer
|
// Update the buffer
|
||||||
auto& schema = _schemaBuffer.edit<Schema>();
|
auto& schema = _schemaBuffer.edit<Schema>();
|
||||||
auto cascadeIndex = 0;
|
|
||||||
for (auto& cascade : _cascades) {
|
for (auto& cascade : _cascades) {
|
||||||
cascade._frustum->setOrientation(orientation);
|
cascade._frustum->setOrientation(orientation);
|
||||||
cascade._frustum->setPosition(position);
|
cascade._frustum->setPosition(position);
|
||||||
schema.cascades[cascadeIndex].frustumPosition = position;
|
|
||||||
cascadeIndex++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 baseBias) {
|
float nearDepth, float farDepth, float fixedBias, float slopeBias) {
|
||||||
assert(nearDepth < farDepth);
|
assert(nearDepth < farDepth);
|
||||||
assert(cascadeIndex < _cascades.size());
|
assert(cascadeIndex < _cascades.size());
|
||||||
|
|
||||||
|
@ -283,8 +268,8 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co
|
||||||
auto& schema = _schemaBuffer.edit<Schema>();
|
auto& schema = _schemaBuffer.edit<Schema>();
|
||||||
auto& schemaCascade = schema.cascades[cascadeIndex];
|
auto& schemaCascade = schema.cascades[cascadeIndex];
|
||||||
schemaCascade.reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
schemaCascade.reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||||
schemaCascade.fixedBias = baseBias;
|
schemaCascade.fixedBias = fixedBias;
|
||||||
schema.updateCascade(cascadeIndex, *cascade._frustum);
|
schemaCascade.slopeBias = slopeBias;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
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& schema = _schemaBuffer.edit<Schema>();
|
||||||
auto& schemaCascade = schema.cascades[cascadeIndex];
|
auto& schemaCascade = schema.cascades[cascadeIndex];
|
||||||
schemaCascade.reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
|
schemaCascade.reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
|
||||||
schema.updateCascade(cascadeIndex, shadowFrustum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LightStage::Index LightStage::findLight(const LightPointer& light) const {
|
LightStage::Index LightStage::findLight(const LightPointer& light) const {
|
||||||
|
|
|
@ -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 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);
|
void setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum);
|
||||||
|
|
||||||
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
|
const UniformBufferView& getBuffer() const { return _schemaBuffer; }
|
||||||
|
@ -110,8 +110,6 @@ public:
|
||||||
|
|
||||||
Schema();
|
Schema();
|
||||||
|
|
||||||
void updateCascade(int cascadeIndex, const ViewFrustum& shadowFrustum);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
UniformBufferView _schemaBuffer = nullptr;
|
UniformBufferView _schemaBuffer = nullptr;
|
||||||
};
|
};
|
||||||
|
|
|
@ -256,9 +256,8 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderShadowCascadeSetup::configure(const Config& configuration) {
|
void RenderShadowCascadeSetup::configure(const Config& configuration) {
|
||||||
// I'm not very proud of this empirical adjustment
|
_fixedBias = configuration.fixedBias * configuration.fixedBias * configuration.fixedBias * 0.004f;
|
||||||
auto cascadeBias = configuration.bias * powf(1.1f, _cascadeIndex);
|
_slopeBias = configuration.slopeBias * configuration.slopeBias * configuration.slopeBias * 0.01f;
|
||||||
_baseBias = cascadeBias * cascadeBias * 0.01f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
|
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()) {
|
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, _baseBias);
|
globalShadow->setKeylightCascadeFrustum(_cascadeIndex, args->getViewFrustum(), SHADOW_FRUSTUM_NEAR, SHADOW_FRUSTUM_FAR,
|
||||||
|
_fixedBias, _slopeBias);
|
||||||
|
|
||||||
// Set the keylight render args
|
// Set the keylight render args
|
||||||
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));
|
args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum()));
|
||||||
|
|
|
@ -64,10 +64,12 @@ public:
|
||||||
|
|
||||||
class RenderShadowCascadeSetupConfig : public render::Job::Config {
|
class RenderShadowCascadeSetupConfig : public render::Job::Config {
|
||||||
Q_OBJECT
|
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:
|
public:
|
||||||
|
|
||||||
float bias{ 0.25f };
|
float fixedBias{ 0.15f };
|
||||||
|
float slopeBias{ 0.55f };
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void dirty();
|
void dirty();
|
||||||
|
@ -86,7 +88,8 @@ public:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
unsigned int _cascadeIndex;
|
unsigned int _cascadeIndex;
|
||||||
float _baseBias{ 0.1f };
|
float _fixedBias{ 0.1f };
|
||||||
|
float _slopeBias{ 0.1f };
|
||||||
};
|
};
|
||||||
|
|
||||||
class RenderShadowCascadeTeardown {
|
class RenderShadowCascadeTeardown {
|
||||||
|
|
|
@ -83,19 +83,12 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve
|
||||||
return shadowAttenuation;
|
return shadowAttenuation;
|
||||||
}
|
}
|
||||||
|
|
||||||
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float bias,
|
float evalShadowCascadeAttenuation(int cascadeIndex, ShadowSampleOffsets offsets, vec4 shadowTexcoord, float oneMinusNdotL) {
|
||||||
vec3 worldPosition, vec3 worldLightDir) {
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
// After this, bias is in view units
|
float bias = getShadowFixedBias(cascadeIndex) + getShadowSlopeBias(cascadeIndex) * oneMinusNdotL;
|
||||||
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);
|
|
||||||
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
|
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);
|
float cascadeMix = determineShadowCascadesOnPixel(worldPosition, viewDepth, cascadeShadowCoords, cascadeIndices);
|
||||||
|
|
||||||
// Adjust bias if we are at a grazing angle with light
|
// Adjust bias if we are at a grazing angle with light
|
||||||
float ndotl = dot(worldLightDir, worldNormal);
|
float oneMinusNdotL = 1.0 - clamp(dot(worldLightDir, worldNormal), 0, 1);
|
||||||
float bias = 1.0/(ndotl*ndotl)-1.0;
|
|
||||||
vec2 cascadeAttenuations = vec2(1.0, 1.0);
|
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()) {
|
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);
|
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
||||||
// Falloff to max distance
|
// Falloff to max distance
|
||||||
|
|
|
@ -37,21 +37,14 @@ float getShadowScale() {
|
||||||
return shadow.invMapSize;
|
return shadow.invMapSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
float getShadowBias(int cascadeIndex) {
|
float getShadowFixedBias(int cascadeIndex) {
|
||||||
return shadow.cascades[cascadeIndex].fixedBias;
|
return shadow.cascades[cascadeIndex].fixedBias;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getShadowFrustumPosition(int cascadeIndex) {
|
float getShadowSlopeBias(int cascadeIndex) {
|
||||||
return shadow.cascades[cascadeIndex].frustumPosition;
|
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
|
// Compute the texture coordinates from world coordinates
|
||||||
vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
|
vec4 evalShadowTexcoord(int cascadeIndex, vec4 position) {
|
||||||
|
|
|
@ -11,10 +11,8 @@
|
||||||
|
|
||||||
struct ShadowTransform {
|
struct ShadowTransform {
|
||||||
MAT4 reprojection;
|
MAT4 reprojection;
|
||||||
VEC3 frustumPosition;
|
|
||||||
float fixedBias;
|
float fixedBias;
|
||||||
float adaptiveBiasUnitScale;
|
float slopeBias;
|
||||||
float adaptiveBiasTransformScale;
|
|
||||||
float _padding1;
|
float _padding1;
|
||||||
float _padding2;
|
float _padding2;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,7 +14,7 @@ var qml = Script.resolvePath('shadow.qml');
|
||||||
var window = new OverlayWindow({
|
var window = new OverlayWindow({
|
||||||
title: 'Shadow Debug',
|
title: 'Shadow Debug',
|
||||||
source: qml,
|
source: qml,
|
||||||
width: 200,
|
width: 250,
|
||||||
height: 90
|
height: 300
|
||||||
});
|
});
|
||||||
window.closed.connect(function() { Script.stop(); });
|
window.closed.connect(function() { Script.stop(); });
|
|
@ -36,6 +36,14 @@ Column {
|
||||||
shadow1Config.enabled = false;
|
shadow1Config.enabled = false;
|
||||||
shadow2Config.enabled = false;
|
shadow2Config.enabled = false;
|
||||||
shadow3Config.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 {
|
CheckBox {
|
||||||
|
@ -72,35 +80,68 @@ Column {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConfigSlider {
|
ConfigSlider {
|
||||||
label: qsTr("Cascade 0 bias")
|
label: qsTr("Cascade 0 fixed bias")
|
||||||
integral: false
|
integral: false
|
||||||
config: Render.getConfig("RenderMainView.ShadowCascadeSetup0")
|
config: Render.getConfig("RenderMainView.ShadowCascadeSetup0")
|
||||||
property: "bias"
|
property: "fixedBias"
|
||||||
max: 1.0
|
max: 1.0
|
||||||
min: 0.01
|
min: 0.0
|
||||||
}
|
}
|
||||||
ConfigSlider {
|
ConfigSlider {
|
||||||
label: qsTr("Cascade 1 bias")
|
label: qsTr("Cascade 1 fixed bias")
|
||||||
integral: false
|
integral: false
|
||||||
config: Render.getConfig("RenderMainView.ShadowCascadeSetup1")
|
config: Render.getConfig("RenderMainView.ShadowCascadeSetup1")
|
||||||
property: "bias"
|
property: "fixedBias"
|
||||||
max: 1.0
|
max: 1.0
|
||||||
min: 0.01
|
min: 0.0
|
||||||
}
|
}
|
||||||
ConfigSlider {
|
ConfigSlider {
|
||||||
label: qsTr("Cascade 2 bias")
|
label: qsTr("Cascade 2 fixed bias")
|
||||||
integral: false
|
integral: false
|
||||||
config: Render.getConfig("RenderMainView.ShadowCascadeSetup2")
|
config: Render.getConfig("RenderMainView.ShadowCascadeSetup2")
|
||||||
property: "bias"
|
property: "fixedBias"
|
||||||
max: 1.0
|
max: 1.0
|
||||||
min: 0.01
|
min: 0.0
|
||||||
}
|
}
|
||||||
ConfigSlider {
|
ConfigSlider {
|
||||||
label: qsTr("Cascade 3 bias")
|
label: qsTr("Cascade 3 fixed bias")
|
||||||
integral: false
|
integral: false
|
||||||
config: Render.getConfig("RenderMainView.ShadowCascadeSetup3")
|
config: Render.getConfig("RenderMainView.ShadowCascadeSetup3")
|
||||||
property: "bias"
|
property: "fixedBias"
|
||||||
max: 1.0
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue