Graphics/ShadowMapping: Replace bias by a position scale
Inspired by Godot (see https://github.com/godotengine/godot-proposals/issues/4517)
This commit is contained in:
parent
b04d7662ce
commit
6a9177a109
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ namespace Nz
|
|||
settings.AddValueProperty<Color>("BaseColor", Color::White());
|
||||
settings.AddValueProperty<bool>("AlphaTest", false);
|
||||
settings.AddValueProperty<float>("AlphaTestThreshold", 0.2f);
|
||||
settings.AddValueProperty<float>("ShadowMapNormalOffset", 0.f);
|
||||
settings.AddValueProperty<float>("ShadowPosScale", 1.f - 0.0025f);
|
||||
settings.AddTextureProperty("BaseColorMap", ImageType::E2D);
|
||||
settings.AddTextureProperty("AlphaMap", ImageType::E2D);
|
||||
settings.AddPropertyHandler(std::make_unique<OptionValuePropertyHandler>("AlphaTest", "AlphaTest"));
|
||||
|
|
@ -23,6 +25,8 @@ namespace Nz
|
|||
settings.AddPropertyHandler(std::make_unique<TexturePropertyHandler>("AlphaMap", "HasAlphaTexture"));
|
||||
settings.AddPropertyHandler(std::make_unique<UniformValuePropertyHandler>("BaseColor"));
|
||||
settings.AddPropertyHandler(std::make_unique<UniformValuePropertyHandler>("AlphaTestThreshold"));
|
||||
settings.AddPropertyHandler(std::make_unique<UniformValuePropertyHandler>("ShadowMapNormalOffset"));
|
||||
settings.AddPropertyHandler(std::make_unique<UniformValuePropertyHandler>("ShadowPosScale"));
|
||||
}
|
||||
|
||||
void PredefinedMaterials::AddPbrSettings(MaterialSettings& settings)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in New Issue