概述
本系列文章旨在拆解虛幻4引擎中光線跟蹤功能的具體實現(xiàn)。虛幻引擎是非常流行的游戲引擎,各種拆解文章汗牛充棟,在此不會對虛幻引擎的整體架構(gòu)多做分析。由于本民科水平有限,難免錯漏,還請各位看官海涵。
由于內(nèi)容繁多,打算分成幾篇來詳細(xì)分析:
- 框架分析,即是本篇,主要是從框架抽象的角度來看整體設(shè)計
- 場景空間反射
- 全局照明(計劃中)
- 陰影和全局遮蔽(計劃中)
- 路徑跟蹤(計劃中)
虛幻4延遲渲染框架
此處只分析虛幻4引擎的延遲渲染框架。虛幻4引擎通過TaskGraph實現(xiàn)了渲染邏輯的異步拆分(具體分析文章很多,此處不再贅述),其延遲渲染器的主體部分放在 DeferredShadingRenderer.cpp 文件中,具體邏輯在 FDeferredShadingSceneRenderer::Render() 函數(shù)中。雖然TaskGraph是完全異步的,但是整體的分派邏輯整合到了這個巨大的函數(shù)中,這并不利于定制修改和維護。
一般延遲渲染分為4個步驟:Z-Pass,前向階段,延遲階段,透明渲染和后處理【3】,虛幻引擎也并不例外。光線追蹤部分則通過嵌入的方式合并到渲染中,最終在延遲渲染階段與光柵化的圖像合并在一起。
除PathTracing外,虛幻4引擎將光追拆分成了幾個部分,分別計算,這樣可以將計算量控制在一定范圍,以達(dá)到實時處理的目的。

DXR的渲染框架
傳統(tǒng)的光線跟蹤渲染器可能各部分拆分的不是很明顯,典型的如Blender集成的Cycles渲染器?!?】
DXR和RTX則很小心的把BVH射線求交部分拆分出來,使用硬件進行加速,開創(chuàng)了實時光線追蹤的新時代。
DXR光線追蹤可以大致分成以下模塊:
BVH加速結(jié)構(gòu)(Top Level和Bottom Level)
硬件加速的光線求交運算(硬件實現(xiàn))
可編程的邏輯處理(Pipeline)
運算中臨時存儲結(jié)構(gòu)(Shading Binding Table)

其中可編程部分DXR提供幾種新的Shader,詳情請查詢DXR的官方文檔,此處拆解一下其運算流程:
首先由光線產(chǎn)生Shader調(diào)用TraceRay()發(fā)射光線
進入硬件實現(xiàn)部分,對兩級的BVH加速結(jié)構(gòu)進行遍歷
調(diào)用相交和碰撞的自定義Shader
判斷是否有碰撞,調(diào)用最近碰撞或是未命中Shader

光柵化與光線追蹤的混合
以上介紹了DXR的基本框架,在引擎里面臨的現(xiàn)實問題是純粹的光線跟蹤對于本世代硬件來說還是太慢,無法真正的實時運行。因此虛幻4引擎采用了混合光柵化管線與光線追蹤管線的方法,在最終的延遲/后處理階段將兩者合并在一起。
本文第一部分描繪了整個管線的框架,下面來具體看一下光線追蹤反射與全局光照的調(diào)用邏輯,NVIDIA修改版的半透明焦散效果請參考本民科的 [此篇文章]:(http://www.itdecent.cn/p/a238f22133d6)。
這一部分涉及的調(diào)用邏輯代碼在IndirectLightRendering.cpp文件中。首先是在開始渲染前先要構(gòu)建加速結(jié)構(gòu),在引擎中我們需要兩個步驟:
首先要收集場景中參與光線追蹤的所有物體
GatherRayTracingWorldInstances()其次需要將物體的信息和狀態(tài)同步加速結(jié)構(gòu)中
DispatchRayTracingWorldUpdates()
然后是具體的調(diào)用:
void FDeferredShadingSceneRenderer::RenderDiffuseIndirectAndAmbientOcclusion(FRHICommandListImmediate& RHICmdListImmediate)
{
...
if (bApplyRTGI)
{
bool bIsValid = RenderRayTracingGlobalIllumination(GraphBuilder, SceneTextures, View, /* out */ &RayTracingConfig, /* out */ &DenoiserInputs);
if (!bIsValid)
{
DenoiseMode = 0;
}
}
...
}
void FDeferredShadingSceneRenderer::RenderDeferredReflectionsAndSkyLighting(FRHICommandListImmediate& RHICmdList, TRefCountPtr<IPooledRenderTarget>& DynamicBentNormalAO, TRefCountPtr<IPooledRenderTarget>& VelocityRT, FHairStrandsDatas* HairDatas)
{
...
if (bRayTracedReflections)
{
#if RHI_RAYTRACING
RayTracingConfig.RayCountPerPixel = GetRayTracingReflectionsSamplesPerPixel(View);
#else
RayTracingConfig.RayCountPerPixel = 0;
#endif
RenderRayTracingReflections(
GraphBuilder,
SceneTextures,
View,
RayTracingConfig.RayCountPerPixel, RayTracingConfig.ResolutionFraction,
&DenoiserInputs);
}
...
}
渲染的結(jié)果輸出到指定的貼圖上,最終通過加色混合模式疊加到幀緩存上。這個過程有時候會跟其他過程混合在一起,比如光線跟蹤反射的結(jié)果是通過環(huán)境反射的Shader采樣輸出的,這樣的好處是與屏幕空間反射可以無縫銜接到一起,減少不必要的消耗。
GraphBuilder.AddPass(
RDG_EVENT_NAME("ReflectionEnvironmentAndSky %dx%d", View.ViewRect.Width(), View.ViewRect.Height()),
PassParameters,
ERDGPassFlags::Raster,
[PassParameters, &View, PixelShader, bCheckerboardSubsurfaceRendering](FRHICommandList& InRHICmdList)
{
...
if (GetReflectionEnvironmentCVar() == 2 || GAOOverwriteSceneColor)
{
// override scene color for debugging
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
}
else
{
if (bCheckerboardSubsurfaceRendering)
{
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_One>::GetRHI();
}
else
{
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
}
}
SetGraphicsPipelineState(InRHICmdList, GraphicsPSOInit);
SetShaderParameters(InRHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
FPixelShaderUtils::DrawFullscreenTriangle(InRHICmdList);
});
可以看到,實際上虛幻4的光線追蹤是分離成不同的特性,最終疊加到光柵化管線的結(jié)果上的,這樣做的好處是可以最大化利用原有的流程,修改不用傷筋動骨。
總結(jié)
由于要兼容各種配置的項目,代碼中有大量的宏開關(guān)和條件判斷,這部分功能也尚未完善(4.25版本),有大量的TODO并且缺乏注釋,給分析帶來了一些困難。最近使用最新的3060Ti顯卡搭建Demo,發(fā)現(xiàn)性能還是十分喜人的,希望虛幻引擎能夠盡快完善光追功能。
引用:
【3】Deferred Shading in S.T.A.L.K.E.R. (GPU Gems Chapter 9)
【5】Ray Tracing Deformable Scenes using Dynamic Bounding Volume Hierarchies