Fix normal bias for spotlights.

The normal bias is now computed correctly, this requires to compute
the z in lightspace in the shader.
Note that this would not work as well if we used LISPSM, but we'll
cross that bridge when we get there.
This commit is contained in:
Mathias Agopian
2021-11-04 14:56:00 -07:00
committed by Mathias Agopian
parent 21db695e79
commit f9aaf5c42e
4 changed files with 41 additions and 18 deletions
+5 -2
View File
@@ -427,8 +427,11 @@ void ShadowMap::updateSpot(const FScene::LightSoa& lightData, size_t index,
// Project receivers, casters and view onto near plane,
// compute intersection of that which gives the l,r,t,b planes
// FIXME: texelSizeWorldSpace doesn't work for spotlights
mTexelSizeWs = 0; //texelSizeWorldSpace(Mp, mat4f(MbMt));
// For spotlights, we store the texel size at 1 world unit
// The size of a texel in world unit is given by: (near/dimension) / lightspace.z,
// when computing the required bias we need a half-texel size, so we multiply by 0.5 here.
// Note: this would not work with LISPSM, which warps the texture space.
mTexelSizeWs = 0.5f * nearPlane / float(mShadowMapInfo.shadowDimension);
if (!mShadowMapInfo.vsm) {
mLightSpace = St;
+31 -12
View File
@@ -8,18 +8,37 @@
* The returned point may contain a bias to attempt to eliminate common
* shadowing artifacts such as "acne". To achieve this, the world space
* normal at the point must also be passed to this function.
* Normal bias is not used for VSM.
*/
highp vec4 computeLightSpacePosition(const highp vec3 p, const highp vec3 n, const highp vec3 l,
const float b, const highp mat4 lightFromWorldMatrix) {
#if defined(HAS_VSM)
// VSM don't apply the shadow bias
highp vec4 lightSpacePosition = mulMat4x4Float3(lightFromWorldMatrix, p);
#else
float NoL = saturate(dot(n, l));
float sinTheta = sqrt(1.0 - NoL * NoL);
highp vec3 offsetPosition = p + n * (sinTheta * b);
highp vec4 lightSpacePosition = mulMat4x4Float3(lightFromWorldMatrix, offsetPosition);
#endif
return lightSpacePosition;
#if defined(HAS_DIRECTIONAL_LIGHTING)
highp vec4 computeLightSpacePositionDirectional(const highp vec3 p, const highp vec3 n,
const highp vec3 l, const float b, const highp mat4 lightFromWorldMatrix) {
return mulMat4x4Float3(lightFromWorldMatrix, p);
}
#endif
#endif // HAS_DIRECTIONAL_LIGHTING
#if defined(HAS_DYNAMIC_LIGHTING)
highp vec4 computeLightSpacePositionSpot(const highp vec3 p, const highp vec3 n,
const highp vec3 l, const float b, const highp mat4 lightFromWorldMatrix) {
return mulMat4x4Float3(lightFromWorldMatrix, p);
}
#endif // HAS_DYNAMIC_LIGHTING
#else // HAS_VSM
highp vec4 computeLightSpacePositionDirectional(const highp vec3 p, const highp vec3 n,
const highp vec3 l, const float b, const highp mat4 lightFromWorldMatrix) {
float NoL = saturate(dot(n, l));
highp float sinTheta = sqrt(1.0 - NoL * NoL);
highp vec3 offsetPosition = p + n * (sinTheta * b);
return mulMat4x4Float3(lightFromWorldMatrix, offsetPosition);
}
#if defined(HAS_DYNAMIC_LIGHTING)
highp vec4 computeLightSpacePositionSpot(const highp vec3 p, const highp vec3 n,
const highp vec3 l, const float b, const highp mat4 lightFromWorldMatrix) {
highp vec4 positionLs = mulMat4x4Float3(lightFromWorldMatrix, p);
highp float oneOverZ = positionLs.w / positionLs.z;
return computeLightSpacePositionDirectional(p, n, l, b * oneOverZ, lightFromWorldMatrix);
}
#endif // HAS_DIRECTIONAL_LIGHTING
#endif // HAS_VSM
#endif // HAS_SHADOWING
+3 -3
View File
@@ -102,7 +102,7 @@ highp vec3 getNormalizedViewportCoord2() {
highp vec4 getSpotLightSpacePosition(uint index) {
highp vec3 dir = shadowUniforms.shadows[index].direction;
float bias = shadowUniforms.shadows[index].normalBias;
return computeLightSpacePosition(vertex_worldPosition.xyz,
return computeLightSpacePositionSpot(vertex_worldPosition.xyz,
vertex_worldNormal, dir, bias, shadowUniforms.shadows[index].lightFromWorldMatrix);
}
#endif
@@ -128,13 +128,13 @@ uint getShadowCascade() {
highp vec4 getCascadeLightSpacePosition(uint cascade) {
// For the first cascade, return the interpolated light space position.
// This branch will be coherent (mostly) for neighboring fragments, and it's worth avoiding
// the matrix multiply inside computeLightSpacePosition.
// the matrix multiply inside computeLightSpacePositionDirectional.
if (cascade == 0u) {
// Note: this branch may cause issues with derivatives
return vertex_lightSpacePosition;
}
return computeLightSpacePosition(getWorldPosition(), getWorldNormalVector(),
return computeLightSpacePositionDirectional(getWorldPosition(), getWorldNormalVector(),
frameUniforms.lightDirection, frameUniforms.shadowBias.y,
frameUniforms.lightFromWorldMatrix[cascade]);
}
+2 -1
View File
@@ -89,7 +89,8 @@ void main() {
#endif
#if defined(HAS_SHADOWING) && defined(HAS_DIRECTIONAL_LIGHTING)
vertex_lightSpacePosition = computeLightSpacePosition(vertex_worldPosition.xyz, vertex_worldNormal,
vertex_lightSpacePosition = computeLightSpacePositionDirectional(
vertex_worldPosition.xyz, vertex_worldNormal,
frameUniforms.lightDirection, frameUniforms.shadowBias.y, getLightFromWorldMatrix());
#endif