refactor: formalize directional shadow planning settings

This commit is contained in:
2026-04-10 02:14:45 +08:00
parent f476890116
commit 54eb2415ff
3 changed files with 164 additions and 26 deletions

View File

@@ -12,8 +12,24 @@ class Scene;
namespace Rendering {
struct DirectionalShadowPlanningSettings {
uint32_t mapDimension = 1024u;
float minFocusDistance = 5.0f;
float maxFocusDistance = 64.0f;
float perspectiveFocusFactor = 1.0f;
float orthographicFocusFactor = 2.0f;
float minDepthRange = 20.0f;
float boundsPadding = 1.0f;
float minDepthPadding = 2.0f;
};
class SceneRenderRequestPlanner {
public:
SceneRenderRequestPlanner() = default;
void SetDirectionalShadowPlanningSettings(const DirectionalShadowPlanningSettings& settings);
const DirectionalShadowPlanningSettings& GetDirectionalShadowPlanningSettings() const;
std::vector<Components::CameraComponent*> CollectCameras(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera) const;
@@ -23,6 +39,9 @@ public:
Components::CameraComponent* overrideCamera,
const RenderContext& context,
const RenderSurface& surface) const;
private:
DirectionalShadowPlanningSettings m_directionalShadowPlanningSettings = {};
};
} // namespace Rendering

View File

@@ -20,14 +20,41 @@ namespace Rendering {
namespace {
constexpr uint32_t kDirectionalShadowMapDimension = 1024;
constexpr float kMinShadowFocusDistance = 5.0f;
constexpr float kMaxShadowFocusDistance = 64.0f;
constexpr float kPerspectiveShadowFocusFactor = 1.0f;
constexpr float kOrthographicShadowFocusFactor = 2.0f;
constexpr float kMinShadowDepthRange = 20.0f;
constexpr float kShadowBoundsPadding = 1.0f;
constexpr float kMinShadowDepthPadding = 2.0f;
DirectionalShadowPlanningSettings SanitizeDirectionalShadowPlanningSettings(
const DirectionalShadowPlanningSettings& settings) {
DirectionalShadowPlanningSettings sanitized = settings;
const DirectionalShadowPlanningSettings defaults = {};
if (sanitized.mapDimension == 0u) {
sanitized.mapDimension = defaults.mapDimension;
}
if (sanitized.minFocusDistance <= Math::EPSILON) {
sanitized.minFocusDistance = defaults.minFocusDistance;
}
if (sanitized.maxFocusDistance < sanitized.minFocusDistance) {
sanitized.maxFocusDistance = (settings.maxFocusDistance >= defaults.minFocusDistance)
? settings.maxFocusDistance
: defaults.maxFocusDistance;
sanitized.maxFocusDistance = std::max(sanitized.maxFocusDistance, sanitized.minFocusDistance);
}
if (sanitized.perspectiveFocusFactor <= Math::EPSILON) {
sanitized.perspectiveFocusFactor = defaults.perspectiveFocusFactor;
}
if (sanitized.orthographicFocusFactor <= Math::EPSILON) {
sanitized.orthographicFocusFactor = defaults.orthographicFocusFactor;
}
if (sanitized.minDepthRange <= Math::EPSILON) {
sanitized.minDepthRange = defaults.minDepthRange;
}
if (sanitized.boundsPadding < 0.0f) {
sanitized.boundsPadding = defaults.boundsPadding;
}
if (sanitized.minDepthPadding < 0.0f) {
sanitized.minDepthPadding = defaults.minDepthPadding;
}
return sanitized;
}
bool ShouldPlanDirectionalShadowForCamera(
const Components::CameraComponent& camera,
@@ -102,6 +129,7 @@ DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan(
const Components::Scene& scene,
const Components::CameraComponent& camera,
const Components::LightComponent& light,
const DirectionalShadowPlanningSettings& shadowSettings,
float viewportAspect) {
DirectionalShadowRenderPlan plan = {};
if (!light.GetCastsShadows()) {
@@ -129,13 +157,13 @@ DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan(
const float shadowDistance = camera.GetProjectionType() == Components::CameraProjectionType::Perspective
? std::clamp(
camera.GetFarClipPlane() * kPerspectiveShadowFocusFactor,
kMinShadowFocusDistance,
kMaxShadowFocusDistance)
camera.GetFarClipPlane() * shadowSettings.perspectiveFocusFactor,
shadowSettings.minFocusDistance,
shadowSettings.maxFocusDistance)
: std::clamp(
camera.GetOrthographicSize() * kOrthographicShadowFocusFactor,
kMinShadowFocusDistance,
kMaxShadowFocusDistance);
camera.GetOrthographicSize() * shadowSettings.orthographicFocusFactor,
shadowSettings.minFocusDistance,
shadowSettings.maxFocusDistance);
const float sliceNear = std::max(camera.GetNearClipPlane(), 0.1f);
const float sliceFar = std::max(sliceNear + 0.1f, shadowDistance);
@@ -185,7 +213,7 @@ DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan(
frustumWorldBounds.Encapsulate(frustumCorners[index]);
}
const float shadowViewDistance = std::max(sliceFar, kMinShadowDepthRange * 0.5f);
const float shadowViewDistance = std::max(sliceFar, shadowSettings.minDepthRange * 0.5f);
const Math::Vector3 shadowWorldPosition =
focusPoint + lightDirection * shadowViewDistance;
@@ -256,17 +284,24 @@ DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan(
ExpandLightSpaceBounds(view, worldCorners, minX, maxX, minY, maxY, minZ, maxZ);
}
minX -= kShadowBoundsPadding;
maxX += kShadowBoundsPadding;
minY -= kShadowBoundsPadding;
maxY += kShadowBoundsPadding;
minZ -= kMinShadowDepthPadding;
maxZ += kMinShadowDepthPadding;
minX -= shadowSettings.boundsPadding;
maxX += shadowSettings.boundsPadding;
minY -= shadowSettings.boundsPadding;
maxY += shadowSettings.boundsPadding;
minZ -= shadowSettings.minDepthPadding;
maxZ += shadowSettings.minDepthPadding;
const float shadowHalfExtent = std::max(
std::max(std::abs(minX), std::abs(maxX)),
std::max(std::abs(minY), std::abs(maxY)));
const float shadowDepthRange = std::max(maxZ - minZ, kMinShadowDepthRange);
const float unclampedShadowDepthRange = maxZ - minZ;
if (unclampedShadowDepthRange < shadowSettings.minDepthRange) {
const float centerZ = (minZ + maxZ) * 0.5f;
const float halfDepthRange = shadowSettings.minDepthRange * 0.5f;
minZ = centerZ - halfDepthRange;
maxZ = centerZ + halfDepthRange;
}
const Math::Matrix4x4 projection = Math::Matrix4x4::Orthographic(
minX,
maxX,
@@ -281,8 +316,8 @@ DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan(
plan.orthographicHalfExtent = shadowHalfExtent;
plan.nearClipPlane = minZ;
plan.farClipPlane = maxZ;
plan.mapWidth = kDirectionalShadowMapDimension;
plan.mapHeight = kDirectionalShadowMapDimension;
plan.mapWidth = shadowSettings.mapDimension;
plan.mapHeight = shadowSettings.mapDimension;
plan.cameraData.view = view.Transpose();
plan.cameraData.projection = projection.Transpose();
plan.cameraData.viewProjection = (projection * view).Transpose();
@@ -296,6 +331,15 @@ DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan(
} // namespace
void SceneRenderRequestPlanner::SetDirectionalShadowPlanningSettings(
const DirectionalShadowPlanningSettings& settings) {
m_directionalShadowPlanningSettings = SanitizeDirectionalShadowPlanningSettings(settings);
}
const DirectionalShadowPlanningSettings& SceneRenderRequestPlanner::GetDirectionalShadowPlanningSettings() const {
return m_directionalShadowPlanningSettings;
}
std::vector<Components::CameraComponent*> SceneRenderRequestPlanner::CollectCameras(
const Components::Scene& scene,
Components::CameraComponent* overrideCamera) const {
@@ -355,8 +399,12 @@ std::vector<CameraRenderRequest> SceneRenderRequestPlanner::BuildRequests(
? static_cast<float>(surface.GetRenderAreaWidth()) /
static_cast<float>(surface.GetRenderAreaHeight())
: 1.0f;
request.directionalShadow =
BuildDirectionalShadowRenderPlan(scene, *camera, *mainDirectionalLight, viewportAspect);
request.directionalShadow = BuildDirectionalShadowRenderPlan(
scene,
*camera,
*mainDirectionalLight,
m_directionalShadowPlanningSettings,
viewportAspect);
if (request.directionalShadow.IsValid()) {
request.shadowCaster.clearFlags = RenderClearFlags::Depth;
request.shadowCaster.hasCameraDataOverride = true;