--- title: UE的透明渲染功能笔记 date: 2022-10-30 21:36:08 excerpt: tags: Rendering rating: ⭐ --- 其主函数为FDeferredShadingSceneRenderer::RenderTranslucency(),位于FXSystem与头发合成之后,渲染Distortion、速度之前。 ```c++ // Draw translucency. if (bCanOverlayRayTracingOutput && TranslucencyViewsToRender != ETranslucencyView::None) { RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderTranslucency); SCOPE_CYCLE_COUNTER(STAT_TranslucencyDrawTime); // Raytracing doesn't need the distortion effect. const bool bShouldRenderDistortion = TranslucencyViewsToRender != ETranslucencyView::RayTracing; #if RHI_RAYTRACING if (EnumHasAnyFlags(TranslucencyViewsToRender, ETranslucencyView::RayTracing)) { RenderRayTracingTranslucency(GraphBuilder, SceneColorTexture); EnumRemoveFlags(TranslucencyViewsToRender, ETranslucencyView::RayTracing); }#endif // Render all remaining translucency views. AddSetCurrentStatPass(GraphBuilder, GET_STATID(STAT_CLM_Translucency)); RenderTranslucency(GraphBuilder, SceneColorTexture, SceneDepthTexture, HairDatas, &SeparateTranslucencyTextures, TranslucencyViewsToRender); AddServiceLocalQueuePass(GraphBuilder); TranslucencyViewsToRender = ETranslucencyView::None; ... } ``` ## Pass Event String static const TCHAR* TranslucencyPassToString(ETranslucencyPass::Type TranslucencyPass) { switch (TranslucencyPass) { case ETranslucencyPass::TPT_StandardTranslucency: return TEXT("Standard"); case ETranslucencyPass::TPT_TranslucencyAfterDOF: return TEXT("AfterDOF"); case ETranslucencyPass::TPT_TranslucencyAfterDOFModulate: return TEXT("AfterDOFModulate"); case ETranslucencyPass::TPT_AllTranslucency: return TEXT("All"); } checkNoEntry(); return TEXT(""); } ## 半透明渲染分为可分离与单Pass渲染。 - 可分离渲染调用RenderViewTranslucencyInner()渲染内部,之后调用AddEndSeparateTranslucencyTimerPass()完成渲染。 - 单Pass渲染完所有物体。调用RenderViewTranslucencyInner()渲染内部,之后调用AddEndSeparateTranslucencyTimerPass()完成渲染。 Shader变量使用FTranslucentBasePassParameters。 MeshDrawPass名为。 ```c++ EMeshPass::TranslucencyStandard EMeshPass::TranslucencyAfterDOF EMeshPass::TranslucencyAfterDOFModulate EMeshPass::TranslucencyAfterMotionBlur EMeshPass::TranslucencyAll ``` 最后在AddPass()调用RenderViewTranslucencyInner()进行实际MeshDraw渲染: ```c++ if (bRenderInParallel) { GraphBuilder.AddPass( RDG_EVENT_NAME("Translucency(%s Parallel) %dx%d", TranslucencyPassToString(TranslucencyPass), int32(View.ViewRect.Width() * ViewportScale), int32(View.ViewRect.Height() * ViewportScale)), PassParameters, ERDGPassFlags::Raster | ERDGPassFlags::SkipRenderPass, [&SceneRenderer, &View, PassParameters, ViewportScale, Viewport, TranslucencyPass](FRHICommandListImmediate& RHICmdList) { FRDGParallelCommandListSet ParallelCommandListSet(RHICmdList, GET_STATID(STAT_CLP_Translucency), SceneRenderer, View, FParallelCommandListBindings(PassParameters), ViewportScale); RenderViewTranslucencyInner(RHICmdList, SceneRenderer, View, Viewport, ViewportScale, TranslucencyPass, &ParallelCommandListSet, PassParameters->InstanceCullingDrawParams); }); } else { GraphBuilder.AddPass( RDG_EVENT_NAME("Translucency(%s) %dx%d", TranslucencyPassToString(TranslucencyPass), int32(View.ViewRect.Width() * ViewportScale), int32(View.ViewRect.Height() * ViewportScale)), PassParameters, ERDGPassFlags::Raster, [&SceneRenderer, &View, ViewportScale, Viewport, TranslucencyPass, PassParameters](FRHICommandListImmediate& RHICmdList) { RenderViewTranslucencyInner(RHICmdList, SceneRenderer, View, Viewport, ViewportScale, TranslucencyPass, nullptr, PassParameters->InstanceCullingDrawParams); }); } ``` 最后使用FBasePassMeshProcessor。 ```c++ DrawDynamicMeshPass(View, RHICmdList, [&View, &DrawRenderState, TranslucencyPass](FDynamicPassMeshDrawListContext* DynamicMeshPassContext) { FBasePassMeshProcessor PassMeshProcessor( View.Family->Scene->GetRenderScene(), View.GetFeatureLevel(), &View, DrawRenderState, DynamicMeshPassContext, FBasePassMeshProcessor::EFlags::CanUseDepthStencil, TranslucencyPass); const uint64 DefaultBatchElementMask = ~0ull; for (int32 MeshIndex = 0; MeshIndex < View.ViewMeshElements.Num(); MeshIndex++) { const FMeshBatch& MeshBatch = View.ViewMeshElements[MeshIndex]; PassMeshProcessor.AddMeshBatch(MeshBatch, DefaultBatchElementMask, nullptr); }}); ``` ## 透明物体判断 IsTranslucentBlendMode()位于MaterialShared.h: ```c++ inline bool IsTranslucentBlendMode(enum EBlendMode BlendMode) { return BlendMode != BLEND_Opaque && BlendMode != BLEND_Masked; } ``` 位于`bool FBasePassMeshProcessor::ShouldDraw(const FMaterial& Material)` ```c++ const bool bIsTranslucent = IsTranslucentBlendMode(BlendMode); if (bTranslucentBasePass) { if (bIsTranslucent && !Material.IsDeferredDecal()) { switch (TranslucencyPassType) { case ETranslucencyPass::TPT_StandardTranslucency: bShouldDraw = !Material.IsTranslucencyAfterDOFEnabled(); break; case ETranslucencyPass::TPT_TranslucencyAfterDOF: bShouldDraw = Material.IsTranslucencyAfterDOFEnabled(); break; // only dual blended or modulate surfaces need background modulation case ETranslucencyPass::TPT_TranslucencyAfterDOFModulate: bShouldDraw = Material.IsTranslucencyAfterDOFEnabled() && (Material.IsDualBlendingEnabled(GetFeatureLevelShaderPlatform(FeatureLevel)) || BlendMode == BLEND_Modulate); break; case ETranslucencyPass::TPT_AllTranslucency: bShouldDraw = true; break; } } } ``` ## 为什么MultiDraw无法同时渲染透明与不透明材质 首先透明会使用FBasePassMeshProcessor进行渲染(不同BasePass会有不同的设置),针对图元创建MeshProcessor时是以每个图元为单位进行的。 ```c++ void FMaterialRenderProxy::UpdateUniformExpressionCacheIfNeeded(ERHIFeatureLevel::Type InFeatureLevel) const { if (!UniformExpressionCache[InFeatureLevel].bUpToDate) { // Don't cache uniform expressions if an entirely different FMaterialRenderProxy is going to be used for rendering const FMaterial* Material = GetMaterialNoFallback(InFeatureLevel); if (Material) { FMaterialRenderContext MaterialRenderContext(this, *Material, nullptr); MaterialRenderContext.bShowSelection = GIsEditor; EvaluateUniformExpressions(UniformExpressionCache[InFeatureLevel], MaterialRenderContext); } } } ```