ue4移动端阴影
2023-02-20 17:32 kk20161206 阅读(47) 评论(0) 编辑 收藏 举报1. 阴影的初始化:initViews里面调用了initDynamicShadows:
上面是initviews。
下面是initDynamicShadows:
void FSceneRenderer::InitDynamicShadows(FRHICommandListImmediate& RHICmdList, FGlobalDynamicIndexBuffer& DynamicIndexBuffer, FGlobalDynamicVertexBuffer& DynamicVertexBuffer, FGlobalDynamicReadBuffer& DynamicReadBuffer)
AddViewDependentWholeSceneShadowsForView(ViewDependentWholeSceneShadows, ViewDependentWholeSceneShadowsThatNeedCulling, VisibleLightInfo, *LightSceneInfo);
里面设置了shadowInfo
void FSceneRenderer::AddViewDependentWholeSceneShadowsForView( TArray<FProjectedShadowInfo*, SceneRenderingAllocator>& ShadowInfos, TArray<FProjectedShadowInfo*, SceneRenderingAllocator>& ShadowInfosThatNeedCulling, FVisibleLightInfo& VisibleLightInfo, FLightSceneInfo& LightSceneInfo) { // 遍历所有view, 给每个view创建view关联的全景阴影. for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FViewInfo& View = Views[ViewIndex]; const float LightShadowAmount = LightSceneInfo.Proxy->GetShadowAmount(); TArray<float, TInlineAllocator<2> > FadeAlphas; FadeAlphas.Init(0.0f, Views.Num()); FadeAlphas[ViewIndex] = LightShadowAmount; (......) // 如果是主视图, 处理投射阴影. if (IStereoRendering::IsAPrimaryView(View)) { const bool bExtraDistanceFieldCascade = LightSceneInfo.Proxy->ShouldCreateRayTracedCascade(View.GetFeatureLevel(), LightSceneInfo.IsPrecomputedLightingValid(), View.MaxShadowCascades); // 获取视图相关的投影数量. const int32 ProjectionCount = LightSceneInfo.Proxy->GetNumViewDependentWholeSceneShadows(View, LightSceneInfo.IsPrecomputedLightingValid()) + (bExtraDistanceFieldCascade?1:0); FSceneRenderTargets& SceneContext_ConstantsOnly = FSceneRenderTargets::Get_FrameConstantsOnly(); // 根据投影数量, 创建投射阴影初始化器和对应的FProjectedShadowInfo. for (int32 Index = 0; Index < ProjectionCount; Index++) { FWholeSceneProjectedShadowInitializer ProjectedShadowInitializer; int32 LocalIndex = Index; // Indexing like this puts the ray traced shadow cascade last (might not be needed) if(bExtraDistanceFieldCascade && LocalIndex + 1 == ProjectionCount) { LocalIndex = INDEX_NONE; } if (LightSceneInfo.Proxy->GetViewDependentWholeSceneProjectedShadowInitializer(View, LocalIndex, LightSceneInfo.IsPrecomputedLightingValid(), ProjectedShadowInitializer)) { const FIntPoint ShadowBufferResolution( FMath::Clamp(GetCachedScalabilityCVars().MaxCSMShadowResolution, 1, (int32)GMaxShadowDepthBufferSizeX), FMath::Clamp(GetCachedScalabilityCVars().MaxCSMShadowResolution, 1, (int32)GMaxShadowDepthBufferSizeY)); // 创建FProjectedShadowInfo实例. FProjectedShadowInfo* ProjectedShadowInfo = new(FMemStack::Get(), 1, 16) FProjectedShadowInfo; // 阴影边界. uint32 ShadowBorder = NeedsUnatlasedCSMDepthsWorkaround(FeatureLevel) ? 0 : SHADOW_BORDER; // 设置全景投影. ProjectedShadowInfo->SetupWholeSceneProjection( &LightSceneInfo, &View, ProjectedShadowInitializer, ShadowBufferResolution.X - ShadowBorder * 2, ShadowBufferResolution.Y - ShadowBorder * 2, ShadowBorder, false // no RSM ); ProjectedShadowInfo->FadeAlphas = FadeAlphas; // 将ProjectedShadowInfo添加到可见光源信息的相关列表中. FVisibleLightInfo& LightViewInfo = VisibleLightInfos[LightSceneInfo.Id]; VisibleLightInfo.MemStackProjectedShadows.Add(ProjectedShadowInfo); VisibleLightInfo.AllProjectedShadows.Add(ProjectedShadowInfo); ShadowInfos.Add(ProjectedShadowInfo); // 添加到待裁剪阴影列表. if (!ProjectedShadowInfo->bRayTracedDistanceField) { ShadowInfosThatNeedCulling.Add(ProjectedShadowInfo); } } } // 处理RSM(Refletive Shadow Map), 用于LPV光照. FSceneViewState* ViewState = (FSceneViewState*)View.State; if (ViewState) { (......) } } } }
收集阴影相关的图元
GatherShadowPrimitives 接下来分析初始化阶段的GatherShadowPrimitives: void FSceneRenderer::GatherShadowPrimitives(const TArray<FProjectedShadowInfo*, SceneRenderingAllocator>& PreShadows, const TArray<FProjectedShadowInfo*, SceneRenderingAllocator>& ViewDependentWholeSceneShadows, bool bStaticSceneOnly) { // 存在预阴影或视图关联全景阴影. if (PreShadows.Num() || ViewDependentWholeSceneShadows.Num()) { TArray<FGatherShadowPrimitivesPacket*,SceneRenderingAllocator> Packets; // 八叉树遍历裁剪阴影. if (GUseOctreeForShadowCulling) { Packets.Reserve(100); // 查找和位于八叉树的阴影锥体相交的图元. for(FScenePrimitiveOctree::TConstIterator<SceneRenderingAllocator> PrimitiveOctreeIt(Scene->PrimitiveOctree); PrimitiveOctreeIt.HasPendingNodes(); PrimitiveOctreeIt.Advance()) { const FScenePrimitiveOctree::FNode& PrimitiveOctreeNode = PrimitiveOctreeIt.GetCurrentNode(); const FOctreeNodeContext& PrimitiveOctreeNodeContext = PrimitiveOctreeIt.GetCurrentContext(); { // 查找八叉树节点可能包含关联图元的孩子节点. FOREACH_OCTREE_CHILD_NODE(ChildRef) { if(PrimitiveOctreeNode.HasChild(ChildRef)) { // 检查孩子节点是否至少在一个阴影内. const FOctreeNodeContext ChildContext = PrimitiveOctreeNodeContext.GetChildContext(ChildRef); bool bIsInFrustum = false; if(!bIsInFrustum) { // 遍历所有的preshadow, 判断孩子节点是否和preshadow相交. for(int32 ShadowIndex = 0, Num = PreShadows.Num(); ShadowIndex < Num; ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = PreShadows[ShadowIndex]; // 检测图元是否在阴影锥体内. if(ProjectedShadowInfo->CasterFrustum.IntersectBox(ChildContext.Bounds.Center + ProjectedShadowInfo->PreShadowTranslation, ChildContext.Bounds.Extent)) { bIsInFrustum = true; break; } } } // 如果还不在锥体内, 则让孩子节点的包围盒和视图相关的全景阴影执行相交检测. if (!bIsInFrustum) { for(int32 ShadowIndex = 0, Num = ViewDependentWholeSceneShadows.Num(); ShadowIndex < Num; ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = ViewDependentWholeSceneShadows[ShadowIndex]; //check(ProjectedShadowInfo->CasterFrustum.PermutedPlanes.Num()); // Check if this primitive is in the shadow's frustum. if(ProjectedShadowInfo->CasterFrustum.IntersectBox( ChildContext.Bounds.Center + ProjectedShadowInfo->PreShadowTranslation, ChildContext.Bounds.Extent )) { bIsInFrustum = true; break; } } } // 如何图元和至少一个阴影相交, 则加入图元八叉树迭代器的待定节点堆栈(pending node stack)中. if(bIsInFrustum) { PrimitiveOctreeIt.PushChild(ChildRef); } } } // FOREACH_OCTREE_CHILD_NODE } if (PrimitiveOctreeNode.GetElementCount() > 0) { FGatherShadowPrimitivesPacket* Packet = new(FMemStack::Get()) FGatherShadowPrimitivesPacket(Scene, Views, &PrimitiveOctreeNode, 0, 0, PreShadows, ViewDependentWholeSceneShadows, FeatureLevel, bStaticSceneOnly); Packets.Add(Packet); } } // for } else // 非八叉树遍历. { const int32 PacketSize = CVarParallelGatherNumPrimitivesPerPacket.GetValueOnRenderThread(); const int32 NumPackets = FMath::DivideAndRoundUp(Scene->Primitives.Num(), PacketSize); Packets.Reserve(NumPackets); // 非八叉树模式, 直接线性遍历所有图元, 添加同等数量的FGatherShadowPrimitivesPacket实例. for (int32 PacketIndex = 0; PacketIndex < NumPackets; PacketIndex++) { const int32 StartPrimitiveIndex = PacketIndex * PacketSize; const int32 NumPrimitives = FMath::Min(PacketSize, Scene->Primitives.Num() - StartPrimitiveIndex); FGatherShadowPrimitivesPacket* Packet = new(FMemStack::Get()) FGatherShadowPrimitivesPacket(Scene, Views, NULL, StartPrimitiveIndex, NumPrimitives, PreShadows, ViewDependentWholeSceneShadows, FeatureLevel, bStaticSceneOnly); Packets.Add(Packet); } } // 调用并行For过滤掉和阴影不相交的图元 ParallelFor(Packets.Num(), [&Packets](int32 Index) { Packets[Index]->AnyThreadTask(); }, !(FApp::ShouldUseThreadingForPerformance() && CVarParallelGatherShadowPrimitives.GetValueOnRenderThread() > 0) ); // 释放资源. for (int32 PacketIndex = 0; PacketIndex < Packets.Num(); PacketIndex++) { FGatherShadowPrimitivesPacket* Packet = Packets[PacketIndex]; Packet->RenderThreadFinalize(); Packet->~FGatherShadowPrimitivesPacket(); } } }
线程运行:
FGatherShadowPrimitivesPacket 上面代码中调用Packets[Index]->AnyThreadTask()以收集阴影图元数据包,调用FilterPrimitiveForShadows过滤不受阴影影响的图元,调用Packet->RenderThreadFinalize()将收集的图元添加到对应的阴影列表中。下面分析它们的具体执行逻辑: void FGatherShadowPrimitivesPacket::AnyThreadTask() { if (Node) // 如果存在节点 { // 利用八叉树过滤和收集受阴影影响的图元. for (FScenePrimitiveOctree::ElementConstIt NodePrimitiveIt(Node->GetElementIt()); NodePrimitiveIt; ++NodePrimitiveIt) { if (NodePrimitiveIt->PrimitiveFlagsCompact.bCastDynamicShadow) { FilterPrimitiveForShadows(NodePrimitiveIt->Bounds, NodePrimitiveIt->PrimitiveFlagsCompact, NodePrimitiveIt->PrimitiveSceneInfo, NodePrimitiveIt->Proxy); } } } else { // 利用信息包的索引范围遍历, 逐个去过滤和收集受阴影影响的图元. for (int32 PrimitiveIndex = StartPrimitiveIndex; PrimitiveIndex < StartPrimitiveIndex + NumPrimitives; PrimitiveIndex++) { FPrimitiveFlagsCompact PrimitiveFlagsCompact = Scene->PrimitiveFlagsCompact[PrimitiveIndex]; if (PrimitiveFlagsCompact.bCastDynamicShadow) { FilterPrimitiveForShadows(Scene->PrimitiveBounds[PrimitiveIndex].BoxSphereBounds, PrimitiveFlagsCompact, Scene->Primitives[PrimitiveIndex], Scene->PrimitiveSceneProxies[PrimitiveIndex]); } } } } void FGatherShadowPrimitivesPacket::FilterPrimitiveForShadows(const FBoxSphereBounds& PrimitiveBounds, FPrimitiveFlagsCompact PrimitiveFlagsCompact, FPrimitiveSceneInfo* PrimitiveSceneInfo, FPrimitiveSceneProxy* PrimitiveProxy) { // 检测图元是否受任意一个preshadow影响。 if (PreShadows.Num() && PrimitiveFlagsCompact.bCastStaticShadow && PrimitiveFlagsCompact.bStaticLighting) { for (int32 ShadowIndex = 0, Num = PreShadows.Num(); ShadowIndex < Num; ShadowIndex++) { FProjectedShadowInfo* RESTRICT ProjectedShadowInfo = PreShadows[ShadowIndex]; // 图元包围盒和阴影投射锥体相交测试. bool bInFrustum = ProjectedShadowInfo->CasterFrustum.IntersectBox(PrimitiveBounds.Origin, ProjectedShadowInfo->PreShadowTranslation, PrimitiveBounds.BoxExtent); // 在投影锥体内且相互影响的加入PreShadowSubjectPrimitives中. if (bInFrustum && ProjectedShadowInfo->GetLightSceneInfoCompact().AffectsPrimitive(PrimitiveBounds, PrimitiveProxy)) { PreShadowSubjectPrimitives[ShadowIndex].Add(PrimitiveSceneInfo); } } } // 图元和全景阴影相交测试. for (int32 ShadowIndex = 0, Num = ViewDependentWholeSceneShadows.Num();ShadowIndex < Num;ShadowIndex++) { const FProjectedShadowInfo* RESTRICT ProjectedShadowInfo = ViewDependentWholeSceneShadows[ShadowIndex]; const FLightSceneInfo& RESTRICT LightSceneInfo = ProjectedShadowInfo->GetLightSceneInfo(); const FLightSceneProxy& RESTRICT LightProxy = *LightSceneInfo.Proxy; const FVector LightDirection = LightProxy.GetDirection(); const FVector PrimitiveToShadowCenter = ProjectedShadowInfo->ShadowBounds.Center - PrimitiveBounds.Origin; // 投影图元的包围盒到光源向量上. const float ProjectedDistanceFromShadowOriginAlongLightDir = PrimitiveToShadowCenter | LightDirection; const float PrimitiveDistanceFromCylinderAxisSq = (-LightDirection * ProjectedDistanceFromShadowOriginAlongLightDir + PrimitiveToShadowCenter).SizeSquared(); const float CombinedRadiusSq = FMath::Square(ProjectedShadowInfo->ShadowBounds.W + PrimitiveBounds.SphereRadius); // 检测图元是否在阴影的[圆柱体]内. if (PrimitiveDistanceFromCylinderAxisSq < CombinedRadiusSq && !(ProjectedDistanceFromShadowOriginAlongLightDir < 0 && PrimitiveToShadowCenter.SizeSquared() > CombinedRadiusSq) && ProjectedShadowInfo->CascadeSettings.ShadowBoundsAccurate.IntersectBox(PrimitiveBounds.Origin, PrimitiveBounds.BoxExtent)) { // 为RSM执行距离裁剪. const float MinScreenRadiusForShadowCaster = ProjectedShadowInfo->bReflectiveShadowmap ? GMinScreenRadiusForShadowCasterRSM : GMinScreenRadiusForShadowCaster; // 屏幕空间尺寸裁剪 bool bScreenSpaceSizeCulled = false; { const float DistanceSquared = (PrimitiveBounds.Origin - ProjectedShadowInfo->DependentView->ShadowViewMatrices.GetViewOrigin()).SizeSquared(); bScreenSpaceSizeCulled = FMath::Square(PrimitiveBounds.SphereRadius) < FMath::Square(MinScreenRadiusForShadowCaster) * DistanceSquared * ProjectedShadowInfo->DependentView->LODDistanceFactorSquared; } // 是否计算嵌入阴影(InsetShadow). bool bCastsInsetShadows = PrimitiveProxy->CastsInsetShadow(); // 处理图元的光源附加根组件. if (PrimitiveSceneInfo->LightingAttachmentRoot.IsValid()) { FAttachmentGroupSceneInfo& AttachmentGroup = PrimitiveSceneInfo->Scene->AttachmentGroups.FindChecked(PrimitiveSceneInfo->LightingAttachmentRoot); bCastsInsetShadows = AttachmentGroup.ParentSceneInfo && AttachmentGroup.ParentSceneInfo->Proxy->CastsInsetShadow(); } // 检测各种各样的条件判断, 所有条件通过后才加入ViewDependentWholeSceneShadowSubjectPrimitives列表. if (!bScreenSpaceSizeCulled && ProjectedShadowInfo->GetLightSceneInfoCompact().AffectsPrimitive(PrimitiveBounds, PrimitiveProxy) && (!LightProxy.HasStaticLighting() || (!LightSceneInfo.IsPrecomputedLightingValid() || LightProxy.UseCSMForDynamicObjects())) && !(ProjectedShadowInfo->bReflectiveShadowmap && !PrimitiveProxy->AffectsDynamicIndirectLighting()) && (!bCastsInsetShadows || ProjectedShadowInfo->bReflectiveShadowmap) && !ShouldCreateObjectShadowForStationaryLight(&LightSceneInfo, PrimitiveProxy, true) && (!bStaticSceneOnly || PrimitiveProxy->HasStaticLighting()) && (!LightProxy.UseCSMForDynamicObjects() || !PrimitiveProxy->HasStaticLighting())) { ViewDependentWholeSceneShadowSubjectPrimitives[ShadowIndex].Add(PrimitiveSceneInfo); } } } } void FGatherShadowPrimitivesPacket::RenderThreadFinalize() { // 将每个PreShadow实例内的所有图元都加入该阴影实例中. for (int32 ShadowIndex = 0; ShadowIndex < PreShadowSubjectPrimitives.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = PreShadows[ShadowIndex]; for (int32 PrimitiveIndex = 0; PrimitiveIndex < PreShadowSubjectPrimitives[ShadowIndex].Num(); PrimitiveIndex++) { ProjectedShadowInfo->AddSubjectPrimitive(PreShadowSubjectPrimitives[ShadowIndex][PrimitiveIndex], &Views, FeatureLevel, false); } } // 将每个全景阴影实例内的所有图元都加入该阴影实例中. for (int32 ShadowIndex = 0; ShadowIndex < ViewDependentWholeSceneShadowSubjectPrimitives.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = ViewDependentWholeSceneShadows[ShadowIndex]; (......) for (int32 PrimitiveIndex = 0; PrimitiveIndex < ViewDependentWholeSceneShadowSubjectPrimitives[ShadowIndex].Num(); PrimitiveIndex++) { ProjectedShadowInfo->AddSubjectPrimitive(ViewDependentWholeSceneShadowSubjectPrimitives[ShadowIndex][PrimitiveIndex], NULL, FeatureLevel, bRecordShadowSubjectsForMobile); } } }
ProjectedShadowInfo里有了图元的信息。
分配renderTarget:
AllocateShadowDepthTargets 继续分析初始化阶段的AllocateShadowDepthTargets: void FSceneRenderer::AllocateShadowDepthTargets(FRHICommandListImmediate& RHICmdList) { FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); // 对可见阴影基于分配需求排序. // 此帧的2d阴影图可以跨光源合并成图集. TArray<FProjectedShadowInfo*, SceneRenderingAllocator> Shadows; // 横跨持续存在于多帧的2d阴影图不能合并成图集. TArray<FProjectedShadowInfo*, SceneRenderingAllocator> CachedSpotlightShadows; TArray<FProjectedShadowInfo*, SceneRenderingAllocator> TranslucentShadows; // 横跨持续存在于多帧的2d阴影图 TArray<FProjectedShadowInfo*, SceneRenderingAllocator> CachedPreShadows; TArray<FProjectedShadowInfo*, SceneRenderingAllocator> RSMShadows; // 点光源的cubemap, 不能合并成图集. TArray<FProjectedShadowInfo*, SceneRenderingAllocator> WholeScenePointShadows; // 遍历所有光源 for (TSparseArray<FLightSceneInfoCompact>::TConstIterator LightIt(Scene->Lights); LightIt; ++LightIt) { const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt; FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact.LightSceneInfo; FVisibleLightInfo& VisibleLightInfo = VisibleLightInfos[LightSceneInfo->Id]; // 同一个光源的所有级联阴影须在同一个纹理中. TArray<FProjectedShadowInfo*, SceneRenderingAllocator> WholeSceneDirectionalShadows; // 遍历光源的所有投射阴影实例. for (int32 ShadowIndex = 0; ShadowIndex < VisibleLightInfo.AllProjectedShadows.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = VisibleLightInfo.AllProjectedShadows[ShadowIndex]; // 检测阴影是否至少在一个view中可见. bool bShadowIsVisible = false; for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { FViewInfo& View = Views[ViewIndex]; if (ProjectedShadowInfo->DependentView && ProjectedShadowInfo->DependentView != &View) { continue; } const FVisibleLightViewInfo& VisibleLightViewInfo = View.VisibleLightInfos[LightSceneInfo->Id]; const FPrimitiveViewRelevance ViewRelevance = VisibleLightViewInfo.ProjectedShadowViewRelevanceMap[ShadowIndex]; const bool bHasViewRelevance = (ProjectedShadowInfo->bTranslucentShadow && ViewRelevance.HasTranslucency()) || (!ProjectedShadowInfo->bTranslucentShadow && ViewRelevance.bOpaque); bShadowIsVisible |= bHasViewRelevance && VisibleLightViewInfo.ProjectedShadowVisibilityMap[ShadowIndex]; } // 检测阴影缓存模式为可移动图元时的条件可见性. if (ProjectedShadowInfo->CacheMode == SDCM_MovablePrimitivesOnly && !ProjectedShadowInfo->HasSubjectPrims()) { FCachedShadowMapData& CachedShadowMapData = Scene->CachedShadowMaps.FindChecked(ProjectedShadowInfo->GetLightSceneInfo().Id); if (!CachedShadowMapData.bCachedShadowMapHasPrimitives) { bShadowIsVisible = false; } } // 移动端渲染器只支持半透明物体逐阴影或CSM. if (FeatureLevel < ERHIFeatureLevel::SM5 && (!ProjectedShadowInfo->bPerObjectOpaqueShadow && !(ProjectedShadowInfo->bDirectionalLight && ProjectedShadowInfo->bWholeSceneShadow))) { bShadowIsVisible = false; } // 前向渲染路径中, 动态阴影会被投射到光源衰减纹理(light attenuation texture)的通道中. if (IsForwardShadingEnabled(ShaderPlatform) && ProjectedShadowInfo->GetLightSceneInfo().GetDynamicShadowMapChannel() == -1) { bShadowIsVisible = false; } // 阴影可见, 则根据不同类型加入到不同的阴影实例列表中. if (bShadowIsVisible) { (......) bool bNeedsProjection = ProjectedShadowInfo->CacheMode != SDCM_StaticPrimitivesOnly // Mobile rendering only projects opaque per object shadows. && (FeatureLevel >= ERHIFeatureLevel::SM5 || ProjectedShadowInfo->bPerObjectOpaqueShadow); if (bNeedsProjection) { if (ProjectedShadowInfo->bReflectiveShadowmap) { VisibleLightInfo.RSMsToProject.Add(ProjectedShadowInfo); } else if (ProjectedShadowInfo->bCapsuleShadow) { VisibleLightInfo.CapsuleShadowsToProject.Add(ProjectedShadowInfo); } else { VisibleLightInfo.ShadowsToProject.Add(ProjectedShadowInfo); } } const bool bNeedsShadowmapSetup = !ProjectedShadowInfo->bCapsuleShadow && !ProjectedShadowInfo->bRayTracedDistanceField; if (bNeedsShadowmapSetup) { if (ProjectedShadowInfo->bReflectiveShadowmap) { check(ProjectedShadowInfo->bWholeSceneShadow); RSMShadows.Add(ProjectedShadowInfo); } else if (ProjectedShadowInfo->bPreShadow && ProjectedShadowInfo->bAllocatedInPreshadowCache) { CachedPreShadows.Add(ProjectedShadowInfo); } else if (ProjectedShadowInfo->bDirectionalLight && ProjectedShadowInfo->bWholeSceneShadow) { WholeSceneDirectionalShadows.Add(ProjectedShadowInfo); } else if (ProjectedShadowInfo->bOnePassPointLightShadow) { WholeScenePointShadows.Add(ProjectedShadowInfo); } else if (ProjectedShadowInfo->bTranslucentShadow) { TranslucentShadows.Add(ProjectedShadowInfo); } else if (ProjectedShadowInfo->CacheMode == SDCM_StaticPrimitivesOnly) { check(ProjectedShadowInfo->bWholeSceneShadow); CachedSpotlightShadows.Add(ProjectedShadowInfo); } else { Shadows.Add(ProjectedShadowInfo); } } } } // 排序级联阴影, 在级联之间的混合是必须的. VisibleLightInfo.ShadowsToProject.Sort(FCompareFProjectedShadowInfoBySplitIndex()); VisibleLightInfo.RSMsToProject.Sort(FCompareFProjectedShadowInfoBySplitIndex()); // 分配CSM深度渲染纹理. AllocateCSMDepthTargets(RHICmdList, WholeSceneDirectionalShadows); } // 处理缓存的PreShadow. if (CachedPreShadows.Num() > 0) { // 创建场景的PreShadow缓存深度纹理. if (!Scene->PreShadowCacheDepthZ) { FPooledRenderTargetDesc Desc(FPooledRenderTargetDesc::Create2DDesc(SceneContext.GetPreShadowCacheTextureResolution(), PF_ShadowDepth, FClearValueBinding::None, TexCreate_None, TexCreate_DepthStencilTargetable | TexCreate_ShaderResource, false)); Desc.AutoWritable = false; GRenderTargetPool.FindFreeElement(RHICmdList, Desc, Scene->PreShadowCacheDepthZ, TEXT("PreShadowCacheDepthZ"), true, ERenderTargetTransience::NonTransient); } SortedShadowsForShadowDepthPass.PreshadowCache.RenderTargets.DepthTarget = Scene->PreShadowCacheDepthZ; for (int32 ShadowIndex = 0; ShadowIndex < CachedPreShadows.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = CachedPreShadows[ShadowIndex]; ProjectedShadowInfo->RenderTargets.DepthTarget = Scene->PreShadowCacheDepthZ.GetReference(); // 设置阴影深度视图, 会在场景渲染器中为阴影找到一个专用的视图. ProjectedShadowInfo->SetupShadowDepthView(RHICmdList, this); SortedShadowsForShadowDepthPass.PreshadowCache.Shadows.Add(ProjectedShadowInfo); } } // 分配各类阴影的渲染纹理, 包含点光源cubemap,RSM,缓存的聚光灯,逐物体,透明阴影等.(CSM已经在前面分配过了) AllocateOnePassPointLightDepthTargets(RHICmdList, WholeScenePointShadows); AllocateRSMDepthTargets(RHICmdList, RSMShadows); AllocateCachedSpotlightShadowDepthTargets(RHICmdList, CachedSpotlightShadows); AllocatePerObjectShadowDepthTargets(RHICmdList, Shadows); AllocateTranslucentShadowDepthTargets(RHICmdList, TranslucentShadows); // 更新透明阴影图的uniform buffer. for (int32 TranslucentShadowIndex = 0; TranslucentShadowIndex < TranslucentShadows.Num(); ++TranslucentShadowIndex) { FProjectedShadowInfo* ShadowInfo = TranslucentShadows[TranslucentShadowIndex]; const int32 PrimitiveIndex = ShadowInfo->GetParentSceneInfo()->GetIndex(); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex) { FViewInfo& View = Views[ViewIndex]; FUniformBufferRHIRef* UniformBufferPtr = View.TranslucentSelfShadowUniformBufferMap.Find(PrimitiveIndex); if (UniformBufferPtr) { FTranslucentSelfShadowUniformParameters Parameters; SetupTranslucentSelfShadowUniformParameters(ShadowInfo, Parameters); RHIUpdateUniformBuffer(*UniformBufferPtr, &Parameters); } } } // 删除完全没有被使用的阴影缓存. for (TMap<int32, FCachedShadowMapData>::TIterator CachedShadowMapIt(Scene->CachedShadowMaps); CachedShadowMapIt; ++CachedShadowMapIt) { FCachedShadowMapData& ShadowMapData = CachedShadowMapIt.Value(); if (ShadowMapData.ShadowMap.IsValid() && ViewFamily.CurrentRealTime - ShadowMapData.LastUsedTime > 2.0f) { ShadowMapData.ShadowMap.Release(); } } }
收集动态meshElement:
GatherShadowDynamicMeshElements 阴影初始化阶段的最后一步是GatherShadowDynamicMeshElements: void FSceneRenderer::GatherShadowDynamicMeshElements(FGlobalDynamicIndexBuffer& DynamicIndexBuffer, FGlobalDynamicVertexBuffer& DynamicVertexBuffer, FGlobalDynamicReadBuffer& DynamicReadBuffer) { TArray<const FSceneView*> ReusedViewsArray; ReusedViewsArray.AddZeroed(1); // 遍历所有阴影图图集. for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.ShadowMapAtlases.Num(); AtlasIndex++) { FSortedShadowMapAtlas& Atlas = SortedShadowsForShadowDepthPass.ShadowMapAtlases[AtlasIndex]; // 遍历阴影图图集上的所有阴影实例, 收集它们需要投射阴影的网格元素. for (int32 ShadowIndex = 0; ShadowIndex < Atlas.Shadows.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = Atlas.Shadows[ShadowIndex]; FVisibleLightInfo& VisibleLightInfo = VisibleLightInfos[ProjectedShadowInfo->GetLightSceneInfo().Id]; ProjectedShadowInfo->GatherDynamicMeshElements(*this, VisibleLightInfo, ReusedViewsArray, DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer); } }
...//点光源阴影、半透明阴影等
}
阴影初始化总结
用于前面花费很多笔墨和小节来详细剖析阴影初始化的具体步骤和细节,难免会让很多童鞋望而生畏,那么本节就简洁扼要地总结阴影初始化InitDynamicShadows
的主要过程,如下:
-
根据view、场景光源、控制台变量初始化阴影相关标记。
-
遍历场景所有光源(Scene->Lights),执行以下操作:
-
如果光源没有开启阴影或阴影质量太小,或者光源在所有view都不可见,忽略之,不执行阴影投射。
-
如果是点光源全景阴影,则将该光源组件名字加入Scene的UsedWholeScenePointLightNames列表中。
-
如果符合全景阴影的创建条件,调用CreateWholeSceneProjectedShadow:
- 初始化阴影数据,计算阴影所需的分辨率、过渡因子(FadeAlpha)等。
- 若过渡因子太小(小于1/256),则直接返回。
- 遍历光源的投射阴影数量,每次都执行分辨率计算、位置(SizeX、SizeY)计算、需创建的阴影图数量等。
- 根据阴影图数量创建同等个数的FProjectedShadowInfo阴影实例,对每个阴影实例:
- 设置全景投射参数(视锥体边界、变换矩阵、深度、深度偏移等)、过渡因子、缓存模式等。
- 加入VisibleLightInfo.MemStackProjectedShadows列表;如果是单通道点光源阴影,填充阴影实例的cubemap6个面的数据(视图投影矩阵、包围盒、远近裁剪面等),并初始化锥体。
- 对非光追距离场阴影,执行CPU侧裁剪。构建光源视图的凸面体,再遍历光源的移动图元列表,调用IntersectsConvexHulls让光源包围盒和图元包围盒求交测试,相交的那些图元才会添加到阴影实例的主体图元列表中。对光源的静态图元做相似的操作。
- 最后将需要渲染的阴影实例加入VisibleLightInfo.AllProjectedShadows列表中。
- 根据阴影图数量创建同等个数的FProjectedShadowInfo阴影实例,对每个阴影实例:
-
针对两类光源(移动和固定的光源、尚未构建的静态光源)创建CSM(级联阴影)。调用AddViewDependentWholeSceneShadowsForView创建视图关联的CSM:
- 遍历所有view,针对每个view:
- 如果不是主view,直接跳过后续步骤。
- 获取视图相关的全景投影数量,创建同等数量的FProjectedShadowInfo阴影实例,给每个阴影实例设置全景投影参数,最后添加到阴影实例列表和待裁剪列表中。
- 遍历所有view,针对每个view:
-
处理交互阴影(指光源和图元之间的影响,包含PerObject阴影、透明阴影、自阴影等),遍历光源的动态和静态图元,给每个图元调用SetupInteractionShadows设置交互阴影:
- 处理光源附加根组件,设置相关标记。如果存在附加根组件,跳过后续步骤。
- 如果需要创建阴影实例,则调用CreatePerObjectProjectedShadow创建逐物体阴影:
- 遍历所有view,收集阴影相关的标记。
- 如果本帧不可见且下一帧也没有潜在可见,则直接返回,跳过后续的阴影创建和设置。
- 没有有效的阴影组图元,直接返回。
- 计算阴影的各类数据(阴影视锥、分辨率、可见性标记、图集位置等)。
- 若过渡因子(FadeAlpha)小于某个阈值(1.0/256.0),直接返回。
- 符合阴影创建条件且是不透明阴影,则创和设置建阴影实例,加入VisibleLightInfo.MemStackProjectedShadows列表中。如果本帧可见则加入到阴影实例的主体图元列表,如果下一帧潜在可见则加入到VisibleLightInfo.OccludedPerObjectShadows的实例中。
- 符合阴影创建条件且是半透明阴影,执行上步骤类似操作。
-
-
调用InitProjectedShadowVisibility执行阴影可见性判定:
- 遍历场景的所有光源,针对每个光源:
- 分配视图内的投射阴影可见性和关联的容器。
- 遍历可见光源所有的阴影实例,针对每个阴影实例:
- 保存阴影索引。
- 遍历所有view,针对每个view:
- 处理视图关联的阴影,跳过那些不在视图视锥内的光源。
- 计算主体图元的视图关联数据,断阴影是否被遮挡,设置阴影可见性标记。
- 如果阴影可见且不是RSM,利用FViewElementPDI绘制阴影锥体。
- 遍历场景的所有光源,针对每个光源:
-
调用UpdatePreshadowCache清理旧的预计算阴影,尝试增加新的到缓存中:
- 初始化纹理布局。
- 遍历所有缓存的预阴影, 删除不在此帧渲染的实例。
- 收集可以被缓存的PreShadow列表。
- 对PreShadow从大到小排序(更大的PreShadow在渲染深度时会有更多的物体)。
- 遍历所有未缓存的PreShadow,尝试从纹理布局中给PreShadow找空间,若找到,则设置相关数据并添加到Scene->CachedPreshadows列表中。
-
调用GatherShadowPrimitives收集图元列表,以处理不同类型的阴影:
- 如果没有PreShadow且没有视图关联的全景阴影(ViewDependentWholeSceneShadows),则直接返回。
- 如果允许八叉树遍历(GUseOctreeForShadowCulling决定),利用八叉树遍历Scene->PrimitiveOctree,针对每个孩子节点:
- 检查孩子节点是否至少在一个阴影(包含PreShadow和视图相关的全景阴影)内,如果是,则push到节点容器中。
- 如果图元节点的元素大于0,从FMemStack创建一个FGatherShadowPrimitivesPacket实例,将该节点的相关数据存入其中,添加到FGatherShadowPrimitivesPacket实例列表中。
- 如果是非八叉树遍历模式,则线性遍历图元,创建FGatherShadowPrimitivesPacket并加入到列表中。
- 利用ParallelFor并行地过滤掉和阴影不相交的图元,收集和阴影相交的图元。
- 收集最后阶段,将受阴影影响的图元加入阴影实例的SubjectPrimitive列表中,清理之前申请的资源。
-
调用AllocateShadowDepthTargets分配阴影图所需的渲染纹理:
- 初始化不同类型的指定了分配器的阴影列表。
- 遍历所有光源,针对每个光源:
- 遍历光源的所有阴影实例,针对每个阴影实例:
- 检测阴影是否至少在一个view中可见。
- 检测阴影缓存模式为可移动图元时的条件可见性。
- 其它特殊的可见性判断。
- 如果阴影可见,根据不同类型加入到不同的阴影实例列表中。
- 排序级联阴影,因为在级联之间的混合要求是有序的。
- 调用AllocateCSMDepthTargets分配CSM深度渲染纹理。
- 处理PreShadow。
- 依次分配点光源cubemap、RSM、缓存的聚光灯、逐物体、透明阴影的渲染纹理。
- 更新透明阴影图的uniform buffer。
- 删除完全没有被使用的阴影缓存。
- 遍历光源的所有阴影实例,针对每个阴影实例:
-
调用GatherShadowDynamicMeshElements收集阴影的动态网格元素:
- 遍历所有阴影图图集(ShadowMapAtlases),收集每个图集内的所有阴影实例的网格元素。
- 遍历所有RSM阴影图图集(RSMAtlases),收集每个图集内的所有阴影实例的网格元素。
- 遍历所有点光源立方体阴影图(ShadowMapCubemaps),收集每个立方体阴影图内的所有阴影实例的网格元素。
- 遍历所有PreShadow缓存的阴影图(PreshadowCache),收集阴影实例的网格元素。
- 遍历所有透明物体阴影图图集(TranslucencyShadowMapAtlases),收集每个图集内的所有阴影实例的网格元素。
阴影初始化总结完了,由此可知阴影的处理非常非常复杂,涉及的逻辑和优化技术甚多。这里粗略总结一下阴影初始化阶段涉及的优化技巧:
- 利用物体(视图、光源、阴影、图元)的简单形状做相交测试,剔除不相交的阴影元素。
- 利用各类标记(物体、视图、控制台、全局变量等等)及衍生标记,剔除不符合的阴影元素。
- 利用中间数据(过渡因子、屏幕尺寸大小、深度值等等)剔除不符合的阴影元素。
- 特殊数据结构(纹理布局、阴影图集、八叉树、连续线性数组)和遍历方式(并行、八叉树、线性)提升执行效果。
- 充分利用缓存(PreShadowCache、潜在可见性等)减少渲染效果。
- 对阴影类型进行排序,减少渲染状态切换,减少CPU和GPU交互数据,提升缓存命中率。
- 不同粒度的遮挡剔除。
2. 阴影的渲染:
// Engine\Source\Runtime\Renderer\Private\ShadowDepthRendering.cpp void FSceneRenderer::RenderShadowDepthMaps(FRHICommandListImmediate& RHICmdList) { (......) // 渲染阴影图集. FSceneRenderer::RenderShadowDepthMapAtlases(RHICmdList); // 渲染点光源阴影立方体图. for (int32 CubemapIndex = 0; CubemapIndex < SortedShadowsForShadowDepthPass.ShadowMapCubemaps.Num(); CubemapIndex++) { const FSortedShadowMapAtlas& ShadowMap = SortedShadowsForShadowDepthPass.ShadowMapCubemaps[CubemapIndex]; FSceneRenderTargetItem& RenderTarget = ShadowMap.RenderTargets.DepthTarget->GetRenderTargetItem(); FIntPoint TargetSize = ShadowMap.RenderTargets.DepthTarget->GetDesc().Extent; FProjectedShadowInfo* ProjectedShadowInfo = ShadowMap.Shadows[0]; // 是否可以并行绘制 const bool bDoParallelDispatch = RHICmdList.IsImmediate() && // translucent shadows are draw on the render thread, using a recursive cmdlist (which is not immediate) GRHICommandList.UseParallelAlgorithms() && CVarParallelShadows.GetValueOnRenderThread() && (ProjectedShadowInfo->IsWholeSceneDirectionalShadow() || CVarParallelShadowsNonWholeScene.GetValueOnRenderThread()); FString LightNameWithLevel; GetLightNameForDrawEvent(ProjectedShadowInfo->GetLightSceneInfo().Proxy, LightNameWithLevel); // 设置Uniform Buffer. ProjectedShadowInfo->SetupShadowUniformBuffers(RHICmdList, Scene); // 阴影渲染Pass开始. auto BeginShadowRenderPass = [this, &RenderTarget, &SceneContext](FRHICommandList& InRHICmdList, bool bPerformClear) { FRHITexture* DepthTarget = RenderTarget.TargetableTexture; ERenderTargetLoadAction DepthLoadAction = bPerformClear ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; // 渲染Pass信息. FRHIRenderPassInfo RPInfo(DepthTarget, MakeDepthStencilTargetActions(MakeRenderTargetActions(DepthLoadAction, ERenderTargetStoreAction::EStore), ERenderTargetActions::Load_Store), nullptr, FExclusiveDepthStencil::DepthWrite_StencilWrite); if (!GSupportsDepthRenderTargetWithoutColorRenderTarget) { RPInfo.ColorRenderTargets[0].Action = ERenderTargetActions::DontLoad_DontStore; RPInfo.ColorRenderTargets[0].ArraySlice = -1; RPInfo.ColorRenderTargets[0].MipIndex = 0; RPInfo.ColorRenderTargets[0].RenderTarget = SceneContext.GetOptionalShadowDepthColorSurface(InRHICmdList, DepthTarget->GetTexture2D()->GetSizeX(), DepthTarget->GetTexture2D()->GetSizeY()); InRHICmdList.TransitionResource(EResourceTransitionAccess::EWritable, RPInfo.ColorRenderTargets[0].RenderTarget); } // 转换渲染纹理状态为可写. InRHICmdList.TransitionResource(EResourceTransitionAccess::EWritable, DepthTarget); InRHICmdList.BeginRenderPass(RPInfo, TEXT("ShadowDepthCubeMaps")); }; // 是否需要清理阴影图. { bool bDoClear = true; if (ProjectedShadowInfo->CacheMode == SDCM_MovablePrimitivesOnly && Scene->CachedShadowMaps.FindChecked(ProjectedShadowInfo->GetLightSceneInfo().Id).bCachedShadowMapHasPrimitives) { // Skip the clear when we'll copy from a cached shadowmap bDoClear = false; } BeginShadowRenderPass(RHICmdList, bDoClear); } if (bDoParallelDispatch) { // In parallel mode this first pass will just be the clear. RHICmdList.EndRenderPass(); } // 真正开始渲染阴影图. ProjectedShadowInfo->RenderDepth(RHICmdList, this, BeginShadowRenderPass, bDoParallelDispatch); if (!bDoParallelDispatch) { RHICmdList.EndRenderPass(); } // 转换渲染纹理状态为可读. RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, RenderTarget.TargetableTexture); } // Preshadow缓存. if (SortedShadowsForShadowDepthPass.PreshadowCache.Shadows.Num() > 0) { FSceneRenderTargetItem& RenderTarget = SortedShadowsForShadowDepthPass.PreshadowCache.RenderTargets.DepthTarget->GetRenderTargetItem(); // 遍历所有PreshadowCache的所有阴影实例. for (int32 ShadowIndex = 0; ShadowIndex < SortedShadowsForShadowDepthPass.PreshadowCache.Shadows.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = SortedShadowsForShadowDepthPass.PreshadowCache.Shadows[ShadowIndex]; // 没有被缓存的才需要绘制. if (!ProjectedShadowInfo->bDepthsCached) { const bool bDoParallelDispatch = RHICmdList.IsImmediate() && GRHICommandList.UseParallelAlgorithms() && CVarParallelShadows.GetValueOnRenderThread() && (ProjectedShadowInfo->IsWholeSceneDirectionalShadow() || CVarParallelShadowsNonWholeScene.GetValueOnRenderThread()); ProjectedShadowInfo->SetupShadowUniformBuffers(RHICmdList, Scene); auto BeginShadowRenderPass = [this, ProjectedShadowInfo](FRHICommandList& InRHICmdList, bool bPerformClear) { FRHITexture* PreShadowCacheDepthZ = Scene->PreShadowCacheDepthZ->GetRenderTargetItem().TargetableTexture.GetReference(); InRHICmdList.TransitionResources(EResourceTransitionAccess::EWritable, &PreShadowCacheDepthZ, 1); FRHIRenderPassInfo RPInfo(PreShadowCacheDepthZ, EDepthStencilTargetActions::LoadDepthStencil_StoreDepthStencil, nullptr, FExclusiveDepthStencil::DepthWrite_StencilWrite); // Must preserve existing contents as the clear will be scissored InRHICmdList.BeginRenderPass(RPInfo, TEXT("ShadowDepthMaps")); ProjectedShadowInfo->ClearDepth(InRHICmdList, this, 0, nullptr, PreShadowCacheDepthZ, bPerformClear); }; BeginShadowRenderPass(RHICmdList, true); if (bDoParallelDispatch) { // In parallel mode the first pass is just the clear. RHICmdList.EndRenderPass(); } // 开始绘制. ProjectedShadowInfo->RenderDepth(RHICmdList, this, BeginShadowRenderPass, bDoParallelDispatch); if (!bDoParallelDispatch) { RHICmdList.EndRenderPass(); } // 已经绘制过, 标记已缓存. ProjectedShadowInfo->bDepthsCached = true; } } RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, RenderTarget.TargetableTexture); } // 半透明物体阴影图集. for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.TranslucencyShadowMapAtlases.Num(); AtlasIndex++) { const FSortedShadowMapAtlas& ShadowMapAtlas = SortedShadowsForShadowDepthPass.TranslucencyShadowMapAtlases[AtlasIndex]; FIntPoint TargetSize = ShadowMapAtlas.RenderTargets.ColorTargets[0]->GetDesc().Extent; // 半透明阴影需要用到两张渲染纹理. FSceneRenderTargetItem ColorTarget0 = ShadowMapAtlas.RenderTargets.ColorTargets[0]->GetRenderTargetItem(); FSceneRenderTargetItem ColorTarget1 = ShadowMapAtlas.RenderTargets.ColorTargets[1]->GetRenderTargetItem(); FRHITexture* RenderTargetArray[2] = { ColorTarget0.TargetableTexture, ColorTarget1.TargetableTexture }; FRHIRenderPassInfo RPInfo(UE_ARRAY_COUNT(RenderTargetArray), RenderTargetArray, ERenderTargetActions::Load_Store); TransitionRenderPassTargets(RHICmdList, RPInfo); RHICmdList.BeginRenderPass(RPInfo, TEXT("RenderTranslucencyDepths")); { // 遍历半透明阴影图集的所有阴影实例, 执行半透明阴影深度绘制. for (int32 ShadowIndex = 0; ShadowIndex < ShadowMapAtlas.Shadows.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = ShadowMapAtlas.Shadows[ShadowIndex]; // 渲染半透明阴影深度. ProjectedShadowInfo->RenderTranslucencyDepths(RHICmdList, this); } } RHICmdList.EndRenderPass(); RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, ColorTarget0.TargetableTexture); RHICmdList.TransitionResource(EResourceTransitionAccess::EReadable, ColorTarget1.TargetableTexture); } // 设置LPV的RSM uniform Buffer, 以便后面可以并行提交绘制. { for (int32 ViewIdx = 0; ViewIdx < Views.Num(); ++ViewIdx) { FViewInfo& View = Views[ViewIdx]; FSceneViewState* ViewState = View.ViewState; if (ViewState) { FLightPropagationVolume* Lpv = ViewState->GetLightPropagationVolume(FeatureLevel); if (Lpv) { Lpv->SetRsmUniformBuffer(); } } } } //其他阴影类型 }
void FSceneRenderer::RenderShadowDepthMaps里渲染了csm,半透明,点光源阴影等,我们这里只看csm阴影:
void FSceneRenderer::RenderShadowDepthMapAtlases(FRHICommandListImmediate& RHICmdList) { check(RHICmdList.IsOutsideRenderPass()); // Perform setup work on all GPUs in case any cached shadows are being updated this // frame. We revert to the AllViewsGPUMask for uncached shadows. SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All()); FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); bool bCanUseParallelDispatch = RHICmdList.IsImmediate() && // translucent shadows are draw on the render thread, using a recursive cmdlist (which is not immediate) GRHICommandList.UseParallelAlgorithms() && CVarParallelShadows.GetValueOnRenderThread(); for (int32 AtlasIndex = 0; AtlasIndex < SortedShadowsForShadowDepthPass.ShadowMapAtlases.Num(); AtlasIndex++) { const FSortedShadowMapAtlas& ShadowMapAtlas = SortedShadowsForShadowDepthPass.ShadowMapAtlases[AtlasIndex]; FSceneRenderTargetItem& RenderTarget = ShadowMapAtlas.RenderTargets.DepthTarget->GetRenderTargetItem(); FIntPoint AtlasSize = ShadowMapAtlas.RenderTargets.DepthTarget->GetDesc().Extent; GVisualizeTexture.SetCheckPoint(RHICmdList, ShadowMapAtlas.RenderTargets.DepthTarget.GetReference()); SCOPED_DRAW_EVENTF(RHICmdList, EventShadowDepths, TEXT("Atlas%u %ux%u"), AtlasIndex, AtlasSize.X, AtlasSize.Y); auto BeginShadowRenderPass = [this, &RenderTarget, &SceneContext](FRHICommandList& InRHICmdList, bool bPerformClear) { check(RenderTarget.TargetableTexture->GetDepthClearValue() == 1.0f); ERenderTargetLoadAction DepthLoadAction = bPerformClear ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad; FRHIRenderPassInfo RPInfo(RenderTarget.TargetableTexture, MakeDepthStencilTargetActions(MakeRenderTargetActions(DepthLoadAction, ERenderTargetStoreAction::EStore), ERenderTargetActions::DontLoad_DontStore), nullptr, FExclusiveDepthStencil::DepthWrite_StencilNop); InRHICmdList.Transition(FRHITransitionInfo(RPInfo.DepthStencilRenderTarget.DepthStencilTarget, ERHIAccess::Unknown, ERHIAccess::DSVWrite)); InRHICmdList.BeginRenderPass(RPInfo, TEXT("ShadowMapAtlases")); }; TArray<FProjectedShadowInfo*, SceneRenderingAllocator> ParallelShadowPasses; TArray<FProjectedShadowInfo*, SceneRenderingAllocator> SerialShadowPasses; // Gather our passes here to minimize switching renderpasses for (int32 ShadowIndex = 0; ShadowIndex < ShadowMapAtlas.Shadows.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = ShadowMapAtlas.Shadows[ShadowIndex]; const bool bDoParallelDispatch = bCanUseParallelDispatch && (ProjectedShadowInfo->IsWholeSceneDirectionalShadow() || CVarParallelShadowsNonWholeScene.GetValueOnRenderThread()); if (bDoParallelDispatch) { ParallelShadowPasses.Add(ProjectedShadowInfo); } else { SerialShadowPasses.Add(ProjectedShadowInfo); } } FLightSceneProxy* CurrentLightForDrawEvent = NULL; #if WANTS_DRAW_MESH_EVENTS FDrawEvent LightEvent; #endif if (ParallelShadowPasses.Num() > 0) { { // Clear before going wide. SCOPED_DRAW_EVENT(RHICmdList, SetShadowRTsAndClear); BeginShadowRenderPass(RHICmdList, true); RHICmdList.EndRenderPass(); } for (int32 ShadowIndex = 0; ShadowIndex < ParallelShadowPasses.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = ParallelShadowPasses[ShadowIndex]; SCOPED_GPU_MASK(RHICmdList, GetGPUMaskForShadow(ProjectedShadowInfo)); if (!CurrentLightForDrawEvent || ProjectedShadowInfo->GetLightSceneInfo().Proxy != CurrentLightForDrawEvent) { if (CurrentLightForDrawEvent) { SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All()); STOP_DRAW_EVENT(LightEvent); } CurrentLightForDrawEvent = ProjectedShadowInfo->GetLightSceneInfo().Proxy; FString LightNameWithLevel; GetLightNameForDrawEvent(CurrentLightForDrawEvent, LightNameWithLevel); SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All()); BEGIN_DRAW_EVENTF( RHICmdList, LightNameEvent, LightEvent, *LightNameWithLevel); } ProjectedShadowInfo->SetupShadowUniformBuffers(RHICmdList, Scene); ProjectedShadowInfo->TransitionCachedShadowmap(RHICmdList, Scene); ProjectedShadowInfo->RenderDepth(RHICmdList, this, BeginShadowRenderPass, true); } } if (CurrentLightForDrawEvent) { SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All()); STOP_DRAW_EVENT(LightEvent); } CurrentLightForDrawEvent = nullptr; if (SerialShadowPasses.Num() > 0) { bool bShadowDepthCleared = ParallelShadowPasses.Num() > 0; bool bForceSingleRenderPass = CVarShadowForceSerialSingleRenderPass.GetValueOnAnyThread() != 0; if (bForceSingleRenderPass) { SCOPED_GPU_MASK(RHICmdList, AllViewsGPUMask); BeginShadowRenderPass(RHICmdList, !bShadowDepthCleared); } for (int32 ShadowIndex = 0; ShadowIndex < SerialShadowPasses.Num(); ShadowIndex++) { FProjectedShadowInfo* ProjectedShadowInfo = SerialShadowPasses[ShadowIndex]; SCOPED_GPU_MASK(RHICmdList, GetGPUMaskForShadow(ProjectedShadowInfo)); if (!CurrentLightForDrawEvent || ProjectedShadowInfo->GetLightSceneInfo().Proxy != CurrentLightForDrawEvent) { if (CurrentLightForDrawEvent) { SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All()); STOP_DRAW_EVENT(LightEvent); } CurrentLightForDrawEvent = ProjectedShadowInfo->GetLightSceneInfo().Proxy; FString LightNameWithLevel; GetLightNameForDrawEvent(CurrentLightForDrawEvent, LightNameWithLevel); SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All()); BEGIN_DRAW_EVENTF( RHICmdList, LightNameEvent, LightEvent, *LightNameWithLevel); } ProjectedShadowInfo->SetupShadowUniformBuffers(RHICmdList, Scene); ProjectedShadowInfo->TransitionCachedShadowmap(RHICmdList, Scene); #if WITH_MGPU // In case the first shadow is view-dependent, ensure we do the clear on all GPUs. FRHIGPUMask GPUMaskForRenderPass = RHICmdList.GetGPUMask(); if (ShadowIndex == 0) { // This ensures that we don't downgrade the GPU mask if the first shadow is a // cached whole scene shadow. GPUMaskForRenderPass |= AllViewsGPUMask; } #endif if (!bForceSingleRenderPass) { SCOPED_GPU_MASK(RHICmdList, GPUMaskForRenderPass); BeginShadowRenderPass(RHICmdList, ShadowIndex == 0 && !bShadowDepthCleared); } ProjectedShadowInfo->RenderDepth(RHICmdList, this, BeginShadowRenderPass, false); if (!bForceSingleRenderPass) { RHICmdList.EndRenderPass(); } } if (bForceSingleRenderPass) { SCOPED_GPU_MASK(RHICmdList, AllViewsGPUMask); RHICmdList.EndRenderPass(); } } if (CurrentLightForDrawEvent) { SCOPED_GPU_MASK(RHICmdList, FRHIGPUMask::All()); STOP_DRAW_EVENT(LightEvent); CurrentLightForDrawEvent = NULL; } RHICmdList.Transition(FRHITransitionInfo(RenderTarget.TargetableTexture, ERHIAccess::Unknown, ERHIAccess::SRVMask)); } }
SortedShadowsForShadowDepthPass.ShadowMapAtlases是在void FSceneRenderer::AllocateMobileCSMAndSpotLightShadowDepthTargets函数里分配的。
渲染的核心代码是ProjectedShadowInfo->RenderDepth(RHICmdList, this, BeginShadowRenderPass, false);
void FProjectedShadowInfo::RenderDepth(FRHICommandListImmediate& RHICmdList, FSceneRenderer* SceneRenderer, FBeginShadowRenderPassFunction BeginShadowRenderPass, bool bDoParallelDispatch) { RenderDepthInner(RHICmdList, SceneRenderer, BeginShadowRenderPass, bDoParallelDispatch); } void FProjectedShadowInfo::RenderDepthInner(FRHICommandListImmediate& RHICmdList, FSceneRenderer* SceneRenderer, FBeginShadowRenderPassFunction BeginShadowRenderPass, bool bDoParallelDispatch) { const ERHIFeatureLevel::Type FeatureLevel = ShadowDepthView->FeatureLevel; FRHIUniformBuffer* PassUniformBuffer = ShadowDepthPassUniformBuffer; const bool bIsWholeSceneDirectionalShadow = IsWholeSceneDirectionalShadow(); // 平行光全景阴影的Uniform Buffer更新. if (bIsWholeSceneDirectionalShadow) { // 利用缓存的Uniform Buffer更新阴影视图的Uniform Buffer. ShadowDepthView->ViewUniformBuffer.UpdateUniformBufferImmediate(*ShadowDepthView->CachedViewUniformShaderParameters); // 如果存在依赖视图, 遍历所有持续存在的视图UB扩展, 执行开始渲染命令. if (DependentView) { extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions; for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions) { Extension->BeginRenderView(DependentView); } } } // 移动端平台的绘制的Uniform Buffer更新. if (FSceneInterface::GetShadingPath(FeatureLevel) == EShadingPath::Mobile) { FMobileShadowDepthPassUniformParameters ShadowDepthPassParameters; SetupShadowDepthPassUniformBuffer(this, RHICmdList, *ShadowDepthView, ShadowDepthPassParameters); SceneRenderer->Scene->UniformBuffers.MobileCSMShadowDepthPassUniformBuffer.UpdateUniformBufferImmediate(ShadowDepthPassParameters); MobileShadowDepthPassUniformBuffer.UpdateUniformBufferImmediate(ShadowDepthPassParameters); PassUniformBuffer = SceneRenderer->Scene->UniformBuffers.MobileCSMShadowDepthPassUniformBuffer; } // 设置网格Pass的渲染状态. FMeshPassProcessorRenderState DrawRenderState(*ShadowDepthView, PassUniformBuffer); SetStateForShadowDepth(bReflectiveShadowmap, bOnePassPointLightShadow, DrawRenderState); SetStateForView(RHICmdList); if (CacheMode == SDCM_MovablePrimitivesOnly) { if (bDoParallelDispatch) { BeginShadowRenderPass(RHICmdList, false); } // 在渲染可移动图元的深度之前拷贝静态图元的深度. CopyCachedShadowMap(RHICmdList, DrawRenderState, SceneRenderer, *ShadowDepthView); if (bDoParallelDispatch) { RHICmdList.EndRenderPass(); } } // 并行绘制 if (bDoParallelDispatch) { bool bFlush = CVarRHICmdFlushRenderThreadTasksShadowPass.GetValueOnRenderThread() > 0 || CVarRHICmdFlushRenderThreadTasks.GetValueOnRenderThread() > 0; FScopedCommandListWaitForTasks Flusher(bFlush); // 发送渲染命令. { FShadowParallelCommandListSet ParallelCommandListSet(*ShadowDepthView, SceneRenderer, RHICmdList, CVarRHICmdShadowDeferredContexts.GetValueOnRenderThread() > 0, !bFlush, DrawRenderState, *this, BeginShadowRenderPass); ShadowDepthPass.DispatchDraw(&ParallelCommandListSet, RHICmdList); } } // 非并行绘制 else { ShadowDepthPass.DispatchDraw(nullptr, RHICmdList); } }
ShadowDepthPass是在哪里赋值的?
void FProjectedShadowInfo::GatherDynamicMeshElements(FSceneRenderer& Renderer, FVisibleLightInfo& VisibleLightInfo, TArray<const FSceneView*>& ReusedViewsArray, FGlobalDynamicIndexBuffer& DynamicIndexBuffer, FGlobalDynamicVertexBuffer& DynamicVertexBuffer, FGlobalDynamicReadBuffer& DynamicReadBuffer) { QUICK_SCOPE_CYCLE_COUNTER(STAT_Shadow_GatherDynamicMeshElements); check(ShadowDepthView && IsInRenderingThread()); if (DynamicSubjectPrimitives.Num() > 0 || ReceiverPrimitives.Num() > 0 || SubjectTranslucentPrimitives.Num() > 0) { // Backup properties of the view that we will override FMatrix OriginalViewMatrix = ShadowDepthView->ViewMatrices.GetViewMatrix(); // Override the view matrix so that billboarding primitives will be aligned to the light ShadowDepthView->ViewMatrices.HackOverrideViewMatrixForShadows(ShadowViewMatrix); ReusedViewsArray[0] = ShadowDepthView; if (bPreShadow && GPreshadowsForceLowestLOD) { ShadowDepthView->DrawDynamicFlags = EDrawDynamicFlags::ForceLowestLOD; } if (CascadeSettings.bFarShadowCascade) { (int32&)ShadowDepthView->DrawDynamicFlags |= (int32)EDrawDynamicFlags::FarShadowCascade; } if (IsWholeSceneDirectionalShadow()) { ShadowDepthView->SetPreShadowTranslation(FVector(0, 0, 0)); ShadowDepthView->SetDynamicMeshElementsShadowCullFrustum(&CascadeSettings.ShadowBoundsAccurate); GatherDynamicMeshElementsArray(ShadowDepthView, Renderer, DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer, DynamicSubjectPrimitives, ReusedViewsArray, DynamicSubjectMeshElements, NumDynamicSubjectMeshElements); ShadowDepthView->SetPreShadowTranslation(PreShadowTranslation); } else { ShadowDepthView->SetPreShadowTranslation(PreShadowTranslation); ShadowDepthView->SetDynamicMeshElementsShadowCullFrustum(&CasterFrustum); GatherDynamicMeshElementsArray(ShadowDepthView, Renderer, DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer, DynamicSubjectPrimitives, ReusedViewsArray, DynamicSubjectMeshElements, NumDynamicSubjectMeshElements); } ShadowDepthView->DrawDynamicFlags = EDrawDynamicFlags::None; int32 NumDynamicSubjectTranslucentMeshElements = 0; ShadowDepthView->SetDynamicMeshElementsShadowCullFrustum(&CasterFrustum); GatherDynamicMeshElementsArray(ShadowDepthView, Renderer, DynamicIndexBuffer, DynamicVertexBuffer, DynamicReadBuffer, SubjectTranslucentPrimitives, ReusedViewsArray, DynamicSubjectTranslucentMeshElements, NumDynamicSubjectTranslucentMeshElements); Renderer.MeshCollector.ProcessTasks(); } // Create a pass uniform buffer so we can build mesh commands now in InitDynamicShadows. This will be updated with the correct contents just before the actual pass. const EShadingPath ShadingPath = FSceneInterface::GetShadingPath(Renderer.FeatureLevel); FRHIUniformBuffer* PassUniformBuffer = nullptr; if (ShadingPath == EShadingPath::Deferred) { FShadowDepthPassUniformParameters ShadowDepthParameters; ShadowDepthPassUniformBuffer = TUniformBufferRef<FShadowDepthPassUniformParameters>::CreateUniformBufferImmediate(ShadowDepthParameters, UniformBuffer_MultiFrame, EUniformBufferValidation::None); PassUniformBuffer = ShadowDepthPassUniformBuffer; } else if (ShadingPath == EShadingPath::Mobile) { FMobileShadowDepthPassUniformParameters ShadowDepthParameters; MobileShadowDepthPassUniformBuffer = TUniformBufferRef<FMobileShadowDepthPassUniformParameters>::CreateUniformBufferImmediate(ShadowDepthParameters, UniformBuffer_MultiFrame, EUniformBufferValidation::None); PassUniformBuffer = MobileShadowDepthPassUniformBuffer; } SetupMeshDrawCommandsForShadowDepth(Renderer, PassUniformBuffer); SetupMeshDrawCommandsForProjectionStenciling(Renderer); }
里面收集了需要渲染的DynamicMeshelement,uniformBuffer并且设置阴影用的meshDrawCommand:
SetupMeshDrawCommandsForShadowDepth(Renderer, PassUniformBuffer);
void FProjectedShadowInfo::SetupMeshDrawCommandsForShadowDepth(FSceneRenderer& Renderer, FRHIUniformBuffer* PassUniformBuffer) { QUICK_SCOPE_CYCLE_COUNTER(STAT_SetupMeshDrawCommandsForShadowDepth); FShadowDepthPassMeshProcessor* MeshPassProcessor = new(FMemStack::Get()) FShadowDepthPassMeshProcessor( Renderer.Scene, ShadowDepthView, ShadowDepthView->ViewUniformBuffer, PassUniformBuffer, GetShadowDepthType(), nullptr); if (Renderer.ShouldDumpMeshDrawCommandInstancingStats()) { FString PassNameForStats; GetShadowTypeNameForDrawEvent(PassNameForStats); ShadowDepthPass.SetDumpInstancingStats(TEXT("ShadowDepth ") + PassNameForStats); } extern int32 GShadowUseGS; const uint32 InstanceFactor = !GetShadowDepthType().bOnePassPointLightShadow || (GShadowUseGS && RHISupportsGeometryShaders(Renderer.Scene->GetShaderPlatform())) ? 1 : 6; ShadowDepthPass.DispatchPassSetup( Renderer.Scene, *ShadowDepthView, EMeshPass::Num, FExclusiveDepthStencil::DepthNop_StencilNop, MeshPassProcessor, DynamicSubjectMeshElements, nullptr, NumDynamicSubjectMeshElements * InstanceFactor, SubjectMeshCommandBuildRequests, NumSubjectMeshCommandBuildRequestElements * InstanceFactor, ShadowDepthPassVisibleCommands); Renderer.DispatchedShadowDepthPasses.Add(&ShadowDepthPass); }
shadowDepthType
FShadowDepthType GetShadowDepthType() const { return FShadowDepthType(bDirectionalLight, bOnePassPointLightShadow, bReflectiveShadowmap); }
3. 阴影的应用
void SetupMobileDirectionalLightUniformParameters( const FScene& Scene, const FViewInfo& SceneView, const TArray<FVisibleLightInfo,SceneRenderingAllocator> VisibleLightInfos, int32 ChannelIdx, bool bDynamicShadows, FMobileDirectionalLightShaderParameters& Params) { ERHIFeatureLevel::Type FeatureLevel = Scene.GetFeatureLevel(); FLightSceneInfo* Light = Scene.MobileDirectionalLights[ChannelIdx]; if (Light) { Params.DirectionalLightColor = Light->Proxy->GetColor() / PI; if (Light->Proxy->IsUsedAsAtmosphereSunLight()) { Params.DirectionalLightColor *= Light->Proxy->GetTransmittanceFactor(); } Params.DirectionalLightDirectionAndShadowTransition = FVector4(-Light->Proxy->GetDirection(), 0.f); const FVector2D FadeParams = Light->Proxy->GetDirectionalLightDistanceFadeParameters(FeatureLevel, Light->IsPrecomputedLightingValid(), SceneView.MaxShadowCascades); Params.DirectionalLightDistanceFadeMADAndSpecularScale.X = FadeParams.Y; Params.DirectionalLightDistanceFadeMADAndSpecularScale.Y = -FadeParams.X * FadeParams.Y; Params.DirectionalLightDistanceFadeMADAndSpecularScale.Z = Light->Proxy->GetSpecularScale(); if (bDynamicShadows && VisibleLightInfos.IsValidIndex(Light->Id) && VisibleLightInfos[Light->Id].AllProjectedShadows.Num() > 0) { const TArray<FProjectedShadowInfo*, SceneRenderingAllocator>& DirectionalLightShadowInfos = VisibleLightInfos[Light->Id].AllProjectedShadows; static_assert(MAX_MOBILE_SHADOWCASCADES <= 4, "more than 4 cascades not supported by the shader and uniform buffer"); const int32 NumShadowsToCopy = FMath::Min(DirectionalLightShadowInfos.Num(), MAX_MOBILE_SHADOWCASCADES); int32_t OutShadowIndex = 0; for (int32 i = 0; i < NumShadowsToCopy; ++i) { const FProjectedShadowInfo* ShadowInfo = DirectionalLightShadowInfos[i]; if (ShadowInfo->ShadowDepthView && !ShadowInfo->bRayTracedDistanceField) { if (OutShadowIndex == 0) { const FIntPoint ShadowBufferResolution = ShadowInfo->GetShadowBufferResolution(); const FVector4 ShadowBufferSizeValue((float)ShadowBufferResolution.X, (float)ShadowBufferResolution.Y, 1.0f / (float)ShadowBufferResolution.X, 1.0f / (float)ShadowBufferResolution.Y); Params.DirectionalLightShadowTexture = ShadowInfo->RenderTargets.DepthTarget->GetRenderTargetItem().ShaderResourceTexture.GetReference(); Params.DirectionalLightDirectionAndShadowTransition.W = 1.0f / ShadowInfo->ComputeTransitionSize(); Params.DirectionalLightShadowSize = ShadowBufferSizeValue; } Params.DirectionalLightScreenToShadow[OutShadowIndex] = ShadowInfo->GetScreenToShadowMatrix(SceneView); Params.DirectionalLightShadowDistances[OutShadowIndex] = ShadowInfo->CascadeSettings.SplitFar; OutShadowIndex++; } } } } if (SceneView.MobileMovableSpotLightsShadowInfo.ShadowDepthTexture != nullptr) { checkSlow(Params.DirectionalLightShadowTexture == SceneView.MobileMovableSpotLightsShadowInfo.ShadowDepthTexture || Params.DirectionalLightShadowTexture == GWhiteTexture->TextureRHI); Params.DirectionalLightShadowSize = SceneView.MobileMovableSpotLightsShadowInfo.ShadowBufferSize; Params.DirectionalLightShadowTexture = SceneView.MobileMovableSpotLightsShadowInfo.ShadowDepthTexture; } }
被调用有两处:
1. void FMobileSceneRenderer::InitViews(FRHICommandListImmediate& RHICmdList)里的CreateDirectionalLightUniformBuffers:
void FMobileSceneRenderer::CreateDirectionalLightUniformBuffers(FViewInfo& View) { bool bDynamicShadows = ViewFamily.EngineShowFlags.DynamicShadows; // First array entry is used for primitives with no lighting channel set View.MobileDirectionalLightUniformBuffers[0] = TUniformBufferRef<FMobileDirectionalLightShaderParameters>::CreateUniformBufferImmediate(FMobileDirectionalLightShaderParameters(), UniformBuffer_SingleFrame); // Fill in the other entries based on the lights for (int32 ChannelIdx = 0; ChannelIdx < UE_ARRAY_COUNT(Scene->MobileDirectionalLights); ChannelIdx++) { FMobileDirectionalLightShaderParameters Params; SetupMobileDirectionalLightUniformParameters(*Scene, View, VisibleLightInfos, ChannelIdx, bDynamicShadows, Params); View.MobileDirectionalLightUniformBuffers[ChannelIdx + 1] = TUniformBufferRef<FMobileDirectionalLightShaderParameters>::CreateUniformBufferImmediate(Params, UniformBuffer_SingleFrame); } }
2. FMobileSceneRenderer::RenderMobileBasePass调用了,UpdateDirectionalLightUniformBuffers, 调用了SetupMobileDirectionalLightUniformParameters:
void FMobileSceneRenderer::UpdateDirectionalLightUniformBuffers(FRHICommandListImmediate& RHICmdList, const FViewInfo& View)
void FMobileSceneRenderer::UpdateDirectionalLightUniformBuffers(FRHICommandListImmediate& RHICmdList, const FViewInfo& View) { bool bDynamicShadows = ViewFamily.EngineShowFlags.DynamicShadows; // Fill in the other entries based on the lights for (int32 ChannelIdx = 0; ChannelIdx < UE_ARRAY_COUNT(Scene->MobileDirectionalLights); ChannelIdx++) { FMobileDirectionalLightShaderParameters Params; SetupMobileDirectionalLightUniformParameters(*Scene, View, VisibleLightInfos, ChannelIdx, bDynamicShadows, Params); Scene->UniformBuffers.MobileDirectionalLightUniformBuffers[ChannelIdx + 1].UpdateUniformBufferImmediate(Params); } }
被调用:
void FMobileSceneRenderer::RenderMobileBasePass(FRHICommandListImmediate& RHICmdList, const TArrayView<const FViewInfo*> PassViews) { CSV_SCOPED_TIMING_STAT_EXCLUSIVE(RenderBasePass); SCOPED_DRAW_EVENT(RHICmdList, MobileBasePass); SCOPE_CYCLE_COUNTER(STAT_BasePassDrawTime); SCOPED_GPU_STAT(RHICmdList, Basepass); for (int32 ViewIndex = 0; ViewIndex < PassViews.Num(); ViewIndex++) { SCOPED_CONDITIONAL_DRAW_EVENTF(RHICmdList, EventView, Views.Num() > 1, TEXT("View%d"), ViewIndex); const FViewInfo& View = *PassViews[ViewIndex]; if (!View.ShouldRenderView()) { continue; } if (Scene->UniformBuffers.UpdateViewUniformBuffer(View)) { UpdateOpaqueBasePassUniformBuffer(RHICmdList, View); UpdateDirectionalLightUniformBuffers(RHICmdList, View); }
mobileBasepassPixelShader.usf文件里,使用:
#if DIRECTIONAL_LIGHT_CSM && !MATERIAL_SHADINGMODEL_SINGLELAYERWATER // Cascaded Shadow Map if (UseCSM()) { half ShadowMap = MobileDirectionalLightCSM(MaterialParameters.ScreenPosition.xy, MaterialParameters.ScreenPosition.w, ShadowPositionZ); #if ALLOW_STATIC_LIGHTING Shadow = min(ShadowMap, Shadow); #else Shadow = ShadowMap; #endif } #endif /* DIRECTIONAL_LIGHT_CSM */
half MobileDirectionalLightCSM(float2 ScreenPosition, float SceneDepth, out float ShadowPositionZ) { half ShadowMap = 1; ShadowPositionZ = 0; FPCFSamplerSettings Settings; Settings.ShadowDepthTexture = MobileDirectionalLight.DirectionalLightShadowTexture; Settings.ShadowDepthTextureSampler = MobileDirectionalLight.DirectionalLightShadowSampler; Settings.TransitionScale = MobileDirectionalLight.DirectionalLightDirectionAndShadowTransition.w; Settings.ShadowBufferSize = MobileDirectionalLight.DirectionalLightShadowSize; Settings.bSubsurface = false; Settings.bTreatMaxDepthUnshadowed = false; Settings.DensityMulConstant = 0; Settings.ProjectionDepthBiasParameters = 0; float4 ShadowPosition = float4(0, 0, 0, 0); for (int i = 0; i < MAX_MOBILE_SHADOWCASCADES; i++) { if (SceneDepth < MobileDirectionalLight.DirectionalLightShadowDistances[i]) { #if MOBILE_MULTI_VIEW ShadowPosition = mul(float4(ScreenPosition.x, ScreenPosition.y, SceneDepth, 1), ResolvedView.MobileMultiviewShadowTransform); ShadowPosition = mul(ShadowPosition, MobileDirectionalLight.DirectionalLightScreenToShadow[i]); #else ShadowPosition = mul(float4(ScreenPosition.x, ScreenPosition.y, SceneDepth, 1), MobileDirectionalLight.DirectionalLightScreenToShadow[i]); #endif ShadowPositionZ = ShadowPosition.z; break; // position found. } }