mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 04:44:11 +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;
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -11,10 +11,8 @@
|
|||
|
||||
struct ShadowTransform {
|
||||
MAT4 reprojection;
|
||||
VEC3 frustumPosition;
|
||||
float fixedBias;
|
||||
float adaptiveBiasUnitScale;
|
||||
float adaptiveBiasTransformScale;
|
||||
float slopeBias;
|
||||
float _padding1;
|
||||
float _padding2;
|
||||
};
|
||||
|
|
|
@ -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(); });
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue