rendering: formalize main light shadow bias settings
This commit is contained in:
@@ -65,7 +65,6 @@ Shader "Builtin Shadow Caster"
|
||||
Cull Back
|
||||
ZWrite On
|
||||
ZTest LEqual
|
||||
Offset 1.0, 2
|
||||
HLSLPROGRAM
|
||||
#pragma target 4.5
|
||||
#pragma vertex MainVS
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <XCEngine/Core/Math/Vector4.h>
|
||||
#include <XCEngine/Rendering/FrameData/RenderCameraData.h>
|
||||
#include <XCEngine/Rendering/FrameData/RenderEnvironmentData.h>
|
||||
#include <XCEngine/Rendering/Shadow/DirectionalShadowSettings.h>
|
||||
#include <XCEngine/Rendering/FrameData/VisibleGaussianSplatItem.h>
|
||||
#include <XCEngine/Rendering/FrameData/VisibleRenderItem.h>
|
||||
#include <XCEngine/Rendering/FrameData/VisibleVolumeItem.h>
|
||||
@@ -63,20 +64,23 @@ static_assert(
|
||||
|
||||
struct RenderDirectionalShadowSamplingData {
|
||||
float enabled = 0.0f;
|
||||
float receiverDepthBias = 0.0f;
|
||||
float normalBiasScale = 0.0f;
|
||||
float shadowStrength = 0.0f;
|
||||
DirectionalShadowSamplingSettings settings = {};
|
||||
};
|
||||
|
||||
static_assert(
|
||||
sizeof(RenderDirectionalShadowSamplingData) == sizeof(float) * 4u,
|
||||
"RenderDirectionalShadowSamplingData must stay float4-sized for GPU constant layout");
|
||||
|
||||
struct RenderDirectionalShadowCasterBiasData {
|
||||
DirectionalShadowCasterBiasSettings settings = {};
|
||||
};
|
||||
|
||||
struct RenderDirectionalShadowData {
|
||||
bool enabled = false;
|
||||
Math::Matrix4x4 viewProjection = Math::Matrix4x4::Identity();
|
||||
RenderDirectionalShadowMapMetrics mapMetrics = {};
|
||||
RenderDirectionalShadowSamplingData sampling = {};
|
||||
RenderDirectionalShadowCasterBiasData casterBias = {};
|
||||
RHI::RHIResourceView* shadowMap = nullptr;
|
||||
|
||||
bool IsValid() const {
|
||||
|
||||
@@ -171,6 +171,10 @@ private:
|
||||
ResolvedShaderPass ResolveSurfaceShaderPass(
|
||||
const RenderSceneData& sceneData,
|
||||
const Resources::Material* material) const;
|
||||
Resources::MaterialRenderState ResolveEffectiveDepthRenderState(
|
||||
const Resources::ShaderPass* shaderPass,
|
||||
const Resources::Material* material,
|
||||
const RenderSceneData& sceneData) const;
|
||||
bool TryBuildSupportedBindingPlan(
|
||||
const Resources::ShaderPass& shaderPass,
|
||||
BuiltinPassResourceBindingPlan& outPlan,
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <XCEngine/Rendering/RenderContext.h>
|
||||
#include <XCEngine/Rendering/RenderPass.h>
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
#include <XCEngine/Rendering/Shadow/DirectionalShadowSettings.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
@@ -131,6 +132,8 @@ struct DirectionalShadowRenderPlan {
|
||||
float texelWorldSize = 0.0f;
|
||||
float nearClipPlane = 0.1f;
|
||||
float farClipPlane = 0.0f;
|
||||
DirectionalShadowSamplingSettings sampling = {};
|
||||
DirectionalShadowCasterBiasSettings casterBias = {};
|
||||
uint32_t mapWidth = 0;
|
||||
uint32_t mapHeight = 0;
|
||||
RenderCameraData cameraData = {};
|
||||
|
||||
@@ -12,9 +12,9 @@ class Scene;
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
// Planning-only knobs for fitting the single main-light shadow camera to the
|
||||
// current view. Receiver-side bias/filtering parameters live in runtime shadow
|
||||
// data and are not configured here.
|
||||
// Planner-owned knobs for the single main-light shadow, including both camera
|
||||
// fitting and the default sampling / caster bias values emitted into the
|
||||
// runtime shadow contract.
|
||||
struct DirectionalShadowPlanningSettings {
|
||||
uint32_t mapDimension = 1024u;
|
||||
float minFocusDistance = 5.0f;
|
||||
@@ -24,6 +24,8 @@ struct DirectionalShadowPlanningSettings {
|
||||
float minDepthRange = 20.0f;
|
||||
float boundsPadding = 1.0f;
|
||||
float minDepthPadding = 2.0f;
|
||||
DirectionalShadowSamplingSettings sampling = {};
|
||||
DirectionalShadowCasterBiasSettings casterBias = {};
|
||||
};
|
||||
|
||||
class SceneRenderRequestPlanner {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
struct DirectionalShadowSamplingSettings {
|
||||
float receiverDepthBias = 0.0015f;
|
||||
float normalBiasScale = 1.5f;
|
||||
float shadowStrength = 0.85f;
|
||||
};
|
||||
|
||||
struct DirectionalShadowCasterBiasSettings {
|
||||
float depthBiasFactor = 1.0f;
|
||||
int32_t depthBiasUnits = 2;
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -19,10 +19,6 @@ namespace Rendering {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr float kDirectionalShadowReceiverDepthBias = 0.0015f;
|
||||
constexpr float kDirectionalShadowNormalBiasScale = 1.5f;
|
||||
constexpr float kDirectionalShadowStrength = 0.85f;
|
||||
|
||||
std::shared_ptr<const RenderPipelineAsset> CreateDefaultPipelineAsset() {
|
||||
static const std::shared_ptr<const RenderPipelineAsset> s_defaultPipelineAsset =
|
||||
std::make_shared<Pipelines::BuiltinForwardPipelineAsset>();
|
||||
@@ -395,9 +391,8 @@ RenderDirectionalShadowData BuildDirectionalShadowData(
|
||||
1.0f / static_cast<float>(plan.mapHeight));
|
||||
shadowData.mapMetrics.worldTexelSize = texelWorldSize;
|
||||
shadowData.sampling.enabled = 1.0f;
|
||||
shadowData.sampling.receiverDepthBias = kDirectionalShadowReceiverDepthBias;
|
||||
shadowData.sampling.normalBiasScale = kDirectionalShadowNormalBiasScale;
|
||||
shadowData.sampling.shadowStrength = kDirectionalShadowStrength;
|
||||
shadowData.sampling.settings = plan.sampling;
|
||||
shadowData.casterBias.settings = plan.casterBias;
|
||||
return shadowData;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
||||
const Resources::ShaderPass& shaderPass,
|
||||
const Containers::String& passName,
|
||||
const Resources::ShaderKeywordSet& keywordSet,
|
||||
const Resources::Material* material,
|
||||
const Resources::MaterialRenderState& renderState,
|
||||
const RenderSurface& surface,
|
||||
const RHI::InputLayoutDesc& inputLayout) {
|
||||
RHI::GraphicsPipelineDesc pipelineDesc = {};
|
||||
@@ -116,7 +116,7 @@ RHI::GraphicsPipelineDesc CreatePipelineDesc(
|
||||
pipelineDesc.topologyType = static_cast<uint32_t>(RHI::PrimitiveTopologyType::Triangle);
|
||||
::XCEngine::Rendering::Internal::ApplySurfacePropertiesToGraphicsPipelineDesc(surface, pipelineDesc);
|
||||
pipelineDesc.inputLayout = inputLayout;
|
||||
ApplyResolvedRenderState(&shaderPass, material, pipelineDesc);
|
||||
ApplyRenderState(renderState, pipelineDesc);
|
||||
|
||||
if (!shaderPass.hasFixedFunctionState) {
|
||||
pipelineDesc.blendState.blendEnable = false;
|
||||
@@ -345,6 +345,20 @@ BuiltinDepthStylePassBase::ResolvedShaderPass BuiltinDepthStylePassBase::Resolve
|
||||
return {};
|
||||
}
|
||||
|
||||
Resources::MaterialRenderState BuiltinDepthStylePassBase::ResolveEffectiveDepthRenderState(
|
||||
const Resources::ShaderPass* shaderPass,
|
||||
const Resources::Material* material,
|
||||
const RenderSceneData& sceneData) const {
|
||||
Resources::MaterialRenderState renderState = ResolveEffectiveRenderState(shaderPass, material);
|
||||
if (m_passType == BuiltinMaterialPass::ShadowCaster) {
|
||||
renderState.depthBiasFactor =
|
||||
sceneData.lighting.mainDirectionalShadow.casterBias.settings.depthBiasFactor;
|
||||
renderState.depthBiasUnits =
|
||||
sceneData.lighting.mainDirectionalShadow.casterBias.settings.depthBiasUnits;
|
||||
}
|
||||
return renderState;
|
||||
}
|
||||
|
||||
bool BuiltinDepthStylePassBase::TryBuildSupportedBindingPlan(
|
||||
const Resources::ShaderPass& shaderPass,
|
||||
BuiltinPassResourceBindingPlan& outPlan,
|
||||
@@ -466,8 +480,9 @@ RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState(
|
||||
}
|
||||
|
||||
PipelineStateKey pipelineKey = {};
|
||||
pipelineKey.renderState =
|
||||
BuildStaticPipelineRenderStateKey(ResolveEffectiveRenderState(resolvedShaderPass.pass, material));
|
||||
const Resources::MaterialRenderState effectiveRenderState =
|
||||
ResolveEffectiveDepthRenderState(resolvedShaderPass.pass, material, sceneData);
|
||||
pipelineKey.renderState = BuildStaticPipelineRenderStateKey(effectiveRenderState);
|
||||
pipelineKey.shader = resolvedShaderPass.shader;
|
||||
pipelineKey.passName = resolvedShaderPass.passName;
|
||||
pipelineKey.keywordSignature = ::XCEngine::Rendering::Internal::BuildShaderKeywordSignature(keywordSet);
|
||||
@@ -493,7 +508,7 @@ RHI::RHIPipelineState* BuiltinDepthStylePassBase::GetOrCreatePipelineState(
|
||||
*resolvedShaderPass.pass,
|
||||
resolvedShaderPass.passName,
|
||||
keywordSet,
|
||||
material,
|
||||
effectiveRenderState,
|
||||
surface,
|
||||
BuildCommonInputLayout());
|
||||
RHI::RHIPipelineState* pipelineState = context.device->CreatePipelineState(pipelineDesc);
|
||||
@@ -743,7 +758,7 @@ bool BuiltinDepthStylePassBase::DrawVisibleItem(
|
||||
return false;
|
||||
}
|
||||
const Resources::MaterialRenderState effectiveRenderState =
|
||||
ResolveEffectiveRenderState(resolvedShaderPass.pass, material);
|
||||
ResolveEffectiveDepthRenderState(resolvedShaderPass.pass, material, sceneData);
|
||||
|
||||
PassLayoutKey passLayoutKey = {};
|
||||
passLayoutKey.shader = resolvedShaderPass.shader;
|
||||
|
||||
@@ -111,6 +111,27 @@ DirectionalShadowPlanningSettings SanitizeDirectionalShadowPlanningSettings(
|
||||
if (sanitized.minDepthPadding < 0.0f) {
|
||||
sanitized.minDepthPadding = defaults.minDepthPadding;
|
||||
}
|
||||
if (!std::isfinite(sanitized.sampling.receiverDepthBias) ||
|
||||
sanitized.sampling.receiverDepthBias < 0.0f) {
|
||||
sanitized.sampling.receiverDepthBias = defaults.sampling.receiverDepthBias;
|
||||
}
|
||||
if (!std::isfinite(sanitized.sampling.normalBiasScale) ||
|
||||
sanitized.sampling.normalBiasScale < 0.0f) {
|
||||
sanitized.sampling.normalBiasScale = defaults.sampling.normalBiasScale;
|
||||
}
|
||||
if (!std::isfinite(sanitized.sampling.shadowStrength)) {
|
||||
sanitized.sampling.shadowStrength = defaults.sampling.shadowStrength;
|
||||
} else {
|
||||
sanitized.sampling.shadowStrength =
|
||||
std::clamp(sanitized.sampling.shadowStrength, 0.0f, 1.0f);
|
||||
}
|
||||
if (!std::isfinite(sanitized.casterBias.depthBiasFactor) ||
|
||||
sanitized.casterBias.depthBiasFactor < 0.0f) {
|
||||
sanitized.casterBias.depthBiasFactor = defaults.casterBias.depthBiasFactor;
|
||||
}
|
||||
if (sanitized.casterBias.depthBiasUnits < 0) {
|
||||
sanitized.casterBias.depthBiasUnits = defaults.casterBias.depthBiasUnits;
|
||||
}
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
@@ -340,6 +361,8 @@ DirectionalShadowRenderPlan BuildDirectionalShadowRenderPlan(
|
||||
plan.texelWorldSize = std::max(shadowTexelSizeX, shadowTexelSizeY);
|
||||
plan.nearClipPlane = minZ;
|
||||
plan.farClipPlane = maxZ;
|
||||
plan.sampling = shadowSettings.sampling;
|
||||
plan.casterBias = shadowSettings.casterBias;
|
||||
plan.mapWidth = shadowSettings.mapDimension;
|
||||
plan.mapHeight = shadowSettings.mapDimension;
|
||||
plan.cameraData.view = view.Transpose();
|
||||
|
||||
@@ -44,7 +44,8 @@ struct MockPipelineState {
|
||||
bool lastHasMainDirectionalShadow = false;
|
||||
XCEngine::RHI::RHIResourceView* lastShadowMap = nullptr;
|
||||
XCEngine::Math::Matrix4x4 lastShadowViewProjection = XCEngine::Math::Matrix4x4::Identity();
|
||||
XCEngine::Math::Vector4 lastShadowParams = XCEngine::Math::Vector4::Zero();
|
||||
RenderDirectionalShadowMapMetrics lastShadowMapMetrics = {};
|
||||
RenderDirectionalShadowSamplingData lastShadowSampling = {};
|
||||
bool lastHasMainDirectionalShadowKeyword = false;
|
||||
RenderEnvironmentMode lastEnvironmentMode = RenderEnvironmentMode::None;
|
||||
bool lastHasSkybox = false;
|
||||
@@ -327,7 +328,8 @@ public:
|
||||
m_state->lastHasMainDirectionalShadow = sceneData.lighting.HasMainDirectionalShadow();
|
||||
m_state->lastShadowMap = sceneData.lighting.mainDirectionalShadow.shadowMap;
|
||||
m_state->lastShadowViewProjection = sceneData.lighting.mainDirectionalShadow.viewProjection;
|
||||
m_state->lastShadowParams = sceneData.lighting.mainDirectionalShadow.shadowParams;
|
||||
m_state->lastShadowMapMetrics = sceneData.lighting.mainDirectionalShadow.mapMetrics;
|
||||
m_state->lastShadowSampling = sceneData.lighting.mainDirectionalShadow.sampling;
|
||||
m_state->lastHasMainDirectionalShadowKeyword =
|
||||
XCEngine::Resources::ShaderKeywordSetContains(
|
||||
sceneData.globalShaderKeywords,
|
||||
@@ -1338,10 +1340,10 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) {
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowViewProjection.m[0][3], 11.0f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowViewProjection.m[1][3], 12.0f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowViewProjection.m[2][3], 13.0f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.x, 0.0015f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.y, 1.0f / 256.0f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.z, 1.0f / 128.0f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowParams.w, 0.85f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowSampling.settings.receiverDepthBias, 0.0015f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowMapMetrics.inverseMapSize.x, 1.0f / 256.0f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowMapMetrics.inverseMapSize.y, 1.0f / 128.0f);
|
||||
EXPECT_FLOAT_EQ(pipelineState->lastShadowSampling.settings.shadowStrength, 0.85f);
|
||||
EXPECT_TRUE(pipelineState->lastHasMainDirectionalShadowKeyword);
|
||||
}
|
||||
|
||||
|
||||
@@ -224,6 +224,11 @@ TEST(SceneRenderRequestPlanner_Test, AppliesConfiguredDirectionalShadowPlanningS
|
||||
settings.maxFocusDistance = 12.0f;
|
||||
settings.perspectiveFocusFactor = 0.01f;
|
||||
settings.minDepthRange = 80.0f;
|
||||
settings.sampling.receiverDepthBias = 0.0025f;
|
||||
settings.sampling.normalBiasScale = 2.0f;
|
||||
settings.sampling.shadowStrength = 0.75f;
|
||||
settings.casterBias.depthBiasFactor = 2.5f;
|
||||
settings.casterBias.depthBiasUnits = 4;
|
||||
planner.SetDirectionalShadowPlanningSettings(settings);
|
||||
|
||||
const std::vector<CameraRenderRequest> requests = planner.BuildRequests(
|
||||
@@ -241,6 +246,11 @@ TEST(SceneRenderRequestPlanner_Test, AppliesConfiguredDirectionalShadowPlanningS
|
||||
EXPECT_EQ(request.shadowCaster.cameraDataOverride.viewportHeight, 2048u);
|
||||
EXPECT_GE(request.directionalShadow.focusPoint.z, 6.0f);
|
||||
EXPECT_LE(request.directionalShadow.focusPoint.z, 6.3f);
|
||||
EXPECT_FLOAT_EQ(request.directionalShadow.sampling.receiverDepthBias, 0.0025f);
|
||||
EXPECT_FLOAT_EQ(request.directionalShadow.sampling.normalBiasScale, 2.0f);
|
||||
EXPECT_FLOAT_EQ(request.directionalShadow.sampling.shadowStrength, 0.75f);
|
||||
EXPECT_FLOAT_EQ(request.directionalShadow.casterBias.depthBiasFactor, 2.5f);
|
||||
EXPECT_EQ(request.directionalShadow.casterBias.depthBiasUnits, 4);
|
||||
EXPECT_GE(
|
||||
request.directionalShadow.farClipPlane - request.directionalShadow.nearClipPlane,
|
||||
80.0f);
|
||||
@@ -258,6 +268,11 @@ TEST(SceneRenderRequestPlanner_Test, SanitizesInvalidDirectionalShadowPlanningSe
|
||||
invalidSettings.minDepthRange = 0.0f;
|
||||
invalidSettings.boundsPadding = -1.0f;
|
||||
invalidSettings.minDepthPadding = -3.0f;
|
||||
invalidSettings.sampling.receiverDepthBias = -0.5f;
|
||||
invalidSettings.sampling.normalBiasScale = -2.0f;
|
||||
invalidSettings.sampling.shadowStrength = 3.0f;
|
||||
invalidSettings.casterBias.depthBiasFactor = -1.0f;
|
||||
invalidSettings.casterBias.depthBiasUnits = -6;
|
||||
planner.SetDirectionalShadowPlanningSettings(invalidSettings);
|
||||
|
||||
const DirectionalShadowPlanningSettings& settings =
|
||||
@@ -270,4 +285,9 @@ TEST(SceneRenderRequestPlanner_Test, SanitizesInvalidDirectionalShadowPlanningSe
|
||||
EXPECT_FLOAT_EQ(settings.minDepthRange, 20.0f);
|
||||
EXPECT_FLOAT_EQ(settings.boundsPadding, 1.0f);
|
||||
EXPECT_FLOAT_EQ(settings.minDepthPadding, 2.0f);
|
||||
EXPECT_FLOAT_EQ(settings.sampling.receiverDepthBias, 0.0015f);
|
||||
EXPECT_FLOAT_EQ(settings.sampling.normalBiasScale, 1.5f);
|
||||
EXPECT_FLOAT_EQ(settings.sampling.shadowStrength, 1.0f);
|
||||
EXPECT_FLOAT_EQ(settings.casterBias.depthBiasFactor, 1.0f);
|
||||
EXPECT_EQ(settings.casterBias.depthBiasUnits, 2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user