From 6a9177a1091258752428ebaa594781be7b8ed1b7 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sat, 21 Oct 2023 19:31:07 +0200 Subject: [PATCH] Graphics/ShadowMapping: Replace bias by a position scale Inspired by Godot (see https://github.com/godotengine/godot-proposals/issues/4517) --- src/Nazara/Graphics/Graphics.cpp | 6 ++++++ src/Nazara/Graphics/PredefinedMaterials.cpp | 4 ++++ .../Resources/Shaders/BasicMaterial.nzsl | 19 +++++++++++++++++++ .../Shaders/Modules/Engine/LightShadow.nzsl | 15 +++------------ .../Resources/Shaders/PhongMaterial.nzsl | 14 ++++++++++++++ .../Shaders/PhysicallyBasedMaterial.nzsl | 14 ++++++++++++++ 6 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index c0a16e323..d1d5982ad 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -264,6 +264,8 @@ namespace Nz settings.AddPass(depthPassIndex, depthPass); MaterialPass shadowPass = depthPass; + shadowPass.options[CRC32("ShadowPass")] = true; + shadowPass.states.frontFace = FrontFace::Clockwise; shadowPass.states.depthClamp = enabledFeatures.depthClamping; settings.AddPass(shadowPassIndex, shadowPass); @@ -290,6 +292,8 @@ namespace Nz settings.AddPass(depthPassIndex, depthPass); MaterialPass shadowPass = depthPass; + shadowPass.options[CRC32("ShadowPass")] = true; + shadowPass.states.frontFace = FrontFace::Clockwise; shadowPass.states.depthClamp = enabledFeatures.depthClamping; settings.AddPass(shadowPassIndex, shadowPass); @@ -316,6 +320,8 @@ namespace Nz settings.AddPass(depthPassIndex, depthPass); MaterialPass shadowPass = depthPass; + shadowPass.options[CRC32("ShadowPass")] = true; + shadowPass.states.frontFace = FrontFace::Clockwise; shadowPass.states.depthClamp = enabledFeatures.depthClamping; settings.AddPass(shadowPassIndex, shadowPass); diff --git a/src/Nazara/Graphics/PredefinedMaterials.cpp b/src/Nazara/Graphics/PredefinedMaterials.cpp index 044a473a0..acdc7f399 100644 --- a/src/Nazara/Graphics/PredefinedMaterials.cpp +++ b/src/Nazara/Graphics/PredefinedMaterials.cpp @@ -16,6 +16,8 @@ namespace Nz settings.AddValueProperty("BaseColor", Color::White()); settings.AddValueProperty("AlphaTest", false); settings.AddValueProperty("AlphaTestThreshold", 0.2f); + settings.AddValueProperty("ShadowMapNormalOffset", 0.f); + settings.AddValueProperty("ShadowPosScale", 1.f - 0.0025f); settings.AddTextureProperty("BaseColorMap", ImageType::E2D); settings.AddTextureProperty("AlphaMap", ImageType::E2D); settings.AddPropertyHandler(std::make_unique("AlphaTest", "AlphaTest")); @@ -23,6 +25,8 @@ namespace Nz settings.AddPropertyHandler(std::make_unique("AlphaMap", "HasAlphaTexture")); settings.AddPropertyHandler(std::make_unique("BaseColor")); settings.AddPropertyHandler(std::make_unique("AlphaTestThreshold")); + settings.AddPropertyHandler(std::make_unique("ShadowMapNormalOffset")); + settings.AddPropertyHandler(std::make_unique("ShadowPosScale")); } void PredefinedMaterials::AddPbrSettings(MaterialSettings& settings) diff --git a/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl index e67f8d5f0..ddbc7d3df 100644 --- a/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/BasicMaterial.nzsl @@ -9,6 +9,7 @@ import SkinLinearPosition from Engine.SkinningLinear; // Pass-specific options option DepthPass: bool = false; option DistanceDepth: bool = false; +option ShadowPass: bool = false; // Material options option HasBaseColorTexture: bool = false; @@ -23,12 +24,14 @@ option BillboardSizeRotLocation: i32 = -1; // Vertex declaration related options option VertexColorLoc: i32 = -1; +option VertexNormalLoc: i32 = -1; option VertexPositionLoc: i32; option VertexUvLoc: i32 = -1; option VertexJointIndicesLoc: i32 = -1; option VertexJointWeightsLoc: i32 = -1; +const HasNormal = (VertexNormalLoc >= 0); const HasVertexColor = (VertexColorLoc >= 0); const HasColor = (HasVertexColor || Billboard); const HasUV = (VertexUvLoc >= 0); @@ -40,6 +43,12 @@ struct MaterialSettings [tag("AlphaTestThreshold")] AlphaThreshold: f32, + [tag("ShadowMapNormalOffset")] + ShadowMapNormalOffset: f32, + + [tag("ShadowPosScale")] + ShadowPosScale: f32, + [tag("BaseColor")] BaseColor: vec4[f32] } @@ -157,6 +166,9 @@ struct VertIn [cond(HasVertexColor), location(VertexColorLoc)] color: vec4[f32], + [cond(HasNormal), location(VertexNormalLoc)] + normal: vec3[f32], + [cond(HasUV), location(VertexUvLoc)] uv: vec2[f32], @@ -230,6 +242,13 @@ fn main(input: VertIn) -> VertToFrag else pos = input.pos; + const if (ShadowPass) + { + pos *= settings.ShadowPosScale; + const if (HasNormal) + pos -= input.normal * settings.ShadowMapNormalOffset; + } + let worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0); let output: VertToFrag; diff --git a/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightShadow.nzsl b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightShadow.nzsl index c2e189af1..df3ca02e1 100644 --- a/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightShadow.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/Modules/Engine/LightShadow.nzsl @@ -33,10 +33,6 @@ fn ComputeDirectionalLightShadow(light: DirectionalLight, shadowmap: depth_sampl let lightProjPos = light.viewProjMatrices[cascadeIndex] * vec4[f32](worldPos, 1.0); let shadowCoords = lightProjPos.xyz / lightProjPos.w; - // calculate bias (based on depth map resolution and slope) - let bias = max(0.05 * (1.0 - lambert), 0.005); - bias *= 1.0 / (light.cascadeDistances[cascadeIndex] * 0.05); - shadowFactor = 0.0; [unroll] for x in -1 -> 2 @@ -45,7 +41,7 @@ fn ComputeDirectionalLightShadow(light: DirectionalLight, shadowmap: depth_sampl for y in -1 -> 2 { let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize; - shadowFactor += shadowmap.SampleDepthComp(vec3[f32](coords, f32(cascadeIndex)), shadowCoords.z - bias).r; + shadowFactor += shadowmap.SampleDepthComp(vec3[f32](coords, f32(cascadeIndex)), shadowCoords.z).r; } } shadowFactor /= 9.0; @@ -74,8 +70,6 @@ fn ComputePointLightShadow(light: PointLight, shadowmap: sampler_cube[f32], dist const start = vec3[f32](offset * 0.5, offset * 0.5, offset * 0.5); const shadowContribution = 1.0 / f32(sampleCount * sampleCount * sampleCount); - let bias = 0.05; - [unroll] for x in 0 -> sampleCount { @@ -91,7 +85,7 @@ fn ComputePointLightShadow(light: PointLight, shadowmap: sampler_cube[f32], dist let closestDepth = shadowmap.Sample(sampleDir).r; closestDepth *= light.radius; - if (closestDepth > dist - bias) + if (closestDepth > dist) shadowFactor += shadowContribution; } } @@ -110,9 +104,6 @@ fn ComputeSpotLightShadow(light: SpotLight, shadowmap: depth_sampler2D[f32], wor { if (light.invShadowMapSize.x > 0.0) { - let bias = 0.0005 * tan(acos(lambert)); - bias = clamp(bias, 0.0, 0.01); - let lightProjPos = light.viewProjMatrix * vec4[f32](worldPos, 1.0); let shadowCoords = lightProjPos.xyz / lightProjPos.w; @@ -124,7 +115,7 @@ fn ComputeSpotLightShadow(light: SpotLight, shadowmap: depth_sampler2D[f32], wor for y in -1 -> 2 { let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize; - shadowFactor += shadowmap.SampleDepthComp(coords, shadowCoords.z - bias).r; + shadowFactor += shadowmap.SampleDepthComp(coords, shadowCoords.z).r; } } shadowFactor /= 9.0; diff --git a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl index e3864876d..dba2848fa 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/PhongMaterial.nzsl @@ -12,6 +12,7 @@ import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear; // Pass-specific options option DepthPass: bool = false; option DistanceDepth: bool = false; +option ShadowPass: bool = false; // Basic material options option HasBaseColorTexture: bool = false; @@ -58,6 +59,12 @@ struct MaterialSettings [tag("AlphaTestThreshold")] AlphaThreshold: f32, + [tag("ShadowMapNormalOffset")] + ShadowMapNormalOffset: f32, + + [tag("ShadowPosScale")] + ShadowPosScale: f32, + [tag("BaseColor")] BaseColor: vec4[f32], @@ -403,6 +410,13 @@ fn main(input: VertIn) -> VertToFrag normal = input.normal; } + const if (ShadowPass) + { + pos *= settings.ShadowPosScale; + const if (HasNormal) + pos -= input.normal * settings.ShadowMapNormalOffset; + } + let worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0); let output: VertToFrag; diff --git a/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl b/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl index 86b8ef77d..20f189fb6 100644 --- a/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl +++ b/src/Nazara/Graphics/Resources/Shaders/PhysicallyBasedMaterial.nzsl @@ -12,6 +12,7 @@ import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear; // Pass-specific options option DepthPass: bool = false; option DistanceDepth: bool = false; +option ShadowPass: bool = false; // Basic material options option HasBaseColorTexture: bool = false; @@ -59,6 +60,12 @@ struct MaterialSettings [tag("AlphaTestThreshold")] AlphaThreshold: f32, + [tag("ShadowMapNormalOffset")] + ShadowMapNormalOffset: f32, + + [tag("ShadowPosScale")] + ShadowPosScale: f32, + [tag("BaseColor")] BaseColor: vec4[f32], } @@ -387,6 +394,13 @@ fn main(input: VertIn) -> VertToFrag normal = input.normal; } + const if (ShadowPass) + { + pos *= settings.ShadowPosScale; + const if (HasNormal) + pos -= input.normal * settings.ShadowMapNormalOffset; + } + let worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0); let output: VertToFrag;