mirror of
https://github.com/overte-org/overte.git
synced 2025-04-20 11:45:36 +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 };
|
||||
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 };
|
||||
|
||||
LightStage::LightStage() {
|
||||
|
@ -63,7 +61,7 @@ LightStage::LightStage() {
|
|||
|
||||
LightStage::Shadow::Schema::Schema() {
|
||||
ShadowTransform defaultTransform;
|
||||
defaultTransform.bias = MAX_BIAS;
|
||||
defaultTransform.fixedBias = 0.005f;
|
||||
std::fill(cascades, cascades + SHADOW_CASCADE_MAX_COUNT, defaultTransform);
|
||||
invMapSize = 1.0f / MAP_SIZE;
|
||||
cascadeCount = 1;
|
||||
|
@ -72,6 +70,18 @@ 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 },
|
||||
|
@ -210,13 +220,15 @@ void LightStage::Shadow::setKeylightFrustum(const ViewFrustum& viewFrustum,
|
|||
|
||||
// Position the keylight frustum
|
||||
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++;
|
||||
}
|
||||
// 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,
|
||||
|
@ -269,8 +281,10 @@ void LightStage::Shadow::setKeylightCascadeFrustum(unsigned int cascadeIndex, co
|
|||
|
||||
// Update the buffer
|
||||
auto& schema = _schemaBuffer.edit<Schema>();
|
||||
schema.cascades[cascadeIndex].reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||
schema.cascades[cascadeIndex].bias = baseBias;
|
||||
auto& schemaCascade = schema.cascades[cascadeIndex];
|
||||
schemaCascade.reprojection = _biasMatrix * ortho * shadowViewInverse.getMatrix();
|
||||
schemaCascade.fixedBias = baseBias;
|
||||
schema.updateCascade(cascadeIndex, *cascade._frustum);
|
||||
}
|
||||
|
||||
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;
|
||||
// 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 {
|
||||
|
|
|
@ -110,6 +110,8 @@ public:
|
|||
|
||||
Schema();
|
||||
|
||||
void updateCascade(int cascadeIndex, const ViewFrustum& shadowFrustum);
|
||||
|
||||
};
|
||||
UniformBufferView _schemaBuffer = nullptr;
|
||||
};
|
||||
|
@ -213,6 +215,7 @@ protected:
|
|||
Index _sunOffLightId;
|
||||
|
||||
Index _defaultLightId;
|
||||
|
||||
};
|
||||
using LightStagePointer = std::shared_ptr<LightStage>;
|
||||
|
||||
|
|
|
@ -256,7 +256,9 @@ void RenderShadowSetup::run(const render::RenderContextPointer& renderContext) {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -67,7 +67,7 @@ class RenderShadowCascadeSetupConfig : public render::Job::Config {
|
|||
Q_PROPERTY(float bias MEMBER bias NOTIFY dirty)
|
||||
public:
|
||||
|
||||
float bias{ 0.5f };
|
||||
float bias{ 0.25f };
|
||||
|
||||
signals:
|
||||
void dirty();
|
||||
|
|
|
@ -83,11 +83,17 @@ float evalShadowAttenuationPCF(int cascadeIndex, ShadowSampleOffsets offsets, ve
|
|||
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 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);
|
||||
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
|
||||
float ndotl = dot(worldLightDir, worldNormal);
|
||||
float bias = 0.5*(1.0/(ndotl*ndotl)-1.0);
|
||||
bias *= getShadowScale();
|
||||
float bias = 1.0/(ndotl*ndotl)-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()) {
|
||||
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);
|
||||
// Falloff to max distance
|
||||
|
|
|
@ -38,11 +38,19 @@ float getShadowScale() {
|
|||
}
|
||||
|
||||
float getShadowBias(int cascadeIndex) {
|
||||
return shadow.cascades[cascadeIndex].bias;
|
||||
return shadow.cascades[cascadeIndex].fixedBias;
|
||||
}
|
||||
|
||||
vec3 getShadowDirInViewSpace() {
|
||||
return shadow.lightDirInViewSpace;
|
||||
vec3 getShadowFrustumPosition(int cascadeIndex) {
|
||||
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
|
||||
|
|
|
@ -11,16 +11,16 @@
|
|||
|
||||
struct ShadowTransform {
|
||||
MAT4 reprojection;
|
||||
|
||||
float bias;
|
||||
VEC3 frustumPosition;
|
||||
float fixedBias;
|
||||
float adaptiveBiasUnitScale;
|
||||
float adaptiveBiasTransformScale;
|
||||
float _padding1;
|
||||
float _padding2;
|
||||
float _padding3;
|
||||
};
|
||||
|
||||
struct ShadowParameters {
|
||||
ShadowTransform cascades[SHADOW_CASCADE_MAX_COUNT];
|
||||
VEC3 lightDirInViewSpace;
|
||||
int cascadeCount;
|
||||
float invMapSize;
|
||||
float invCascadeBlendWidth;
|
||||
|
|
Loading…
Reference in a new issue