UE源码阅读[1]---由问题入手UE中的延迟渲染

目录

延迟渲染:

问题:

UE渲染部分的起点:

怎么样处理多光源

Gbuffer Layout:

RenderBasePass():

GetGBufferRenderTargets()

BasePassPixelShader.usf

GBuffer布局:

处理同屏不同的光照模型:

基本思路:

ShaderPermutation:

FDeferredLightPS:


延迟渲染:

问题:

1.UE 延迟渲染的Gbuffer是什么样的?

2.怎么样处理多光源

3.怎么样处理同屏不同的光照模型

UE渲染部分的起点:

UE的渲染部分从GameEngine.h 和 GameEngine.cpp

voidUGameEngine::Tick( float DeltaSeconds, bool bIdleMode )
{
[... ...]
//检查设置,比如是否是专属服务器,只有命令行
	if (!bIdleMode && !IsRunningDedicatedServer() && !IsRunningCommandlet() && FEmbeddedCommunication::IsAwakeForRendering())
	{
		//渲染窗口/所有物体
		RedrawViewports();
		// 渲染完之后的回调
		GetRendererModule().PostRenderAllViewports();
	}
[... ...]
}

//上述的RedrawViewports();
void UGameEngine::RedrawViewports( bool bShouldPresent /*= true*/ )
{
	SCOPE_CYCLE_COUNTER(STAT_RedrawViewports);
	CSV_SCOPED_TIMING_STAT_EXCLUSIVE(ViewportMisc);
	if ( GameViewport != NULL )
	{
		GameViewport->LayoutPlayers();
		if ( GameViewport->Viewport != NULL )
		{
			GameViewport->Viewport->Draw(bShouldPresent);
		}
	}
}

//上述的GameViewport->Viewport->Draw(bShouldPresent);
void UGameViewportClient::Draw() 
{
[... ...]
	if (!bDisableWorldRendering && PlayerViewMap.Num() > 0 && FSlateApplication::Get().GetPlatformApplication()->IsAllowedToRender()) //-V560
	{
//渲染玩家视窗,从游戏线程调用发送一个消息到渲染线程正在渲染这个视图家族。
		GetRendererModule().BeginRenderingViewFamily(SceneCanvas,&ViewFamily);
	}
[... ...]
}
void FRendererModule::BeginRenderingViewFamily(FCanvas* Canvas, FSceneViewFamily* ViewFamily)
{
[... ...]
		// 创建场景渲染器,FSceneRenderer有两个子类,分别是FMobileSceneRenderer和FDeferredShadingSceneRenderer
		FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(ViewFamily, Canvas->GetHitProxyConsumer());
// 向渲染线程发送绘制场景指令.
		ENQUEUE_RENDER_COMMAND(FDrawSceneCommand)(
			[SceneRenderer, DrawSceneEnqueue](FRHICommandListImmediate& RHICmdList)
			{
				const float StartDelayMillisec = FPlatformTime::ToMilliseconds(FPlatformTime::Cycles() - DrawSceneEnqueue);
				CSV_CUSTOM_STAT_GLOBAL(DrawSceneCommand_StartDelay, StartDelayMillisec, ECsvCustomStatOp::Set);
[... ...]
//渲染线程实际干的活
				RenderViewFamily_RenderThread(RHICmdList, SceneRenderer);
				FlushPendingDeleteRHIResources_RenderThread();
			});
[... ...]
}

//RenderViewFamily_RenderThread(RHICmdList, SceneRenderer);
static void RenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneRenderer* SceneRenderer)
{
[... ...]
//更新任何需要延迟更新的资源
FDeferredUpdateResource::UpdateResources(RHICmdList);
[... ...]
//渲染场景
	if(SceneRenderer->ViewFamily.EngineShowFlags.HitProxies)
		{
			// Render the scene's hit proxies.
			SceneRenderer->RenderHitProxies(RHICmdList);
		}
		else
		{
			// Render the scene.
			SceneRenderer->Render(RHICmdList);
		}
[... ...]
}

因为要看延迟渲染,所以接下来就直接看FDeferredShadingSceneRenderer.h和FDeferredShadingSceneRenderer.cpp

FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)这个函数一千三百多行,大概包含了准备大气散射的数据,Prepass 渲染深度, 光追,Base pass 延迟渲染的几何阶段,用于遮挡剔除的简化包围盒深度值渲染,渲染阴影和深度,渲染motion vector,渲染头发,渲染光照,渲染大气,渲染大气散射,渲染雾,渲染体积云,通知特效系统,渲染半透明物体,后处理

怎么样处理多光源

目前不会讲述关于cluster shading的处理

 FDeferredShadingSceneRenderer::Render(FRHICommandListImmediate& RHICmdList)
{
[... ...]
	bool bDoInitViewAftersPrepass = false;
	{
		SCOPED_GPU_STAT(RHICmdList, VisibilityCommands);
		bDoInitViewAftersPrepass = InitViews(RHICmdList, BasePassDepthStencilAccess, ILCTaskData)

	}
[... ...]
}

bool FDeferredShadingSceneRenderer::InitViews(FRHICommandListImmediate& RHICmdList, FExclusiveDepthStencil::Type BasePassDepthStencilAccess, struct FILCUpdatePrimTaskData& ILCTaskData)
{
[... ...]

//平头体裁剪,包围盒计算,遮挡剔除,收集网格信息,创建光源信息
	ComputeViewVisibility(RHICmdList, BasePassDepthStencilAccess, ViewCommandsPerView, DynamicIndexBufferForInitViews, DynamicVertexBufferForInitViews, DynamicReadBufferForInitViews);
[... ...]

//处理贴花排序,处理光源可见性
	PostVisibilityFrameSetup(ILCTaskData);

[... ...]
}

void FSceneRenderer::PostVisibilityFrameSetup(FILCUpdatePrimTaskData& OutILCTaskData)
{
[... ...]
	// 确定光源的可见性
	for(TSparseArray::TConstIterator LightIt(Scene->Lights);LightIt;++LightIt)
	{
		const FLightSceneInfoCompact& LightSceneInfoCompact = *LightIt;
		const FLightSceneInfo* LightSceneInfo = LightSceneInfoCompact.LightSceneInfo;

		// 利用摄像机的视锥体裁剪光源
		for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
		{		
			const FLightSceneProxy* Proxy = LightSceneInfo->Proxy;
			FViewInfo& View = Views[ViewIndex];
			FVisibleLightViewInfo& VisibleLightViewInfo = View.VisibleLightInfos[LightIt.GetIndex()];
			// 直接光永远可见,点光/聚光灯/面光需要判断
			if( Proxy->GetLightType() == LightType_Point ||
				Proxy->GetLightType() == LightType_Spot ||
				Proxy->GetLightType() == LightType_Rect )
			{
				FSphere const& BoundingSphere = Proxy->GetBoundingSphere();
				if (View.ViewFrustum.IntersectSphere(BoundingSphere.Center, BoundingSphere.W))
				{
//透视相机需要提出太远的光源
					if (View.IsPerspectiveProjection())
					{
						FSphere Bounds = Proxy->GetBoundingSphere();
						float DistanceSquared = (Bounds.Center - View.ViewMatrices.GetViewOrigin()).SizeSquared();
						float MaxDistSquared = Proxy->GetMaxDrawDistance() * Proxy->GetMaxDrawDistance() * GLightMaxDrawDistanceScale * GLightMaxDrawDistanceScale;

//考虑了光源的半径、视图的LOD因子、最小光源屏幕半径等因素来决定最终光源是否需要绘制,以便剔除掉远距离屏幕占比很小的光源。
						const bool bDrawLight = (FMath::Square(FMath::Min(0.0002f, GMinScreenRadiusForLights / Bounds.W) * View.LODDistanceFactor) * DistanceSquared < 1.0f)
													&& (MaxDistSquared == 0 || DistanceSquared < MaxDistSquared);
							
						VisibleLightViewInfo.bInViewFrustum = bDrawLight;
					}
					else
					{
						VisibleLightViewInfo.bInViewFrustum = true;
					}
				}
			}
			else
			{
				VisibleLightViewInfo.bInViewFrustum = true;

				// 设置单个太阳轴从方向灯移动。 
				if (bSetupMobileLightShafts && LightSceneInfo->bEnableLightShaftBloom && ShouldRenderLightShaftsForLight(View, *LightSceneInfo->Proxy))
				{
					View.MobileLightShaft = GetMobileLightShaftInfo(View, *LightSceneInfo);
				}
			}

[... ...]
}

由上可知对于光源的处理为:使用光源的包围盒与视锥体求交,将不相交的剔除,然后对屏幕占比较小的光源进行提出,并将其可见性记录到VisibleLightViewInfo.bInViewFrustum

Gbuffer Layout:

RenderBasePass():

渲染几何阶段的代码应该在RenderBasePass()这个函数

void FDeferredShadingSceneRenderer::RenderBasePass(
	FRDGBuilder& GraphBuilder,
	FExclusiveDepthStencil::Type BasePassDepthStencilAccess,
	FRDGTextureRef SceneColorTexture,
	FRDGTextureRef SceneDepthTexture,
	ERenderTargetLoadAction SceneDepthLoadAction,
	FRDGTextureRef ForwardShadowMaskTexture)
{
	const bool bEnableParallelBasePasses = GRHICommandList.UseParallelAlgorithms() && CVarParallelBasePass.GetValueOnRenderThread();

	static const auto ClearMethodCVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.ClearSceneMethod"));
	bool bRequiresRHIClear = true;
	bool bRequiresFarZQuadClear = false;

	if (ClearMethodCVar)
	{
		int32 ClearMethod = ClearMethodCVar->GetValueOnRenderThread();

		if (ClearMethod == 0 && !ViewFamily.EngineShowFlags.Game)
		{
			// Do not clear the scene only if the view family is in game mode.
			ClearMethod = 1;
		}

		switch (ClearMethod)
		{
		case 0: // No clear
			bRequiresRHIClear = false;
			bRequiresFarZQuadClear = false;
			break;

		case 1: // RHICmdList.Clear
			bRequiresRHIClear = true;
			bRequiresFarZQuadClear = false;
			break;

		case 2: // Clear using far-z quad
			bRequiresFarZQuadClear = true;
			bRequiresRHIClear = false;
			break;
		}
	}

	// Always perform a full buffer clear for wireframe, shader complexity view mode, and stationary light overlap viewmode.
	if (ViewFamily.EngineShowFlags.Wireframe || ViewFamily.EngineShowFlags.ShaderComplexity || ViewFamily.EngineShowFlags.StationaryLightOverlap)
	{
		bRequiresRHIClear = true;
		bRequiresFarZQuadClear = false;
	}

	const bool bIsWireframeRenderpass = ViewFamily.EngineShowFlags.Wireframe && FSceneRenderer::ShouldCompositeEditorPrimitives(Views[0]);
	const bool bDebugViewMode = ViewFamily.UseDebugViewPS();
	const bool bRenderLightmapDensity = ViewFamily.EngineShowFlags.LightMapDensity && AllowDebugViewmodes();
	const bool bRenderSkyAtmosphereEditorNotifications = ShouldRenderSkyAtmosphereEditorNotifications();
	const bool bDoParallelBasePass = bEnableParallelBasePasses && !bDebugViewMode && !bRenderLightmapDensity; // DebugView and LightmapDensity are non-parallel substitutions inside BasePass
	const bool bNeedsBeginRender = AllowDebugViewmodes() &&
		(ViewFamily.EngineShowFlags.RequiredTextureResolution ||
			ViewFamily.EngineShowFlags.MaterialTextureScaleAccuracy ||
			ViewFamily.EngineShowFlags.MeshUVDensityAccuracy ||
			ViewFamily.EngineShowFlags.PrimitiveDistanceAccuracy ||
			ViewFamily.EngineShowFlags.ShaderComplexity ||
			ViewFamily.EngineShowFlags.LODColoration ||
			ViewFamily.EngineShowFlags.HLODColoration);

	const FExclusiveDepthStencil ExclusiveDepthStencil(BasePassDepthStencilAccess);

	FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(GraphBuilder.RHICmdList);

	TStaticArray BasePassTextures;
	int32 GBufferDIndex = INDEX_NONE;
	uint32 BasePassTextureCount = SceneContext.GetGBufferRenderTargets(GraphBuilder, BasePassTextures, GBufferDIndex);
	TArrayView BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount);
	FRDGTextureRef BasePassDepthTexture = SceneDepthTexture;
	FLinearColor SceneColorClearValue;

	if (bRequiresRHIClear)
	{
		if (ViewFamily.EngineShowFlags.ShaderComplexity)
		{
			SceneContext.ClearQuadOverdrawUAV(GraphBuilder);
		}

		if (ViewFamily.EngineShowFlags.ShaderComplexity || ViewFamily.EngineShowFlags.StationaryLightOverlap)
		{
			SceneColorClearValue = FLinearColor(0, 0, 0, kSceneColorClearAlpha);
		}
		else
		{
			SceneColorClearValue = FLinearColor(Views[0].BackgroundColor.R, Views[0].BackgroundColor.G, Views[0].BackgroundColor.B, kSceneColorClearAlpha);
		}

		ERenderTargetLoadAction ColorLoadAction = ERenderTargetLoadAction::ELoad;

		if (SceneColorTexture->Desc.ClearValue.GetClearColor() == SceneColorClearValue)
		{
			ColorLoadAction = ERenderTargetLoadAction::EClear;
		}
		else
		{
			ColorLoadAction = ERenderTargetLoadAction::ENoAction;
		}

		auto* PassParameters = GraphBuilder.AllocParameters();
		PassParameters->RenderTargets = GetRenderTargetBindings(ColorLoadAction, BasePassTexturesView);

		static TConsoleVariableData* CVarNoGBufferDClear = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.NoGBufferDClear"));
		if (CVarNoGBufferDClear && !!CVarNoGBufferDClear->GetValueOnRenderThread() && GBufferDIndex != INDEX_NONE)
		{
			PassParameters->RenderTargets[GBufferDIndex].SetLoadAction(ERenderTargetLoadAction::ENoAction);
		}

		if (SceneDepthLoadAction == ERenderTargetLoadAction::EClear)
		{
			PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, SceneDepthLoadAction, SceneDepthLoadAction, ExclusiveDepthStencil);
		}

		GraphBuilder.AddPass(RDG_EVENT_NAME("GBufferClear"), PassParameters, ERDGPassFlags::Raster,
			[PassParameters, ColorLoadAction, SceneColorClearValue](FRHICommandList& RHICmdList)
		{
			// If no fast-clear action was used, we need to do an MRT shader clear.
			if (ColorLoadAction == ERenderTargetLoadAction::ENoAction)
			{
				const FRenderTargetBindingSlots& RenderTargets = PassParameters->RenderTargets;
				FLinearColor ClearColors[MaxSimultaneousRenderTargets];
				FRHITexture* Textures[MaxSimultaneousRenderTargets];
				int32 TextureIndex = 0;

				RenderTargets.Enumerate([&](const FRenderTargetBinding& RenderTarget)
				{
					FRHITexture* TextureRHI = RenderTarget.GetTexture()->GetRHI();
					ClearColors[TextureIndex] = TextureIndex == 0 ? SceneColorClearValue : TextureRHI->GetClearColor();
					Textures[TextureIndex] = TextureRHI;
					++TextureIndex;
				});

				// Clear color only; depth-stencil is fast cleared.
				DrawClearQuadMRT(RHICmdList, true, TextureIndex, ClearColors, false, 0, false, 0);
			}
		});

		if (bRenderSkyAtmosphereEditorNotifications)
		{
			// We only render this warning text when bRequiresRHIClear==true to make sure the scene color buffer is allocated at this stage.
			// When false, the option specifies that all pixels must be written to by a sky dome anyway.
			RenderSkyAtmosphereEditorNotifications(GraphBuilder, SceneColorTexture);
		}
	}

	if (ViewFamily.EngineShowFlags.Wireframe)
	{
		checkf(ExclusiveDepthStencil.IsDepthWrite(), TEXT("Wireframe base pass requires depth-write, but it is set to read-only."));

		SceneContext.GetEditorPrimitivesColor(GraphBuilder.RHICmdList);
		SceneContext.GetEditorPrimitivesDepth(GraphBuilder.RHICmdList);

		BasePassTextureCount = 1;
		BasePassTextures[0] = GraphBuilder.RegisterExternalTexture(SceneContext.EditorPrimitivesColor, ERenderTargetTexture::Targetable);
		BasePassTexturesView = MakeArrayView(BasePassTextures.GetData(), BasePassTextureCount);

		BasePassDepthTexture = GraphBuilder.RegisterExternalTexture(SceneContext.EditorPrimitivesDepth, ERenderTargetTexture::Targetable);

		auto* PassParameters = GraphBuilder.AllocParameters();
		PassParameters->RenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::EClear, BasePassTexturesView);
		PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::EClear, ERenderTargetLoadAction::EClear, ExclusiveDepthStencil);

		GraphBuilder.AddPass(RDG_EVENT_NAME("WireframeClear"), PassParameters, ERDGPassFlags::Raster, [](FRHICommandList&) {});
	}

	// Render targets bindings should remain constant at this point.
	FRenderTargetBindingSlots BasePassRenderTargets = GetRenderTargetBindings(ERenderTargetLoadAction::ELoad, BasePassTexturesView);
	BasePassRenderTargets.DepthStencil = FDepthStencilBinding(BasePassDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, ExclusiveDepthStencil);

	BasePassRenderTargets.ShadingRateTexture = GVRSImageManager.GetVariableRateShadingImage(GraphBuilder, ViewFamily, nullptr, EVRSType::None);

	AddSetCurrentStatPass(GraphBuilder, GET_STATID(STAT_CLM_BasePass));
	RenderBasePassInternal(GraphBuilder, BasePassRenderTargets, BasePassDepthStencilAccess, ForwardShadowMaskTexture, bDoParallelBasePass, bRenderLightmapDensity);
	AddSetCurrentStatPass(GraphBuilder, GET_STATID(STAT_CLM_AfterBasePass));

	if (ViewFamily.ViewExtensions.Num() > 0)
	{
		SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_ViewExtensionPostRenderBasePass);
		RDG_EVENT_SCOPE(GraphBuilder, "BasePass_ViewExtensions");
		auto* PassParameters = GraphBuilder.AllocParameters();
		PassParameters->RenderTargets = BasePassRenderTargets;
		for (auto& ViewExtension : ViewFamily.ViewExtensions)
		{
			for (FViewInfo& View : Views)
			{
				RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
				GraphBuilder.AddPass(
					{},
					PassParameters,
					ERDGPassFlags::Raster,
					[&ViewExtension, &View](FRHICommandListImmediate& RHICmdList)
				{
					ViewExtension->PostRenderBasePass_RenderThread(RHICmdList, View);
				});
			}
		}
	}

	if (bRequiresFarZQuadClear)
	{
		ClearGBufferAtMaxZ(GraphBuilder, Views, BasePassRenderTargets, SceneColorClearValue);
	}

	if (ShouldRenderAnisotropyPass())
	{
		AddSetCurrentStatPass(GraphBuilder, GET_STATID(STAT_CLM_AnisotropyPass));
		RenderAnisotropyPass(GraphBuilder, SceneDepthTexture, bEnableParallelBasePasses);
		AddSetCurrentStatPass(GraphBuilder, GET_STATID(STAT_CLM_AfterAnisotropyPass));
	}

	if (SceneContext.GBufferA)
	{
		AddAsyncComputeSRVTransitionHackPass(GraphBuilder, GraphBuilder.RegisterExternalTexture(SceneContext.GBufferA));
	}
}

