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);
|
settings.AddPass(depthPassIndex, depthPass);
|
||||||
|
|
||||||
MaterialPass shadowPass = depthPass;
|
MaterialPass shadowPass = depthPass;
|
||||||
|
shadowPass.options[CRC32("ShadowPass")] = true;
|
||||||
|
shadowPass.states.frontFace = FrontFace::Clockwise;
|
||||||
shadowPass.states.depthClamp = enabledFeatures.depthClamping;
|
shadowPass.states.depthClamp = enabledFeatures.depthClamping;
|
||||||
settings.AddPass(shadowPassIndex, shadowPass);
|
settings.AddPass(shadowPassIndex, shadowPass);
|
||||||
|
|
||||||
|
|
@ -290,6 +292,8 @@ namespace Nz
|
||||||
settings.AddPass(depthPassIndex, depthPass);
|
settings.AddPass(depthPassIndex, depthPass);
|
||||||
|
|
||||||
MaterialPass shadowPass = depthPass;
|
MaterialPass shadowPass = depthPass;
|
||||||
|
shadowPass.options[CRC32("ShadowPass")] = true;
|
||||||
|
shadowPass.states.frontFace = FrontFace::Clockwise;
|
||||||
shadowPass.states.depthClamp = enabledFeatures.depthClamping;
|
shadowPass.states.depthClamp = enabledFeatures.depthClamping;
|
||||||
settings.AddPass(shadowPassIndex, shadowPass);
|
settings.AddPass(shadowPassIndex, shadowPass);
|
||||||
|
|
||||||
|
|
@ -316,6 +320,8 @@ namespace Nz
|
||||||
settings.AddPass(depthPassIndex, depthPass);
|
settings.AddPass(depthPassIndex, depthPass);
|
||||||
|
|
||||||
MaterialPass shadowPass = depthPass;
|
MaterialPass shadowPass = depthPass;
|
||||||
|
shadowPass.options[CRC32("ShadowPass")] = true;
|
||||||
|
shadowPass.states.frontFace = FrontFace::Clockwise;
|
||||||
shadowPass.states.depthClamp = enabledFeatures.depthClamping;
|
shadowPass.states.depthClamp = enabledFeatures.depthClamping;
|
||||||
settings.AddPass(shadowPassIndex, shadowPass);
|
settings.AddPass(shadowPassIndex, shadowPass);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ namespace Nz
|
||||||
settings.AddValueProperty<Color>("BaseColor", Color::White());
|
settings.AddValueProperty<Color>("BaseColor", Color::White());
|
||||||
settings.AddValueProperty<bool>("AlphaTest", false);
|
settings.AddValueProperty<bool>("AlphaTest", false);
|
||||||
settings.AddValueProperty<float>("AlphaTestThreshold", 0.2f);
|
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("BaseColorMap", ImageType::E2D);
|
||||||
settings.AddTextureProperty("AlphaMap", ImageType::E2D);
|
settings.AddTextureProperty("AlphaMap", ImageType::E2D);
|
||||||
settings.AddPropertyHandler(std::make_unique<OptionValuePropertyHandler>("AlphaTest", "AlphaTest"));
|
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<TexturePropertyHandler>("AlphaMap", "HasAlphaTexture"));
|
||||||
settings.AddPropertyHandler(std::make_unique<UniformValuePropertyHandler>("BaseColor"));
|
settings.AddPropertyHandler(std::make_unique<UniformValuePropertyHandler>("BaseColor"));
|
||||||
settings.AddPropertyHandler(std::make_unique<UniformValuePropertyHandler>("AlphaTestThreshold"));
|
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)
|
void PredefinedMaterials::AddPbrSettings(MaterialSettings& settings)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import SkinLinearPosition from Engine.SkinningLinear;
|
||||||
// Pass-specific options
|
// Pass-specific options
|
||||||
option DepthPass: bool = false;
|
option DepthPass: bool = false;
|
||||||
option DistanceDepth: bool = false;
|
option DistanceDepth: bool = false;
|
||||||
|
option ShadowPass: bool = false;
|
||||||
|
|
||||||
// Material options
|
// Material options
|
||||||
option HasBaseColorTexture: bool = false;
|
option HasBaseColorTexture: bool = false;
|
||||||
|
|
@ -23,12 +24,14 @@ option BillboardSizeRotLocation: i32 = -1;
|
||||||
|
|
||||||
// Vertex declaration related options
|
// Vertex declaration related options
|
||||||
option VertexColorLoc: i32 = -1;
|
option VertexColorLoc: i32 = -1;
|
||||||
|
option VertexNormalLoc: i32 = -1;
|
||||||
option VertexPositionLoc: i32;
|
option VertexPositionLoc: i32;
|
||||||
option VertexUvLoc: i32 = -1;
|
option VertexUvLoc: i32 = -1;
|
||||||
|
|
||||||
option VertexJointIndicesLoc: i32 = -1;
|
option VertexJointIndicesLoc: i32 = -1;
|
||||||
option VertexJointWeightsLoc: i32 = -1;
|
option VertexJointWeightsLoc: i32 = -1;
|
||||||
|
|
||||||
|
const HasNormal = (VertexNormalLoc >= 0);
|
||||||
const HasVertexColor = (VertexColorLoc >= 0);
|
const HasVertexColor = (VertexColorLoc >= 0);
|
||||||
const HasColor = (HasVertexColor || Billboard);
|
const HasColor = (HasVertexColor || Billboard);
|
||||||
const HasUV = (VertexUvLoc >= 0);
|
const HasUV = (VertexUvLoc >= 0);
|
||||||
|
|
@ -40,6 +43,12 @@ struct MaterialSettings
|
||||||
[tag("AlphaTestThreshold")]
|
[tag("AlphaTestThreshold")]
|
||||||
AlphaThreshold: f32,
|
AlphaThreshold: f32,
|
||||||
|
|
||||||
|
[tag("ShadowMapNormalOffset")]
|
||||||
|
ShadowMapNormalOffset: f32,
|
||||||
|
|
||||||
|
[tag("ShadowPosScale")]
|
||||||
|
ShadowPosScale: f32,
|
||||||
|
|
||||||
[tag("BaseColor")]
|
[tag("BaseColor")]
|
||||||
BaseColor: vec4[f32]
|
BaseColor: vec4[f32]
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +166,9 @@ struct VertIn
|
||||||
[cond(HasVertexColor), location(VertexColorLoc)]
|
[cond(HasVertexColor), location(VertexColorLoc)]
|
||||||
color: vec4[f32],
|
color: vec4[f32],
|
||||||
|
|
||||||
|
[cond(HasNormal), location(VertexNormalLoc)]
|
||||||
|
normal: vec3[f32],
|
||||||
|
|
||||||
[cond(HasUV), location(VertexUvLoc)]
|
[cond(HasUV), location(VertexUvLoc)]
|
||||||
uv: vec2[f32],
|
uv: vec2[f32],
|
||||||
|
|
||||||
|
|
@ -230,6 +242,13 @@ fn main(input: VertIn) -> VertToFrag
|
||||||
else
|
else
|
||||||
pos = input.pos;
|
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 worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0);
|
||||||
|
|
||||||
let output: VertToFrag;
|
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 lightProjPos = light.viewProjMatrices[cascadeIndex] * vec4[f32](worldPos, 1.0);
|
||||||
let shadowCoords = lightProjPos.xyz / lightProjPos.w;
|
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;
|
shadowFactor = 0.0;
|
||||||
[unroll]
|
[unroll]
|
||||||
for x in -1 -> 2
|
for x in -1 -> 2
|
||||||
|
|
@ -45,7 +41,7 @@ fn ComputeDirectionalLightShadow(light: DirectionalLight, shadowmap: depth_sampl
|
||||||
for y in -1 -> 2
|
for y in -1 -> 2
|
||||||
{
|
{
|
||||||
let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize;
|
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;
|
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 start = vec3[f32](offset * 0.5, offset * 0.5, offset * 0.5);
|
||||||
const shadowContribution = 1.0 / f32(sampleCount * sampleCount * sampleCount);
|
const shadowContribution = 1.0 / f32(sampleCount * sampleCount * sampleCount);
|
||||||
|
|
||||||
let bias = 0.05;
|
|
||||||
|
|
||||||
[unroll]
|
[unroll]
|
||||||
for x in 0 -> sampleCount
|
for x in 0 -> sampleCount
|
||||||
{
|
{
|
||||||
|
|
@ -91,7 +85,7 @@ fn ComputePointLightShadow(light: PointLight, shadowmap: sampler_cube[f32], dist
|
||||||
let closestDepth = shadowmap.Sample(sampleDir).r;
|
let closestDepth = shadowmap.Sample(sampleDir).r;
|
||||||
closestDepth *= light.radius;
|
closestDepth *= light.radius;
|
||||||
|
|
||||||
if (closestDepth > dist - bias)
|
if (closestDepth > dist)
|
||||||
shadowFactor += shadowContribution;
|
shadowFactor += shadowContribution;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -110,9 +104,6 @@ fn ComputeSpotLightShadow(light: SpotLight, shadowmap: depth_sampler2D[f32], wor
|
||||||
{
|
{
|
||||||
if (light.invShadowMapSize.x > 0.0)
|
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 lightProjPos = light.viewProjMatrix * vec4[f32](worldPos, 1.0);
|
||||||
let shadowCoords = lightProjPos.xyz / lightProjPos.w;
|
let shadowCoords = lightProjPos.xyz / lightProjPos.w;
|
||||||
|
|
||||||
|
|
@ -124,7 +115,7 @@ fn ComputeSpotLightShadow(light: SpotLight, shadowmap: depth_sampler2D[f32], wor
|
||||||
for y in -1 -> 2
|
for y in -1 -> 2
|
||||||
{
|
{
|
||||||
let coords = shadowCoords.xy + vec2[f32](f32(x), f32(y)) * light.invShadowMapSize;
|
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;
|
shadowFactor /= 9.0;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear;
|
||||||
// Pass-specific options
|
// Pass-specific options
|
||||||
option DepthPass: bool = false;
|
option DepthPass: bool = false;
|
||||||
option DistanceDepth: bool = false;
|
option DistanceDepth: bool = false;
|
||||||
|
option ShadowPass: bool = false;
|
||||||
|
|
||||||
// Basic material options
|
// Basic material options
|
||||||
option HasBaseColorTexture: bool = false;
|
option HasBaseColorTexture: bool = false;
|
||||||
|
|
@ -58,6 +59,12 @@ struct MaterialSettings
|
||||||
[tag("AlphaTestThreshold")]
|
[tag("AlphaTestThreshold")]
|
||||||
AlphaThreshold: f32,
|
AlphaThreshold: f32,
|
||||||
|
|
||||||
|
[tag("ShadowMapNormalOffset")]
|
||||||
|
ShadowMapNormalOffset: f32,
|
||||||
|
|
||||||
|
[tag("ShadowPosScale")]
|
||||||
|
ShadowPosScale: f32,
|
||||||
|
|
||||||
[tag("BaseColor")]
|
[tag("BaseColor")]
|
||||||
BaseColor: vec4[f32],
|
BaseColor: vec4[f32],
|
||||||
|
|
||||||
|
|
@ -403,6 +410,13 @@ fn main(input: VertIn) -> VertToFrag
|
||||||
normal = input.normal;
|
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 worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0);
|
||||||
|
|
||||||
let output: VertToFrag;
|
let output: VertToFrag;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import SkinLinearPosition, SkinLinearPositionNormal from Engine.SkinningLinear;
|
||||||
// Pass-specific options
|
// Pass-specific options
|
||||||
option DepthPass: bool = false;
|
option DepthPass: bool = false;
|
||||||
option DistanceDepth: bool = false;
|
option DistanceDepth: bool = false;
|
||||||
|
option ShadowPass: bool = false;
|
||||||
|
|
||||||
// Basic material options
|
// Basic material options
|
||||||
option HasBaseColorTexture: bool = false;
|
option HasBaseColorTexture: bool = false;
|
||||||
|
|
@ -59,6 +60,12 @@ struct MaterialSettings
|
||||||
[tag("AlphaTestThreshold")]
|
[tag("AlphaTestThreshold")]
|
||||||
AlphaThreshold: f32,
|
AlphaThreshold: f32,
|
||||||
|
|
||||||
|
[tag("ShadowMapNormalOffset")]
|
||||||
|
ShadowMapNormalOffset: f32,
|
||||||
|
|
||||||
|
[tag("ShadowPosScale")]
|
||||||
|
ShadowPosScale: f32,
|
||||||
|
|
||||||
[tag("BaseColor")]
|
[tag("BaseColor")]
|
||||||
BaseColor: vec4[f32],
|
BaseColor: vec4[f32],
|
||||||
}
|
}
|
||||||
|
|
@ -387,6 +394,13 @@ fn main(input: VertIn) -> VertToFrag
|
||||||
normal = input.normal;
|
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 worldPosition = instanceData.worldMatrix * vec4[f32](pos, 1.0);
|
||||||
|
|
||||||
let output: VertToFrag;
|
let output: VertToFrag;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue