深入分析虛幻4引擎光線追蹤(一):框架分析

概述

本系列文章旨在拆解虛幻4引擎中光線跟蹤功能的具體實現(xiàn)。虛幻引擎是非常流行的游戲引擎,各種拆解文章汗牛充棟,在此不會對虛幻引擎的整體架構(gòu)多做分析。由于本民科水平有限,難免錯漏,還請各位看官海涵。

由于內(nèi)容繁多,打算分成幾篇來詳細(xì)分析:

  1. 框架分析,即是本篇,主要是從框架抽象的角度來看整體設(shè)計
  2. 場景空間反射
  3. 全局照明(計劃中)
  4. 陰影和全局遮蔽(計劃中)
  5. 路徑跟蹤(計劃中)

虛幻4延遲渲染框架

此處只分析虛幻4引擎的延遲渲染框架。虛幻4引擎通過TaskGraph實現(xiàn)了渲染邏輯的異步拆分(具體分析文章很多,此處不再贅述),其延遲渲染器的主體部分放在 DeferredShadingRenderer.cpp 文件中,具體邏輯在 FDeferredShadingSceneRenderer::Render() 函數(shù)中。雖然TaskGraph是完全異步的,但是整體的分派邏輯整合到了這個巨大的函數(shù)中,這并不利于定制修改和維護。

一般延遲渲染分為4個步驟:Z-Pass,前向階段,延遲階段,透明渲染和后處理【3】,虛幻引擎也并不例外。光線追蹤部分則通過嵌入的方式合并到渲染中,最終在延遲渲染階段與光柵化的圖像合并在一起。

除PathTracing外,虛幻4引擎將光追拆分成了幾個部分,分別計算,這樣可以將計算量控制在一定范圍,以達(dá)到實時處理的目的。

虛幻4延遲渲染框架

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的整體架構(gòu)

其中可編程部分DXR提供幾種新的Shader,詳情請查詢DXR的官方文檔,此處拆解一下其運算流程:

  • 首先由光線產(chǎn)生Shader調(diào)用TraceRay()發(fā)射光線

  • 進入硬件實現(xiàn)部分,對兩級的BVH加速結(jié)構(gòu)進行遍歷

  • 調(diào)用相交和碰撞的自定義Shader

  • 判斷是否有碰撞,調(diào)用最近碰撞或是未命中Shader

DXR Shader的執(zhí)行流程

光柵化與光線追蹤的混合

以上介紹了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)性能還是十分喜人的,希望虛幻引擎能夠盡快完善光追功能。

引用:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 最近光線追蹤技術(shù)終于隨著兩大主機平臺的支持進入了全面普及時代,AMD也緊跟NVIDIA的腳步推出了支持光追功能的新...
    HAPPY_CUTE閱讀 1,151評論 0 1
  • 4 更多渲染技術(shù) 傳統(tǒng)的向前渲染方式需要一個完整的圖像渲染管線,以頂點著色器為起點,氣候跟隨多個后續(xù)階段,通常以片...
    RichardJieChen閱讀 2,205評論 0 3
  • Chapt1. Why to write a RayTracing Render 提到Computer Graph...
    zsYec閱讀 29,144評論 5 27
  • 久違的晴天,家長會。 家長大會開好到教室時,離放學(xué)已經(jīng)沒多少時間了。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,819評論 16 22
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉(zhuǎn)變要...
    余生動聽閱讀 10,843評論 0 11

友情鏈接更多精彩內(nèi)容