GetGBufferRenderTargets()

首先我先确定他有多少个MRT,追踪其中的BasePassRenderTargets变量,一步步深入可以在

中找到int32 FSceneRenderTargets::GetGBufferRenderTargets()

int32 FSceneRenderTargets::GetGBufferRenderTargets(const TRefCountPtr* OutRenderTargets[MaxSimultaneousRenderTargets], int32& OutVelocityRTIndex, int32& OutGBufferDIndex) const
{
	int32 MRTCount = 0;
//rt0为场景颜色
	OutRenderTargets[MRTCount++] = &GetSceneColor();

	const EShaderPlatform ShaderPlatform = GetFeatureLevelShaderPlatform(CurrentFeatureLevel);
	const bool bUseGBuffer = IsUsingGBuffers(ShaderPlatform);

	if (bUseGBuffer)
	{
		OutRenderTargets[MRTCount++] = &GBufferA;
		OutRenderTargets[MRTCount++] = &GBufferB;
		OutRenderTargets[MRTCount++] = &GBufferC;
	}

//rtVelocity =速度缓冲rt
	// The velocity buffer needs to be bound before other optionnal rendertargets (when UseSelectiveBasePassOutputs() is true).
	// Otherwise there is an issue on some AMD hardware where the target does not get updated. Seems to be related to the velocity buffer format as it works fine with other targets.
	if (bAllocateVelocityGBuffer && !IsSimpleForwardShadingEnabled(ShaderPlatform))
	{
		OutVelocityRTIndex = MRTCount;
		check(OutVelocityRTIndex == 4 || (!bUseGBuffer && OutVelocityRTIndex == 1)); // As defined in BasePassPixelShader.usf
		OutRenderTargets[MRTCount++] = &SceneVelocity;
	}
	else
	{
		OutVelocityRTIndex = -1;
	}

	OutGBufferDIndex = INDEX_NONE;

	if (bUseGBuffer)
	{
		OutGBufferDIndex = MRTCount;
		OutRenderTargets[MRTCount++] = &GBufferD;

		if (bAllowStaticLighting)
		{
			check(MRTCount == (bAllocateVelocityGBuffer ? 6 : 5)); // As defined in BasePassPixelShader.usf
			OutRenderTargets[MRTCount++] = &GBufferE;
		}
	}

	check(MRTCount <= MaxSimultaneousRenderTargets);
	return MRTCount;
}

其中MaxSimultaneousRenderTargets如下:

UE源码阅读[1]---由问题入手UE中的延迟渲染_第1张图片

根据上述的代码可以知道,RT最多有8个(根据MaxSimultaneousRenderTargets可知,但是上面好像大概只用了七个?),分别为SceneColor,GBufferA,GBufferB,GBufferC,SceneVelocity,GBufferD,GBufferE,然后其相关的定义在BasePassPixelShader.usf这个像素着色器中

BasePassPixelShader.usf

接下来看BasePassPixelShader.usf

BasePassPixelShader的主入口为void FPixelShaderInOut_MainPS()

UE源码阅读[1]---由问题入手UE中的延迟渲染_第2张图片

可以从MRT的写入这里分析出各个RT到底存了什么

	#if MATERIAL_DOMAIN_POSTPROCESS
		#if MATERIAL_OUTPUT_OPACITY_AS_ALPHA
			Out.MRT[0] = half4(Color, Opacity);
		#else
			Out.MRT[0] = half4(Color, 0);
		#endif
		Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
	// MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT must come first because it also has MATERIALBLENDING_TRANSLUCENT defined
	#elif MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT
		// After thin translucency, the final color is going to be:
		//    FinalColor = DualBlendColorAdd + DualBlendColorMul * BackgroundColor;
	    // To apply fogging, we want the final equation to be:
		//    FinalColor = Fogging.rgb + Fogging.a * (DualBlendColorAdd + DualBlendColorMul * BackgroundColor);
		//    FinalColor = (Fogging.rgb + Fogging.a * DualBlendColorAdd) + Fogging.a * DualBlendColorMul * BackgroundColor;
		// Or in other words:
		//    AdjustedDualBlendAdd = Fogging.rgb + Fogging.a * DualBlendColorAdd;
		//    AdjustedDualBlendMul = Fogging.a * DualBlendColorMul;
		//    FinalColor = AdjustedDualBlendAdd + AdjustedDualBlendMul * BackgroundColor;

		float3 AdjustedDualBlendAdd = Fogging.rgb + Fogging.a * DualBlendColorAdd;
		float3 AdjustedDualBlendMul =               Fogging.a * DualBlendColorMul;

		#if THIN_TRANSLUCENT_USE_DUAL_BLEND
			// no RETURN_COLOR because these values are explicit multiplies and adds
			Out.MRT[0] = half4(AdjustedDualBlendAdd,0.0);
			Out.MRT[1] = half4(AdjustedDualBlendMul,1.0);
		#else
			// In the fallback case, we are blending with the mode 
			float AdjustedAlpha = saturate(1-dot(AdjustedDualBlendMul,float3(1.0f,1.0f,1.0f)/3.0f));
			Out.MRT[0] = half4(AdjustedDualBlendAdd,AdjustedAlpha);
			Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
		#endif
	#elif MATERIALBLENDING_ALPHAHOLDOUT
		// not implemented for holdout
		Out.MRT[0] = half4(Color * Fogging.a + Fogging.rgb * Opacity, Opacity);
		Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
	#elif MATERIALBLENDING_ALPHACOMPOSITE
		Out.MRT[0] = half4(Color * Fogging.a + Fogging.rgb * Opacity, Opacity);
		Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
	#elif MATERIALBLENDING_TRANSLUCENT
		Out.MRT[0] = half4(Color * Fogging.a + Fogging.rgb, Opacity);
		Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
	#elif MATERIALBLENDING_ADDITIVE
		Out.MRT[0] = half4(Color * Fogging.a * Opacity, 0.0f);
		Out.MRT[0] = RETURN_COLOR(Out.MRT[0]);
	#elif MATERIALBLENDING_MODULATE
		// RETURN_COLOR not needed with modulative blending
		half3 FoggedColor = lerp(float3(1, 1, 1), Color, Fogging.aaa * Fogging.aaa);
		Out.MRT[0] = half4(FoggedColor, Opacity);
	#else
		{
			FLightAccumulator LightAccumulator = (FLightAccumulator)0;

			// Apply vertex fog
			Color = Color * Fogging.a + Fogging.rgb;

#if POST_PROCESS_SUBSURFACE
			// Apply vertex fog to diffuse color
			DiffuseColor = DiffuseColor * Fogging.a + Fogging.rgb;

			if (UseSubsurfaceProfile(GBuffer.ShadingModelID) && 
                View.bSubsurfacePostprocessEnabled > 0 && View.bCheckerboardSubsurfaceProfileRendering > 0 )
			{
				// Adjust for checkerboard. only apply non-diffuse lighting (including emissive) 
				// to the specular component, otherwise lighting is applied twice
				Color *= !bChecker;
			}
			LightAccumulator_Add(LightAccumulator, Color + DiffuseColor, DiffuseColor, 1.0f, UseSubsurfaceProfile(GBuffer.ShadingModelID));
#else
			LightAccumulator_Add(LightAccumulator, Color, 0, 1.0f, false);
#endif
			Out.MRT[0] = RETURN_COLOR(LightAccumulator_GetResult(LightAccumulator));

			#if !USES_GBUFFER
				// Without deferred shading the SSS pass will not be run to reset scene color alpha for opaque / masked to 0
				// Scene color alpha is used by scene captures and planar reflections
				Out.MRT[0].a = 0;
			#endif
		}
	#endif

	#if USES_GBUFFER
		GBuffer.IndirectIrradiance = IndirectIrradiance;

		// -0.5 .. 0.5, could be optimzed as lower quality noise would be sufficient
		float QuantizationBias = PseudoRandom( MaterialParameters.SvPosition.xy ) - 0.5f;
		EncodeGBuffer(GBuffer, Out.MRT[1], Out.MRT[2], Out.MRT[3], OutGBufferD, OutGBufferE, OutVelocity, QuantizationBias);
	#endif 

	if(bEditorWeightedZBuffering)
	{
		Out.MRT[0].a = 1;

		#if MATERIALBLENDING_MASKED
			// some material might have a opacity value
			Out.MRT[0].a = GetMaterialMaskInputRaw(PixelMaterialInputs);
		#endif

		#if EDITOR_ALPHA2COVERAGE != 0
			// per MSAA sample
			if(View.NumSceneColorMSAASamples > 1)
			{
				Out.Coverage = In.Coverage & CustomAlpha2Coverage(Out.MRT[0]);
			}
			else
			{
				// no MSAA is handle like per pixel
				clip(Out.MRT[0].a - GetMaterialOpacityMaskClipValue());
			}
		#else
			// per pixel
			clip(Out.MRT[0].a - GetMaterialOpacityMaskClipValue());
		#endif
	}

#if USES_GBUFFER
	#if GBUFFER_HAS_VELOCITY
		Out.MRT[4] = OutVelocity;
	#endif

	Out.MRT[GBUFFER_HAS_VELOCITY ? 5 : 4] = OutGBufferD;

	#if GBUFFER_HAS_PRECSHADOWFACTOR
		Out.MRT[GBUFFER_HAS_VELOCITY ? 6 : 5] = OutGBufferE;
	#endif
#else
	// If not using the full gbuffer (forward shading) the velocity buffer can still be written to in the basepass.
	#if GBUFFER_HAS_VELOCITY
		Out.MRT[1] = OutVelocity;
	#endif
#endif

#if !MATERIALBLENDING_MODULATE && USE_PREEXPOSURE
#if MATERIAL_IS_SKY
	// Dynamic capture exposure is 1 as of today.
	const float ViewPreExposure = View.RealTimeReflectionCapture>0.0f ? View.RealTimeReflectionCapturePreExposure : View.PreExposure;
#else
	const float ViewPreExposure = View.PreExposure;
#endif
	// We need to multiply pre-exposure by all components including A, otherwise the ratio of
	// diffuse to specular lighting will get messed up in the SSS pass.
	// RGB: Full color (Diffuse + Specular)
	// A:   Diffuse Intensity, but only if we are not blending
	#if MATERIAL_DOMAIN_POSTPROCESS || MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT || MATERIALBLENDING_ALPHAHOLDOUT || MATERIALBLENDING_ALPHACOMPOSITE || MATERIALBLENDING_TRANSLUCENT || MATERIALBLENDING_ADDITIVE
		Out.MRT[0].rgb  *= ViewPreExposure;
	#else
		Out.MRT[0].rgba *= ViewPreExposure;
	#endif
