From d421996ba2f8f02b273b5e8b096085f8d1458d09 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 11 Sep 2023 22:39:10 -0700 Subject: [PATCH] repair ShadowOptions::shadowFar the shadow far plane (shadowFar) was only partially taken into account --- .../google/android/filament/LightManager.java | 1 + filament/include/filament/LightManager.h | 1 + filament/src/ShadowMap.cpp | 23 ++++--------------- filament/src/ShadowMap.h | 8 +++---- filament/src/ShadowMapManager.cpp | 21 ++++++++++++++++- filament/src/ShadowMapManager.h | 2 +- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java index 9e141a5ce38..bc930d7c6cf 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java @@ -258,6 +258,7 @@ public static class ShadowOptions { * shadows that are too far and wouldn't contribute to the scene much, improving * performance and quality. This value is always positive. * Use 0.0f to use the camera far distance. + * This only affect directional lights. */ public float shadowFar = 0.0f; diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h index c29aa7d224c..b7cb62e16a1 100644 --- a/filament/include/filament/LightManager.h +++ b/filament/include/filament/LightManager.h @@ -245,6 +245,7 @@ class UTILS_PUBLIC LightManager : public FilamentAPI { * shadows that are too far and wouldn't contribute to the scene much, improving * performance and quality. This value is always positive. * Use 0.0f to use the camera far distance. + * This only affect directional lights. */ float shadowFar = 0.0f; diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index db1f43cbb9f..f3c250e2d9c 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -131,19 +131,6 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, else params.options.shadowFarHint = dzf * dz + camera.zf; #endif - // Adjust the camera's projection for the light's shadowFar - const mat4f cullingProjection{ [&](auto p) { - if (params.options.shadowFar > 0.0f) { - float const n = camera.zn; - float const f = params.options.shadowFar; - // orthographic projection - assert_invariant(std::abs(p[2].w) <= std::numeric_limits::epsilon()); - p[2].z = 2.0f / (n - f); - p[3].z = (f + n) / (n - f); - } - return p; - }(camera.cullingProjection) }; - const auto direction = params.options.transform * lightData.elementAt(index); /* @@ -164,7 +151,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, // view frustum vertices in world-space float3 wsViewFrustumVertices[8]; - const mat4f worldToClipMatrix = cullingProjection * camera.view; + const mat4f worldToClipMatrix = camera.cullingProjection * camera.view; const Frustum wsFrustum(worldToClipMatrix); computeFrustumCorners(wsViewFrustumVertices, inverse(worldToClipMatrix), sceneInfo.csNearFar); @@ -243,7 +230,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, // in stable mode we simply take the view volume bounding sphere, but we calculate it // in view space, so that it's perfectly stable. float3 vertices[8]; - computeFrustumCorners(vertices, inverse(cullingProjection), sceneInfo.csNearFar); + computeFrustumCorners(vertices, inverse(camera.cullingProjection), sceneInfo.csNearFar); viewVolumeBoundingSphere = computeBoundingSphere(vertices, 8); if (shadowReceiverVolumeBoundingSphere.w < viewVolumeBoundingSphere.w) { @@ -1081,7 +1068,7 @@ bool ShadowMap::intersectSegmentWithPlanarQuad(float3& UTILS_RESTRICT p, } float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture, - uint16_t shadowDimension) const noexcept { + uint16_t shadowDimension) noexcept { // The Jacobian of the transformation from texture-to-world is the matrix itself for // orthographic projections. We just need to inverse worldToShadowTexture, // which is guaranteed to be orthographic. @@ -1096,7 +1083,7 @@ float ShadowMap::texelSizeWorldSpace(const mat3f& worldToShadowTexture, } float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF, - uint16_t shadowDimension) const noexcept { + uint16_t shadowDimension) noexcept { // Here we compute the Jacobian of inverse(MbMtF * Wp). // The expression below has been computed with Mathematica. However, it's not very hard, // albeit error-prone, to do it by hand because MbMtF is a linear transform. @@ -1117,7 +1104,7 @@ float ShadowMap::texelSizeWorldSpace(const mat4f& Wp, const mat4f& MbMtF, constexpr bool JACOBIAN_ESTIMATE = false; if constexpr (JACOBIAN_ESTIMATE) { - // this estimates the Jacobian -- this is a lot heavier. This is mostly for reference + // This estimates the Jacobian -- this is a lot heavier. This is mostly for reference // and testing. const mat4f Si(inverse(MbMtF * Wp)); const float3 p0 = mat4f::project(Si, p); diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index 7e52dc14cfa..f77ff6bfe83 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -283,11 +283,11 @@ class ShadowMap { math::float4 getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo) const noexcept; - float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture, - uint16_t shadowDimension) const noexcept; + static float texelSizeWorldSpace(const math::mat3f& worldToShadowTexture, + uint16_t shadowDimension) noexcept; - float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF, - uint16_t shadowDimension) const noexcept; + static float texelSizeWorldSpace(const math::mat4f& W, const math::mat4f& MbMtF, + uint16_t shadowDimension) noexcept; static constexpr const Segment sBoxSegments[12] = { { 0, 1 }, { 1, 3 }, { 3, 2 }, { 2, 0 }, diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index fbb4e744e89..34c47519e6b 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -435,8 +435,9 @@ FrameGraphId ShadowMapManager::render(FEngine& engine, FrameG } ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEngine& engine, - FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData, + FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept { + FScene* scene = view.getScene(); auto& lcm = engine.getLightManager(); @@ -444,6 +445,24 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng FLightManager::ShadowOptions const& options = lcm.getShadowOptions(directionalLight); FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight); + // Adjust the camera's projection for the light's shadowFar + + cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf; + if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) { + cameraInfo.zf = params.options.shadowFar; + float const n = cameraInfo.zn; + float const f = cameraInfo.zf; + if (std::abs(cameraInfo.cullingProjection[2].w) > std::numeric_limits::epsilon()) { + // perspective projection + cameraInfo.cullingProjection[2].z = (f + n) / (n - f); + cameraInfo.cullingProjection[3].z = (2 * f * n) / (n - f); + } else { + // orthographic projection + cameraInfo.cullingProjection[2].z = 2.0f / (n - f); + cameraInfo.cullingProjection[3].z = (f + n) / (n - f); + } + } + const ShadowMap::ShadowMapInfo shadowMapInfo{ .atlasDimension = mTextureAtlasRequirements.size, .textureDimension = uint16_t(options.mapSize), diff --git a/filament/src/ShadowMapManager.h b/filament/src/ShadowMapManager.h index 488e108e405..712e230266e 100644 --- a/filament/src/ShadowMapManager.h +++ b/filament/src/ShadowMapManager.h @@ -109,7 +109,7 @@ class ShadowMapManager { private: ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine, - FView& view, CameraInfo const& cameraInfo, FScene::RenderableSoa& renderableData, + FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept; ShadowMapManager::ShadowTechnique updateSpotShadowMaps(FEngine& engine,