BlueRoseNote/03-UnrealEngine/Rendering/Shader/UE的透明渲染功能笔记.md
2023-06-29 11:55:02 +08:00

6.8 KiB
Raw Permalink Blame History

title, date, excerpt, tags, rating
title date excerpt tags rating
UE的透明渲染功能笔记 2022-10-30 21:36:08 Rendering

其主函数为FDeferredShadingSceneRenderer::RenderTranslucency()位于FXSystem与头发合成之后渲染Distortion、速度之前。

// 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名为。

EMeshPass::TranslucencyStandard
EMeshPass::TranslucencyAfterDOF
EMeshPass::TranslucencyAfterDOFModulate
EMeshPass::TranslucencyAfterMotionBlur
EMeshPass::TranslucencyAll

最后在AddPass()调用RenderViewTranslucencyInner()进行实际MeshDraw渲染

	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。

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

inline bool IsTranslucentBlendMode(enum EBlendMode BlendMode)  
{  
   return BlendMode != BLEND_Opaque && BlendMode != BLEND_Masked;  
}

位于bool FBasePassMeshProcessor::ShouldDraw(const FMaterial& Material)

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时是以每个图元为单位进行的。

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);  
      }   
	}
}