#endif
#if MATERIAL_IS_SKY
	// Sky materials can result in high luminance values, e.g. the sun disk. 
	// This is so we make sure to at least stay within the boundaries of fp10 and not cause NaN on some platforms.
	// We also half that range to also make sure we have room for other additive elements such as bloom, clouds or particle visual effects.
	Out.MRT[0].xyz = min(Out.MRT[0].xyz, Max10BitsFloat.xxx * 0.5f);
#endif

首先分析SceneColor ,由于ue为了兼容不同的光照模型,用了一堆的宏,基本是思路是调用实际的光照模型变体计算基础Diffuse色+自发光+叠加雾效【会根据着色模型的不同有变化】

然后上面有个EncodeGBuffer函数将GBuffer中的部分数据编码到MRT中,这个函数的定义在DeferredShadingCommon.ush

/** Populates OutGBufferA, B and C */
void EncodeGBuffer(
	FGBufferData GBuffer,
	out float4 OutGBufferA,
	out float4 OutGBufferB,
	out float4 OutGBufferC,
	out float4 OutGBufferD,
	out float4 OutGBufferE,
	out float4 OutGBufferVelocity,
	float QuantizationBias = 0		// -0.5 to 0.5 random float. Used to bias quantization.
	)
{
	if (GBuffer.ShadingModelID == SHADINGMODELID_UNLIT)
	{
		OutGBufferA = 0;
		SetGBufferForUnlit(OutGBufferB);
		OutGBufferC = 0;
		OutGBufferD = 0;
		OutGBufferE = 0;
	}
	else
	{
#if MOBILE_DEFERRED_SHADING
		OutGBufferA.rg = UnitVectorToOctahedron( normalize(GBuffer.WorldNormal) ) * 0.5f + 0.5f;
		OutGBufferA.b = GBuffer.PrecomputedShadowFactors.x;
		OutGBufferA.a = GBuffer.PerObjectGBufferData;		
#elif 1
		OutGBufferA.rgb = EncodeNormal( GBuffer.WorldNormal );
		OutGBufferA.a = GBuffer.PerObjectGBufferData;
#else
		float3 Normal = GBuffer.WorldNormal;
		uint   NormalFace = 0;
		EncodeNormal( Normal, NormalFace );

		OutGBufferA.rg = Normal.xy;
		OutGBufferA.b = 0;
		OutGBufferA.a = GBuffer.PerObjectGBufferData;
#endif

		OutGBufferB.r = GBuffer.Metallic;
		OutGBufferB.g = GBuffer.Specular;
		OutGBufferB.b = GBuffer.Roughness;
		OutGBufferB.a = EncodeShadingModelIdAndSelectiveOutputMask(GBuffer.ShadingModelID, GBuffer.SelectiveOutputMask);

		OutGBufferC.rgb = EncodeBaseColor( GBuffer.BaseColor );

#if ALLOW_STATIC_LIGHTING
		// No space for AO. Multiply IndirectIrradiance by AO instead of storing.
		OutGBufferC.a = EncodeIndirectIrradiance(GBuffer.IndirectIrradiance * GBuffer.GBufferAO) + QuantizationBias * (1.0 / 255.0);
#else
		OutGBufferC.a = GBuffer.GBufferAO;
#endif

		OutGBufferD = GBuffer.CustomData;
		OutGBufferE = GBuffer.PrecomputedShadowFactors;
	}

#if WRITES_VELOCITY_TO_GBUFFER
	OutGBufferVelocity = GBuffer.Velocity;
#else
	OutGBufferVelocity = 0;
#endif
}

