Skip to content

Commit

Permalink
repair ShadowOptions::shadowFar
Browse files Browse the repository at this point in the history
the shadow far plane (shadowFar) was only partially taken into account
  • Loading branch information
pixelflinger committed Sep 13, 2023
1 parent 58017a0 commit c35a608
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
1 change: 1 addition & 0 deletions filament/include/filament/LightManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
23 changes: 5 additions & 18 deletions filament/src/ShadowMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<float>::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<FScene::DIRECTION>(index);

/*
Expand All @@ -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);

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions filament/src/ShadowMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
21 changes: 20 additions & 1 deletion filament/src/ShadowMapManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,15 +435,34 @@ FrameGraphId<FrameGraphTexture> 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();

FLightManager::Instance const directionalLight = lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
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<float>::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),
Expand Down
2 changes: 1 addition & 1 deletion filament/src/ShadowMapManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit c35a608

Please sign in to comment.