Add forward shadow receiving support

This commit is contained in:
2026-04-04 23:01:34 +08:00
parent 353d129613
commit 96a44da2cb
22 changed files with 889 additions and 89 deletions

View File

@@ -55,7 +55,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderDeclaresExplicitForwardRes
const ShaderPass* pass = shader->FindPass("ForwardLit");
ASSERT_NE(pass, nullptr);
ASSERT_EQ(pass->resources.Size(), 4u);
ASSERT_EQ(pass->resources.Size(), 7u);
EXPECT_EQ(pass->resources[0].semantic, "PerObject");
EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer);
@@ -77,6 +77,21 @@ TEST(BuiltinForwardPipeline_Test, BuiltinForwardShaderDeclaresExplicitForwardRes
EXPECT_EQ(pass->resources[3].set, 4u);
EXPECT_EQ(pass->resources[3].binding, 0u);
EXPECT_EQ(pass->resources[4].semantic, "ShadowReceiver");
EXPECT_EQ(pass->resources[4].type, ShaderResourceType::ConstantBuffer);
EXPECT_EQ(pass->resources[4].set, 5u);
EXPECT_EQ(pass->resources[4].binding, 0u);
EXPECT_EQ(pass->resources[5].semantic, "ShadowMapTexture");
EXPECT_EQ(pass->resources[5].type, ShaderResourceType::Texture2D);
EXPECT_EQ(pass->resources[5].set, 6u);
EXPECT_EQ(pass->resources[5].binding, 0u);
EXPECT_EQ(pass->resources[6].semantic, "ShadowMapSampler");
EXPECT_EQ(pass->resources[6].type, ShaderResourceType::Sampler);
EXPECT_EQ(pass->resources[6].set, 7u);
EXPECT_EQ(pass->resources[6].binding, 0u);
delete shader;
}
@@ -133,10 +148,13 @@ TEST(BuiltinForwardPipeline_Test, BuildsBuiltinPassResourceBindingPlanFromExplic
EXPECT_TRUE(TryBuildBuiltinPassResourceBindingPlan(pass->resources, plan, &error)) << error.CStr();
EXPECT_TRUE(plan.perObject.IsValid());
EXPECT_TRUE(plan.material.IsValid());
EXPECT_TRUE(plan.shadowReceiver.IsValid());
EXPECT_TRUE(plan.baseColorTexture.IsValid());
EXPECT_TRUE(plan.linearClampSampler.IsValid());
EXPECT_TRUE(plan.shadowMapTexture.IsValid());
EXPECT_TRUE(plan.shadowMapSampler.IsValid());
EXPECT_EQ(plan.firstDescriptorSet, 1u);
EXPECT_EQ(plan.descriptorSetCount, 4u);
EXPECT_EQ(plan.descriptorSetCount, 7u);
EXPECT_TRUE(plan.usesConstantBuffers);
EXPECT_TRUE(plan.usesTextures);
EXPECT_TRUE(plan.usesSamplers);

View File

@@ -6,6 +6,7 @@
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIResourceView.h>
#include <XCEngine/RHI/RHITexture.h>
#include <XCEngine/Core/Math/Vector4.h>
#include <XCEngine/Rendering/CameraRenderer.h>
#include <XCEngine/Rendering/ObjectIdPass.h>
#include <XCEngine/Rendering/RenderPipelineAsset.h>
@@ -39,6 +40,10 @@ struct MockPipelineState {
size_t lastVisibleItemCount = 0;
RenderClearFlags lastClearFlags = RenderClearFlags::All;
XCEngine::Math::Color lastClearColor = XCEngine::Math::Color::Black();
bool lastHasMainDirectionalShadow = false;
XCEngine::RHI::RHIResourceView* lastShadowMap = nullptr;
XCEngine::Math::Matrix4x4 lastShadowViewProjection = XCEngine::Math::Matrix4x4::Identity();
XCEngine::Math::Vector4 lastShadowParams = XCEngine::Math::Vector4::Zero();
std::vector<CameraComponent*> renderedCameras;
std::vector<RenderClearFlags> renderedClearFlags;
std::vector<XCEngine::Math::Color> renderedClearColors;
@@ -52,10 +57,14 @@ struct MockShadowAllocationState {
int createDepthViewCalls = 0;
int shutdownDepthViewCalls = 0;
int destroyDepthViewCalls = 0;
int createShaderViewCalls = 0;
int shutdownShaderViewCalls = 0;
int destroyShaderViewCalls = 0;
uint32_t lastTextureWidth = 0;
uint32_t lastTextureHeight = 0;
XCEngine::RHI::Format lastTextureFormat = XCEngine::RHI::Format::Unknown;
XCEngine::RHI::Format lastDepthViewFormat = XCEngine::RHI::Format::Unknown;
XCEngine::RHI::Format lastShaderViewFormat = XCEngine::RHI::Format::Unknown;
};
class MockShadowTexture final : public XCEngine::RHI::RHITexture {
@@ -114,11 +123,19 @@ public:
}
~MockShadowView() override {
++m_state->destroyDepthViewCalls;
if (m_viewType == XCEngine::RHI::ResourceViewType::ShaderResource) {
++m_state->destroyShaderViewCalls;
} else {
++m_state->destroyDepthViewCalls;
}
}
void Shutdown() override {
++m_state->shutdownDepthViewCalls;
if (m_viewType == XCEngine::RHI::ResourceViewType::ShaderResource) {
++m_state->shutdownShaderViewCalls;
} else {
++m_state->shutdownDepthViewCalls;
}
}
void* GetNativeHandle() override { return nullptr; }
@@ -217,7 +234,15 @@ public:
XCEngine::RHI::RHIResourceView* CreateShaderResourceView(
XCEngine::RHI::RHITexture*,
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
const XCEngine::RHI::ResourceViewDesc& desc) override {
++m_state->createShaderViewCalls;
m_state->lastShaderViewFormat = static_cast<XCEngine::RHI::Format>(desc.format);
return new MockShadowView(
m_state,
XCEngine::RHI::ResourceViewType::ShaderResource,
static_cast<XCEngine::RHI::Format>(desc.format),
desc.dimension);
}
XCEngine::RHI::RHIResourceView* CreateUnorderedAccessView(
XCEngine::RHI::RHITexture*,
const XCEngine::RHI::ResourceViewDesc&) override { return nullptr; }
@@ -271,6 +296,10 @@ public:
m_state->lastVisibleItemCount = sceneData.visibleItems.size();
m_state->lastClearFlags = sceneData.cameraData.clearFlags;
m_state->lastClearColor = sceneData.cameraData.clearColor;
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->renderedCameras.push_back(sceneData.camera);
m_state->renderedClearFlags.push_back(sceneData.cameraData.clearFlags);
m_state->renderedClearColors.push_back(sceneData.cameraData.clearColor);
@@ -677,6 +706,8 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) {
request.directionalShadow.cameraData.viewportHeight = 128;
request.directionalShadow.cameraData.clearFlags = RenderClearFlags::Depth;
request.directionalShadow.cameraData.worldPosition = XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f);
request.directionalShadow.cameraData.viewProjection =
XCEngine::Math::Matrix4x4::Translation(XCEngine::Math::Vector3(11.0f, 12.0f, 13.0f));
ASSERT_TRUE(renderer.Render(request));
EXPECT_EQ(
@@ -693,14 +724,27 @@ TEST(CameraRenderer_Test, AutoAllocatesDirectionalShadowSurfaceFromShadowPlan) {
EXPECT_EQ(shadowPassRaw->lastWorldPosition, XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f));
EXPECT_EQ(allocationState->createTextureCalls, 1);
EXPECT_EQ(allocationState->createDepthViewCalls, 1);
EXPECT_EQ(allocationState->createShaderViewCalls, 1);
EXPECT_EQ(allocationState->lastTextureWidth, 256u);
EXPECT_EQ(allocationState->lastTextureHeight, 128u);
EXPECT_EQ(allocationState->lastTextureFormat, XCEngine::RHI::Format::D24_UNorm_S8_UInt);
EXPECT_EQ(allocationState->lastDepthViewFormat, XCEngine::RHI::Format::D24_UNorm_S8_UInt);
EXPECT_EQ(allocationState->lastShaderViewFormat, XCEngine::RHI::Format::D24_UNorm_S8_UInt);
EXPECT_EQ(allocationState->shutdownDepthViewCalls, 1);
EXPECT_EQ(allocationState->shutdownShaderViewCalls, 1);
EXPECT_EQ(allocationState->shutdownTextureCalls, 1);
EXPECT_EQ(allocationState->destroyDepthViewCalls, 1);
EXPECT_EQ(allocationState->destroyShaderViewCalls, 1);
EXPECT_EQ(allocationState->destroyTextureCalls, 1);
EXPECT_TRUE(pipelineState->lastHasMainDirectionalShadow);
EXPECT_NE(pipelineState->lastShadowMap, nullptr);
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);
}
TEST(CameraRenderer_Test, StopsRenderingWhenShadowCasterRequestIsInvalid) {