下面配上上面函数用到的一些函数:

①单位向量的压缩,八面体映射压缩:

// Octahedron Normal Vectors
// [Cigolle 2014, "A Survey of Efficient Representations for Independent Unit Vectors"]
//						Mean	Max
// oct		8:8			0.33709 0.94424
// snorm	8:8:8		0.17015 0.38588
// oct		10:10		0.08380 0.23467
// snorm	10:10:10	0.04228 0.09598
// oct		12:12		0.02091 0.05874

float2 UnitVectorToOctahedron( float3 N )
{
	N.xy /= dot( 1, abs(N) );
	if( N.z <= 0 )
	{
		N.xy = ( 1 - abs(N.yx) ) * ( N.xy >= 0 ? float2(1,1) : float2(-1,-1) );
	}
	return N.xy;
}

②压缩ShadingModelId和SelectiveOutputMask

loat EncodeShadingModelIdAndSelectiveOutputMask(uint ShadingModelId, uint SelectiveOutputMask)
{
	uint Value = (ShadingModelId & SHADINGMODELID_MASK) | SelectiveOutputMask;
	return (float)Value / (float)0xFF;
}

③编码IndirectIrradiance

float EncodeIndirectIrradiance(float IndirectIrradiance)
{
	float L = IndirectIrradiance;
#if USE_PREEXPOSURE
	L *= View.PreExposure; // Apply pre-exposure as a mean to prevent compression overflow.
#endif
	const float LogBlackPoint = 0.00390625;	// exp2(-8);
	return log2( L + LogBlackPoint ) / 16 + 0.5;
}

然后根据这个编码函数我们可以知道GBuffer的布局,虽然根据不同光照模型/管线会有些许差异,

但是大致可以总结如下:

GBuffer布局:

①GBufferA:rg/b存储法线/预计算的阴影因子,a存储预计算的物体数据

②GBufferB:r存储金属度,g存储高光值,b存储粗糙度,a存储编码的光照模型和SelectiveOutputMask

SelectiveOutputMask记录了绘制时以下宏的开启结果:

  • MATERIAL_USES_ANISOTROPY 禁止计算各向异性
  • !GBUFFER_HAS_PRECSHADOWFACTOR 禁止读取GBufferE数据作为预计算阴影
  • GBUFFER_HAS_PRECSHADOWFACTOR && WRITES_PRECSHADOWFACTOR_ZERO 当不读取GBufferE时,若此值为1时,预计算阴影设为0,否则为1。
  • WRITES_VELOCITY_TO_GBUFFER 禁止从Gbuffer中读取速度值。

③GBufferC :rgb存储basecolor,a存储AO,如果允许静态光照则a存储IndirectIrradiance*Material AO

④GBufferD:存储自定义的数据

⑤GBufferE:存储预计算的阴影值

⑥GBufferVelocity:存储SceneVelocity

处理同屏不同的光照模型:

基本思路:

UE的shader采用的是Uber Shader的设计,通过再一个shader里面定义一堆的宏,然后在编译Shader的时候,传入不同的宏参数,编译出不同的shader代码(bgfx里面也是这么干的),

在编译的时候,不同的宏怎么传入编译器的呢,UE主要建立了一个Shader Permutation的概念,来存储一个唯一的哈希键值,通过不同的Shader Permutation传到HLSL,编译出对应的着色器代码,然后在通过ShaderMap获取到实例的shader

ShaderPermutation:

Shader Permutation的相关代码在ShaderPermutation.h中,

template 
struct TShaderPermutationDomain
{
	/** Setup the dimension's type in permutation domain as itself so that a permutation domain can be
	 * used as a dimension of another domain.
	 */
	using Type = TShaderPermutationDomain;

	/** Define a domain as a multidimensional dimension so that ModifyCompilationEnvironment() is used. */
	static constexpr bool IsMultiDimensional = true;

	/** Parent type in the variadic template to reduce code. */
	using Super = TShaderPermutationDomain;

	/** Total number of permutation within the domain. */
	static constexpr int32 PermutationCount = Super::PermutationCount * TDimension::PermutationCount;


	/** Constructors. */
	TShaderPermutationDomain()
		: DimensionValue(TDimension::FromDimensionValueId(0))
	{
	}

