mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 18:50:00 +02:00
Trying to improve adaptive shadow bias
This commit is contained in:
parent
6b0b17ff63
commit
0324f41565
7 changed files with 58 additions and 23 deletions
|
@ -23,8 +23,6 @@ const glm::mat4 LightStage::Shadow::_biasMatrix{
|
||||||
0.5, 0.5, 0.5, 1.0 };
|
0.5, 0.5, 0.5, 1.0 };
|
||||||
const int LightStage::Shadow::MAP_SIZE = 1024;
|
const int LightStage::Shadow::MAP_SIZE = 1024;
|
||||||
|
|
||||||
static const auto MAX_BIAS = 0.006f;
|
|
||||||
|
|
||||||
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
|
||||||
|
|
||||||
LightStage::LightStage() {
|
LightStage::LightStage() {
|
||||||
|
@ -63,7 +61,7 @@ LightStage::LightStage() {
|
||||||
|
|
||||||
LightStage::Shadow::Schema::Schema() {
|
LightStage::Shadow::Schema::Schema() {
|
||||||
ShadowTransform defaultTransform;
|
ShadowTransform defaultTransform;
|
||||||
defaultTransform.bias = MAX_BIAS;
|
defaultTransform.fixedBias = 0.005f;
|
||||||
std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform);
|
std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform);
|
||||||
invMapSize = 1.0f / MAP_SIZE;
|
invMapSize = 1.0f / MAP_SIZE;
|
||||||
cascadeCount = 1;
|
cascadeCount = 1;
|
||||||
|
@ -72,6 +70,18 @@ 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 },
|
||||||
|
@ -210,13 +220,15 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
||||||
|
|
||||||
// Position the keylight frustum
|
// Position the keylight frustum
|
||||||
auto position = viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection;
|
auto position = viewFrustum.getPosition() - (nearDepth + farDepth)*lightDirection;
|
||||||
|
// Update the buffer
|
||||||
|
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++;
|
||||||
}
|
}
|
||||||
// Update the buffer
|
|
||||||
auto& schema = _schemaBuffer.edit<Schema>();
|
|
||||||
schema.lightDirInViewSpace = glm::inverse(viewFrustum.getView()) * glm::vec4(lightDirection, 0.f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& viewFrustum,
|
||||||
|
@ -269,8 +281,10 @@ 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();
|
auto& schemaCascade = schema.cascades[cascadeIndex];
|
||||||
schema.cascades[cascadeIndex].bias = baseBias;
|
schemaCascade.reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||||
|
schemaCascade.fixedBias = baseBias;
|
||||||
|
schema.updateCascade(cascadeIndex, *cascade._frustum);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const ViewFrustum& shadowFrustum) {
|
||||||
|
@ -281,7 +295,10 @@ void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const View
|
||||||
|
|
||||||
*cascade._frustum = shadowFrustum;
|
*cascade._frustum = shadowFrustum;
|
||||||
// Update the buffer
|
// Update the buffer
|
||||||
_schemaBuffer.edit<Schema>().cascades[cascadeIndex].reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
|
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 {
|
LightStage::Index LightStage::findLight(const LightPointer& light) const {
|
||||||
|
|
|
@ -110,6 +110,8 @@ public:
|
||||||
|
|
||||||
Schema();
|
Schema();
|
||||||
|
|
||||||
|
void updateCascade(int cascadeIndex, const ViewFrustum& shadowFrustum);
|
||||||
|
|
||||||
};
|
};
|
||||||
UniformBufferView _schemaBuffer = nullptr;
|
UniformBufferView _schemaBuffer = nullptr;
|
||||||
};
|
};
|
||||||
|
@ -213,6 +215,7 @@ protected:
|
||||||
Index _sunOffLightId;
|
Index _sunOffLightId;
|
||||||
|
|
||||||
Index _defaultLightId;
|
Index _defaultLightId;
|
||||||
|
|
||||||
};
|
};
|
||||||
using LightStagePointer = std::shared_ptr<LightStage>;
|
using LightStagePointer = std::shared_ptr<LightStage>;
|
||||||
|
|
||||||
|
|
|
@ -256,7 +256,9 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderShadowCascadeSetup::configure(const Config& configuration) {
|
void RenderShadowCascadeSetup::configure(const Config& configuration) {
|
||||||
_baseBias = configuration.bias * configuration.bias * 0.01f;
|
// I'm not very proud of this empirical adjustment
|
||||||
|
auto cascadeBias = configuration.bias * powf(1.1f, _cascadeIndex);
|
||||||
|
_baseBias = cascadeBias * cascadeBias * 0.01f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
|
void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderContext, Outputs& output) {
|
||||||
|
|
|
@ -67,7 +67,7 @@ class RenderShadowCascadeSetupConfig : public render::Job::Config {
|
||||||
Q_PROPERTY(float bias MEMBER bias NOTIFY dirty)
|
Q_PROPERTY(float bias MEMBER bias NOTIFY dirty)
|
||||||
public:
|
public:
|
||||||
|
|
||||||
float bias{ 0.5f };
|
float bias{ 0.25f };
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void dirty();
|
void dirty();
|
||||||
|
|
|
@ -83,11 +83,17 @@ 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 bias,
|
||||||
|
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
|
||||||
|
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
|
// Add fixed bias
|
||||||
bias += getShadowBias(cascadeIndex);
|
bias += getShadowBias(cascadeIndex);
|
||||||
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
|
return evalShadowAttenuationPCF(cascadeIndex, offsets, shadowTexcoord, bias);
|
||||||
|
@ -101,12 +107,11 @@ float evalShadowAttenuation(vec3 worldLightDir, vec4 worldPosition, float viewDe
|
||||||
|
|
||||||
// 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 ndotl = dot(worldLightDir, worldNormal);
|
||||||
float bias = 0.5*(1.0/(ndotl*ndotl)-1.0);
|
float bias = 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, offsets, cascadeShadowCoords[0], bias);
|
cascadeAttenuations.x = evalShadowCascadeAttenuation(cascadeIndices.x, offsets, cascadeShadowCoords[0], bias, worldPosition.xyz, worldLightDir);
|
||||||
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
if (cascadeMix > 0.0 && cascadeIndices.y < getShadowCascadeCount()) {
|
||||||
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], bias);
|
cascadeAttenuations.y = evalShadowCascadeAttenuation(cascadeIndices.y, offsets, cascadeShadowCoords[1], bias, worldPosition.xyz, worldLightDir);
|
||||||
}
|
}
|
||||||
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
float attenuation = mix(cascadeAttenuations.x, cascadeAttenuations.y, cascadeMix);
|
||||||
// Falloff to max distance
|
// Falloff to max distance
|
||||||
|
|
|
@ -38,11 +38,19 @@ float getShadowScale() {
|
||||||
}
|
}
|
||||||
|
|
||||||
float getShadowBias(int cascadeIndex) {
|
float getShadowBias(int cascadeIndex) {
|
||||||
return shadow.cascades[cascadeIndex].bias;
|
return shadow.cascades[cascadeIndex].fixedBias;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3 getShadowDirInViewSpace() {
|
vec3 getShadowFrustumPosition(int cascadeIndex) {
|
||||||
return shadow.lightDirInViewSpace;
|
return shadow.cascades[cascadeIndex].frustumPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -11,16 +11,16 @@
|
||||||
|
|
||||||
struct ShadowTransform {
|
struct ShadowTransform {
|
||||||
MAT4 reprojection;
|
MAT4 reprojection;
|
||||||
|
VEC3 frustumPosition;
|
||||||
float bias;
|
float fixedBias;
|
||||||
|
float adaptiveBiasUnitScale;
|
||||||
|
float adaptiveBiasTransformScale;
|
||||||
float _padding1;
|
float _padding1;
|
||||||
float _padding2;
|
float _padding2;
|
||||||
float _padding3;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ShadowParameters {
|
struct ShadowParameters {
|
||||||
ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT];
|
ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT];
|
||||||
VEC3 lightDirInViewSpace;
|
|
||||||
int cascadeCount;
|
int cascadeCount;
|
||||||
float invMapSize;
|
float invMapSize;
|
||||||
float invCascadeBlendWidth;
|
float invCascadeBlendWidth;
|
||||||
|
|
Loading…
Reference in a new issue