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

@@ -283,6 +283,13 @@ bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& p
commandList->SetPrimitiveTopology(RHI::PrimitiveTopology::TriangleList);
if (sceneData.lighting.HasMainDirectionalShadow()) {
commandList->TransitionBarrier(
sceneData.lighting.mainDirectionalShadow.shadowMap,
RHI::ResourceStates::DepthWrite,
RHI::ResourceStates::PixelShaderResource);
}
RHI::RHIPipelineState* currentPipelineState = nullptr;
for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) {
const Resources::Material* material = ResolveMaterial(visibleItem);
@@ -303,6 +310,13 @@ bool BuiltinForwardPipeline::ExecuteForwardOpaquePass(const RenderPassContext& p
DrawVisibleItem(context, sceneData, visibleItem);
}
if (sceneData.lighting.HasMainDirectionalShadow()) {
commandList->TransitionBarrier(
sceneData.lighting.mainDirectionalShadow.shadowMap,
RHI::ResourceStates::PixelShaderResource,
RHI::ResourceStates::DepthWrite);
}
if (surface.IsAutoTransitionEnabled()) {
commandList->EndRenderPass();
for (RHI::RHIResourceView* renderTarget : renderTargets) {
@@ -370,6 +384,13 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
return false;
}
RHI::SamplerDesc shadowSamplerDesc = samplerDesc;
shadowSamplerDesc.filter = static_cast<uint32_t>(RHI::FilterMode::Point);
m_shadowSampler = context.device->CreateSampler(shadowSamplerDesc);
if (m_shadowSampler == nullptr) {
return false;
}
const unsigned char whitePixel[4] = { 255, 255, 255, 255 };
RHI::TextureDesc textureDesc = {};
textureDesc.width = 1;
@@ -438,6 +459,12 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
m_sampler = nullptr;
}
if (m_shadowSampler != nullptr) {
m_shadowSampler->Shutdown();
delete m_shadowSampler;
m_shadowSampler = nullptr;
}
m_device = nullptr;
m_initialized = false;
m_builtinForwardShader.Reset();
@@ -527,8 +554,11 @@ BuiltinForwardPipeline::PassResourceLayout* BuiltinForwardPipeline::GetOrCreateP
passLayout.perObject = bindingPlan.perObject;
passLayout.material = bindingPlan.material;
passLayout.shadowReceiver = bindingPlan.shadowReceiver;
passLayout.baseColorTexture = bindingPlan.baseColorTexture;
passLayout.linearClampSampler = bindingPlan.linearClampSampler;
passLayout.shadowMapTexture = bindingPlan.shadowMapTexture;
passLayout.shadowMapSampler = bindingPlan.shadowMapSampler;
if (!passLayout.perObject.IsValid()) {
return failLayout("BuiltinForwardPipeline requires a PerObject resource binding");
@@ -672,14 +702,33 @@ RHI::RHIDescriptorSet* BuiltinForwardPipeline::GetOrCreateStaticDescriptorSet(
}
if (passLayout.setLayouts[setIndex].usesSampler) {
if (m_sampler == nullptr ||
!passLayout.linearClampSampler.IsValid() ||
passLayout.linearClampSampler.set != setIndex) {
RHI::RHISampler* sampler = nullptr;
Core::uint32 binding = 0;
if (passLayout.setLayouts[setIndex].usesLinearClampSampler) {
sampler = m_sampler;
if (!passLayout.linearClampSampler.IsValid() ||
passLayout.linearClampSampler.set != setIndex) {
DestroyOwnedDescriptorSet(descriptorSet);
return nullptr;
}
binding = passLayout.linearClampSampler.binding;
} else if (passLayout.setLayouts[setIndex].usesShadowMapSampler) {
sampler = m_shadowSampler;
if (!passLayout.shadowMapSampler.IsValid() ||
passLayout.shadowMapSampler.set != setIndex) {
DestroyOwnedDescriptorSet(descriptorSet);
return nullptr;
}
binding = passLayout.shadowMapSampler.binding;
}
if (sampler == nullptr) {
DestroyOwnedDescriptorSet(descriptorSet);
return nullptr;
}
descriptorSet.set->UpdateSampler(passLayout.linearClampSampler.binding, m_sampler);
descriptorSet.set->UpdateSampler(binding, sampler);
}
}
@@ -694,7 +743,9 @@ BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreate
Core::uint64 objectId,
const Resources::Material* material,
const MaterialConstantPayloadView& materialConstants,
RHI::RHIResourceView* textureView) {
const ShadowReceiverConstants& shadowReceiverConstants,
RHI::RHIResourceView* baseColorTextureView,
RHI::RHIResourceView* shadowMapTextureView) {
DynamicDescriptorSetKey key = {};
key.passLayout = passLayoutKey;
key.setIndex = setIndex;
@@ -709,35 +760,61 @@ BuiltinForwardPipeline::CachedDescriptorSet* BuiltinForwardPipeline::GetOrCreate
}
const Core::uint64 materialVersion = material != nullptr ? material->GetChangeVersion() : 0;
if ((setLayout.usesMaterial || setLayout.usesTexture) &&
(cachedDescriptorSet.materialVersion != materialVersion ||
cachedDescriptorSet.textureView != textureView)) {
if (setLayout.usesMaterial) {
if (!passLayout.material.IsValid() || passLayout.material.set != setIndex) {
return nullptr;
}
if (!materialConstants.IsValid()) {
return nullptr;
}
if (setLayout.usesMaterial) {
if (!passLayout.material.IsValid() || passLayout.material.set != setIndex) {
return nullptr;
}
if (!materialConstants.IsValid()) {
return nullptr;
}
if (cachedDescriptorSet.materialVersion != materialVersion) {
cachedDescriptorSet.descriptorSet.set->WriteConstant(
passLayout.material.binding,
materialConstants.data,
materialConstants.size);
}
if (setLayout.usesTexture) {
if (textureView == nullptr ||
!passLayout.baseColorTexture.IsValid() ||
passLayout.baseColorTexture.set != setIndex) {
return nullptr;
}
cachedDescriptorSet.descriptorSet.set->Update(passLayout.baseColorTexture.binding, textureView);
}
cachedDescriptorSet.materialVersion = materialVersion;
cachedDescriptorSet.textureView = textureView;
}
if (setLayout.usesShadowReceiver) {
if (!passLayout.shadowReceiver.IsValid() || passLayout.shadowReceiver.set != setIndex) {
return nullptr;
}
cachedDescriptorSet.descriptorSet.set->WriteConstant(
passLayout.shadowReceiver.binding,
&shadowReceiverConstants,
sizeof(shadowReceiverConstants));
}
if (setLayout.usesBaseColorTexture) {
if (baseColorTextureView == nullptr ||
!passLayout.baseColorTexture.IsValid() ||
passLayout.baseColorTexture.set != setIndex) {
return nullptr;
}
if (cachedDescriptorSet.baseColorTextureView != baseColorTextureView) {
cachedDescriptorSet.descriptorSet.set->Update(
passLayout.baseColorTexture.binding,
baseColorTextureView);
}
}
if (setLayout.usesShadowMapTexture) {
if (shadowMapTextureView == nullptr ||
!passLayout.shadowMapTexture.IsValid() ||
passLayout.shadowMapTexture.set != setIndex) {
return nullptr;
}
if (cachedDescriptorSet.shadowMapTextureView != shadowMapTextureView) {
cachedDescriptorSet.descriptorSet.set->Update(
passLayout.shadowMapTexture.binding,
shadowMapTextureView);
}
}
cachedDescriptorSet.materialVersion = materialVersion;
cachedDescriptorSet.baseColorTextureView = baseColorTextureView;
cachedDescriptorSet.shadowMapTextureView = shadowMapTextureView;
return &cachedDescriptorSet;
}
@@ -772,8 +849,11 @@ void BuiltinForwardPipeline::DestroyPassResourceLayout(PassResourceLayout& passL
passLayout.descriptorSetCount = 0;
passLayout.perObject = {};
passLayout.material = {};
passLayout.shadowReceiver = {};
passLayout.baseColorTexture = {};
passLayout.linearClampSampler = {};
passLayout.shadowMapTexture = {};
passLayout.shadowMapSampler = {};
}
const Resources::Texture* BuiltinForwardPipeline::ResolveTexture(const Resources::Material* material) const {
@@ -833,6 +913,17 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
1.0f)
: Math::Vector4::Zero()
};
const ShadowReceiverConstants shadowReceiverConstants = {
sceneData.lighting.HasMainDirectionalShadow()
? sceneData.lighting.mainDirectionalShadow.viewProjection
: Math::Matrix4x4::Identity(),
sceneData.lighting.HasMainDirectionalShadow()
? sceneData.lighting.mainDirectionalShadow.shadowParams
: Math::Vector4::Zero(),
sceneData.lighting.HasMainDirectionalShadow()
? Math::Vector4(1.0f, 0.0f, 0.0f, 0.0f)
: Math::Vector4::Zero()
};
const Resources::Material* material = ResolveMaterial(visibleItem);
const ResolvedShaderPass resolvedShaderPass = ResolveSurfaceShaderPass(material);
@@ -849,8 +940,15 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
return false;
}
RHI::RHIResourceView* textureView = ResolveTextureView(visibleItem);
if (passLayout->baseColorTexture.IsValid() && textureView == nullptr) {
RHI::RHIResourceView* baseColorTextureView = ResolveTextureView(visibleItem);
if (passLayout->baseColorTexture.IsValid() && baseColorTextureView == nullptr) {
return false;
}
RHI::RHIResourceView* shadowMapTextureView = sceneData.lighting.HasMainDirectionalShadow()
? sceneData.lighting.mainDirectionalShadow.shadowMap
: m_fallbackTextureView;
if (passLayout->shadowMapTexture.IsValid() && shadowMapTextureView == nullptr) {
return false;
}
@@ -887,13 +985,16 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
const BuiltinPassSetLayoutMetadata& setLayout = passLayout->setLayouts[setIndex];
RHI::RHIDescriptorSet* descriptorSet = nullptr;
if (setLayout.usesPerObject || setLayout.usesMaterial || setLayout.usesTexture) {
if (setLayout.usesPerObject ||
setLayout.usesMaterial ||
setLayout.usesShadowReceiver ||
setLayout.usesTexture) {
const Core::uint64 objectId =
(setLayout.usesPerObject && visibleItem.gameObject != nullptr)
? visibleItem.gameObject->GetID()
: 0;
const Resources::Material* materialKey =
(setLayout.usesMaterial || setLayout.usesTexture) ? material : nullptr;
(setLayout.usesMaterial || setLayout.usesBaseColorTexture) ? material : nullptr;
CachedDescriptorSet* cachedDescriptorSet = GetOrCreateDynamicDescriptorSet(
passLayoutKey,
@@ -903,7 +1004,9 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
objectId,
materialKey,
materialConstants,
textureView);
shadowReceiverConstants,
baseColorTextureView,
shadowMapTextureView);
if (cachedDescriptorSet == nullptr || cachedDescriptorSet->descriptorSet.set == nullptr) {
return false;
}