	explicit TShaderPermutationDomain(int32 PermutationId)
		: DimensionValue(TDimension::FromDimensionValueId(PermutationId % TDimension::PermutationCount))
		, Tail(PermutationId / TDimension::PermutationCount)
	{
		checkf(PermutationId >= 0 && PermutationId < PermutationCount, TEXT("Invalid shader permutation id %i."), PermutationId);
	}


	/** Set dimension's value. */
	template
	void Set(typename DimensionToSet::Type Value)
	{
		return TShaderPermutationDomainSpetialization::Value>::template SetDimension(*this, Value);
	}


	/** Get dimension's value. */
	template
	const typename DimensionToGet::Type& Get() const
	{
		return TShaderPermutationDomainSpetialization::Value>::template GetDimension(*this);
	}


	/** Modify the shader's compilation environment. */
	void ModifyCompilationEnvironment(FShaderCompilerEnvironment& OutEnvironment) const
	{
		TShaderPermutationDomainSpetialization::template ModifyCompilationEnvironment(*this, OutEnvironment);
	}


	/** Converts domain permutation vector to domain's value id. */
	static int32 ToDimensionValueId(const Type& PermutationVector)
	{
		return PermutationVector.ToDimensionValueId();
	}

	int32 ToDimensionValueId() const
	{
		return TDimension::ToDimensionValueId(DimensionValue) + TDimension::PermutationCount * Tail.ToDimensionValueId();
	}


	/** Returns the permutation domain from the unique ID. */
	static Type FromDimensionValueId(const int32 PermutationId)
	{
		return Type(PermutationId);
	}


	/** Test if equal. */
	bool operator==(const Type& Other) const
	{
		return DimensionValue == Other.DimensionValue && Tail == Other.Tail;
	}

	/** Test if not equal. */
	bool operator!=(const Type& Other) const
	{
		return !(*this == Other);
	}

private:
	template
	friend class TShaderPermutationDomainSpetialization;

	typename TDimension::Type DimensionValue;
	Super Tail;
};

这个模板其实不太好理解,根据结合class FDeferredLightPS : public FGlobalShader中的代码简单理一下

FDeferredLightPS:

首先把对应的阵列声明出来,其中的string就是shader中的宏

	class FSourceShapeDim		: SHADER_PERMUTATION_ENUM_CLASS("LIGHT_SOURCE_SHAPE", ELightSourceShape);
	class FSourceTextureDim		: SHADER_PERMUTATION_BOOL("USE_SOURCE_TEXTURE");
	class FIESProfileDim		: SHADER_PERMUTATION_BOOL("USE_IES_PROFILE");
	class FInverseSquaredDim	: SHADER_PERMUTATION_BOOL("INVERSE_SQUARED_FALLOFF");
	class FVisualizeCullingDim	: SHADER_PERMUTATION_BOOL("VISUALIZE_LIGHT_CULLING");
	class FLightingChannelsDim	: SHADER_PERMUTATION_BOOL("USE_LIGHTING_CHANNELS");
	class FTransmissionDim		: SHADER_PERMUTATION_BOOL("USE_TRANSMISSION");
	class FHairLighting			: SHADER_PERMUTATION_INT("USE_HAIR_LIGHTING", 2);
	class FAtmosphereTransmittance : SHADER_PERMUTATION_BOOL("USE_ATMOSPHERE_TRANSMITTANCE");
	class FCloudTransmittance 	: SHADER_PERMUTATION_BOOL("USE_CLOUD_TRANSMITTANCE");
	class FAnistropicMaterials 	: SHADER_PERMUTATION_BOOL("SUPPORTS_ANISOTROPIC_MATERIALS");

然后在获取实例shader的时候如下,先声明一个Permutation域,并且设置他的值表明对应的编译选项是否开启,然后再利用这个Permutation域从ShaderMap获取到对应的实例shader,然后一个shader是否需要编译是通过一个静态函数static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)

	FDeferredLightPS::FPermutationDomain PermutationVector;
				PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >( ELightSourceShape::Directional );
				PermutationVector.Set< FDeferredLightPS::FIESProfileDim >( false );
				PermutationVector.Set< FDeferredLightPS::FInverseSquaredDim >( false );
				PermutationVector.Set< FDeferredLightPS::FVisualizeCullingDim >( View.Family->EngineShowFlags.VisualizeLightCulling );
				PermutationVector.Set< FDeferredLightPS::FLightingChannelsDim >( View.bUsesLightingChannels );
				PermutationVector.Set< FDeferredLightPS::FAnistropicMaterials >(ShouldRenderAnisotropyPass());
				PermutationVector.Set< FDeferredLightPS::FTransmissionDim >( bTransmission );
				PermutationVector.Set< FDeferredLightPS::FHairLighting>(0);
				// Only directional lights are rendered in this path, so we only need to check if it is use to light the atmosphere
				PermutationVector.Set< FDeferredLightPS::FAtmosphereTransmittance >(bAtmospherePerPixelTransmittance);
				PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(bLight0CloudPerPixelTransmittance || bLight1CloudPerPixelTransmittance);

				TShaderMapRef< FDeferredLightPS > PixelShader( View.ShaderMap, PermutationVector );
				GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
				GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
				GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();

				SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
				PixelShader->SetParameters(RHICmdList, View, LightSceneInfo, ScreenShadowMaskTexture, LightingChannelsTexture, &RenderLightParams);

你可能感兴趣的:(图形学学习笔记,游戏引擎,ue4,unreal,图形渲染,算法)