こちらは Unreal Engine 4 Advent Calender 2017 16日目の記事です。
以前書いたUE4描画パスの順序をまとめたものがだいぶ古くなってしまっていたので最新版へのアップデート。
以前の記事はこちらです。
動作を完全に確認しているわけではないので、ミスがあるかもしれません。
見かけたらお伝えいただければ。
なお、今回もDeferred Renderingパスに関してのみで、モバイルやForward Renderingパスについては言及しません。
RenderDocを見ながらチェックする方が読む側は楽しいと思いますが、描画パスを詳しく知りたい、あわよくば改造したいという人はRenderDocよりソースコードの行数やコード片の方がうれしいかな、と思いまして。
Deferred Renderingの開始は
Engine/Source/Runtime/Renderer/Private/DeferredShadingRenderer.cpp (532)
FDeferredShadingSceneRenderer::Render()
ここから開始です。
行数 | コード片 | 内容 |
---|---|---|
539 | GRenderTargetPool.TransitionTargetsWritable() | ターゲットプールのターゲットを描画可能な状態に遷移 |
542 | SceneContext.ReleaseSceneColor() | シーンカラーバッファを解放 |
546~552 | FRHICommandListExecutor::WaitOnRHIThreadFence() | RHIが別スレッドで動作している場合、ここで OcculusionSubmittedFence を待つ 前段のOcculusionCulling処理の終了待ち? |
568 | GSystemTextures.InitializeTextures() | システムテクスチャの初期化 初期化済みならなにもしない |
571 | SceneContext.Allocate() | ViewFamilyに見合ったレンダーターゲット用のバッファを確保する ViewFamilyはFeatureLevelやSceneCaptureがあるかなどの情報 |
573 | SceneContext.AllocDummyGBufferTargets() | GBufferにダミーの黒テクスチャを割り当て |
579 | InitViews() | Viewに関する様々な初期化 メッシュのカリング、半透明のソート、シャドウやライトキャッシュの初期化など多岐にわたる 詳しくはソースを追ってほしい |
581~588 | PostInitViewFamily_RenderThread()など | 未実装っぽい |
599~606 | GetEyeAdaptation() | const関数を呼び出してるが取得したものを格納してないので意味がない? |
608~631 | if (ShouldPrepareDistanceFieldScene()) | DistanceField系(AO、Shadowなど)が使用されているとこのブロックが有効になる ただし、IntelHD4000シリーズは3Dテクスチャ生成に失敗するらしく、強制的に無効化される 処理内容はDF系技術で必要になるView空間に整列したVolumeテクスチャの作成、更新など |
720~727 | FGlobalDynamicVertexBuffer::Get().Commit() | 動的な頂点バッファとインデックスバッファのコミット処理 主にパーティクル用の頂点、インデックスバッファをアンロック(アンマップ) |
732~737 | Scene->FXSystem->PreRender() | エフェクトの描画事前処理 GPUパーティクルの更新を主に行う 平面リフレクションが有効だったりレンダースレッドが有効な場合はここでは行わない |
767~788 | RenderPrePass() | Z Pre-Pass レンダリングを行う |
807 | SceneContext.ResolveSceneDepthTexture() | Z Pre-Passで描画した深度バッファのリゾルブ 圧縮深度の展開やMSAAのリゾルブ処理 |
811 | ComputeLightGrid() | ライトグリッドの計算 シェーダコードから察するに、Clusterdライティングのためのライトリンクリストを生成している 主に半透明のForward Rendering用 |
821~826 | SceneContext.AllocGBufferTargets() | GBufferためのメモリ確保 |
830~841 | RenderOcclusion() | オクルージョンに関するレンダリング 低解像度バッファに対するオクルージョンクエリなど 階層型深度バッファもここで作る Z Pre-Passですべてが深度バッファに描画されている場合に有効になる SSAOの並列処理を行う場合はここで処理を開始 |
845~849 | RenderShadowDepthMaps() | シャドウマップの描画 昔と違ってアトラス化したシャドウマップにレンダリングする |
852~857 | ClearLPVs() | Light Propagation Volumeで使用するバッファをクリアする |
859~863 | RenderCustomDepthPassAtLocation() | CustomDepthをベースパス前に描画する r.CustomDepth.Orderで0を設定するとこの段階で描画する DBufferパスで使用することができる |
865~868 | ComputeVolumetricFog() | ボリュームフォグを適用するため、空間分割した3Dテクスチャにライティングを行う 平行光源1灯のみ(多分)、ライトファンクションに対応している |
870~878 | RenderForwardShadingShadowProjections() | Forward Renderingの場合にシーンにシャドウを投影する Forwardでは複数のシャドウをマテリアル計算時に処理しづらいので、ホワイトバッファという真っ白なバッファに影による減衰を予め計算しておく |
885~900 | if (bDBuffer) | DBufferが有効な場合にDeferred Decalを描画する |
902~934 | AllocateDeferredShadingPathRenderTargets() | Deferred Shading用のレンダーターゲットを確保 条件によってはGBufferや半透明用のボリュームライトバッファもクリアする |
936~946 | BeginRenderingGBuffer() | レンダーターゲットの設定 ワイヤーフレーム表示の場合はMSAAバッファを使用する |
951~958 | RenderBasePass() | ベースパスの描画 複数スレッドで並列にコマンドを積むようになってるっぽい ベースパス終了後に深度バッファのリゾルブ |
976~985 | ClearGBufferAtMaxZ() | 描画されなかった部分のGBufferのクリア 通常は必要ないはずだが、GBuffer表示の対応のためか? |
987 | VisualizeVolumetricLightmap() | ボリュームライトマップの可視化 |
989 | ResolveSceneDepthToAuxiliaryTexture() | 深度バッファを別バッファにリゾルブする 深度テストしながらフェッチできないハード向け |
991~1002 | RenderOcclusion() | ベースパス後のオクルージョン描画 830行目でレンダリングを行ってない場合はここでレンダリング |
1012~1017 | RenderShadowDepthMaps() | シャドウマップやボリュームフォグの描画 オクルージョン描画がベースパス後の場合はここで行う |
1019~1023 | RenderCustomDepthPassAtLocation() | ベースパス後のカスタムデプス描画 通常はここで描画される |
1028~1038 | FXSystem->PostRenderOpaque() | ベースパス後のエフェクト描画 主に衝突判定をとるGPUパーティクルの更新を行っている |
1042~1054 | RenderVelocities() | 速度バッファを描画する |
1057 | CopyStencilToLightingChannelTexture() | ステンシルバッファに書きこまれたライトチャンネル情報をコピー |
1059~1061 | GfxWaitForAsyncSSAO() | 並列処理しているSSAOのレンダリングを待つ |
1065~1081 | ProcessAfterBasePass() | ベースパス後のポストプロセス まだ実行されていない場合、Deferred DecalやSSAOの処理を行う |
1084~1101 | SetRenderTargetsAndClear() | ステンシルバッファのみクリア |
1111~1114 | RenderIndirectCapsuleShadows() | カプセルシャドウによる間接照明計算 SceneColorとSSAOに乗算合成される この段階ではSceneColorはベースパスのエミッシブが書き込まれているはず |
1118 | RenderDFAOAsIndirectShadowing() | DistanceFieldAOの描画 ついでにベントノーマルも計算し、この後数段で使用する |
1121~1124 | ClearTranslucentVolumeLighting() | 半透明用のボリュームライトをクリアする |
1127 | RenderLights() | ライトのレンダリング |
ここから RenderLights() 関数内に一旦入ります。
ソースコードは以下です。
Engine/Source/Runtime/Renderer/Private/LightRendering.cpp (316)
行数 | コード片 | 内容 |
---|---|---|
329~332 | GatherSimpleLights() | パーティクルライトをSimpleLightとして集める 主にタイルベースライティング用 |
340~365 | LightSceneInfo->ShouldRenderLight() | 描画すべきライトのリストを作成する |
368~375 | SortedLights.Sort() | シャドウあり、ライトファンクションありなどでライトをソートする |
420~424 | WaitComputeFence(TranslucencyLightingVolumeClearEndFence) | 非同期実行している半透明用ライトボリュームのクリアを待つ |
435~455 | RenderTiledDeferredLighting() | タイルベースのDeferred Lightingを実行する |
457~461 | RenderSimpleLightsStandardDeferred() | SimpleLightをレンダリングする タイルベースレンダリングを実行している場合はここでのレンダリングは行わない |
463~478 | RenderLight() | シャドウなし、ライトファンクションなしのライトをレンダリング |
480~495 | InjectTranslucentVolumeLightingArray() | シャドウなしライト、SimpleLightを半透明用ライトボリュームに描画する ジオメトリシェーダを用いて各スライスに1ライト1DrawCallで描画 |
499~559 | UpdateLPVs() | LPVが有効な場合、LPVの更新を行う Reflective Shadow Mapsと直接光をボリュームに流し込む |
569 | for (int32 LightIndex = AttenuationLightStart;... | ここからシャドウあり or/and ライトファンクションありのライトごとの処理 |
592~631 | RenderShadowProjections() | ホワイトバッファへシャドウによる減衰を描画する シャドウはライティング計算時に行うのではなく、一旦フレームバッファと同サイズのホワイトバッファに描き込む また、半透明用ボリュームライトやハイトマップライトへの描画も行っている |
633~637 | HeightfieldLightingViewInfo.ComputeLighting() | ハイトフィールドのライティング計算 ハイトフィールドだけライティング結果をアトラステクスチャにレンダリングしてる |
640~657 | RenderPreviewShadowsIndicator() | ライトファンクションによる減衰をホワイトバッファに描き込む |
659~662 | CopyToResolveTarget() | ホワイトバッファのリゾルブ |
664~669 | InjectTranslucentVolumeLighting() | シャドウが有効でない場合はここで半透明用ライトボリュームへレンダリング |
674~677 | RenderLight() | 直接ライトのレンダリング |
ライトレンダリングはここまでです。
以降は再び DeferredShadingRenderer.cpp に戻ります。
行数 | コード片 | 内容 |
---|---|---|
1133 | InjectAmbientCubemapTranslucentVolumeLighting() | Ambient CubeMapを半透明用ライトボリュームへレンダリング |
1137 | FilterTranslucentVolumeLighting() | 半透明用ライトボリュームのフィルタリング 3x3x3のボックスフィルタ |
1142~1153 | ProcessLpvIndirect() | ライティング後のポストプロセス処理 現在はLPVの適用のみ |
1155 | RenderDynamicSkyLighting() | 動的スカイライトの描画 |
1159 | ResolveSceneColor() | ここまで描画したSceneColorのターゲットをリゾルブする 通常はRHIのリゾルブ命令を使用するが、モバイルエミュレートの必要がある場合は特殊処理 |
1162 | RenderDeferredReflections() | ポストプロセス的に環境リフレクションを適用する Screen Space Reflectionの計算もここで |
1169~1173 | ProcessAfterLighting() | リフレクション適用後のポストプロセス 現在はScreen Space Subsurface Scatteringのポストプロセス処理のみ |
1180~1185 | RenderLightShaftOcclusion() | ライトシャフトの遮蔽情報の描画 これがなくてもライトシャフトは有効化できるが、カメラ近くに遮蔽物があった場合の品質に差が出る |
1188~1212 | RenderAtmosphere() | Atomospheric Fogの描画 |
1217~1222 | RenderFog() | Height Fogの描画 ボリュームフォグもここで適用される |
1224~1236 | RenderPostOpaqueExtensions() | 不透明描画後の追加処理 ユーザがC++で指定できるレンダリング処理っぽい? ComputeShaderのDispatchもできるようだ |
1252~1261 | RenderTranslucency() | 半透明オブジェクトの描画 Separate Translucencyの場合は別バッファに描画して合成まで行う |
1267~1275 | RenderDistortion() | Refractionが有効なマテリアルを描画 |
1280~1286 | RenderLightShaftBloom() | ライトシャフトを描画する 遮蔽情報が描画されているかどうかで結果が異なる |
1288~1293 | RenderOverlayExtensions() | 多分ユーザが実行可能なレンダリングパス RenderPostOpaqueExtensionsと同様 |
1295~1303 | RenderDistanceFieldLighting() | DistanceField系のライティング処理(GIなど) |
1306~1318 | RenderMeshDistanceFieldVisualization() RenderStationaryLightOverlap() |
デバッグ用の情報ビジュアライズ メッシュDFとStationaryLightのオーバーラップ情報 |
1336~1341 | GPostProcessing.Process() | ポストプロセスの描画 後述 |
1361~1366 | RenderFinish() | 描画の終了処理 デバッグビジュアライズなど |
以上です。
ここからはポストプロセスです。
Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp (1262)
この場所からです。
行数 | コード片 | 内容 |
---|---|---|
1385~1451 | AddPostProcessDepthOfFieldGaussian() AddPostProcessDepthOfFieldCircle() AddPostProcessDepthOfFieldBokeh() |
被写界深度(DoF)の描画 BokehDoFの場合はSeparate Translucencyバッファとの合成もここで行う |
1453~1463 | RegisterPass(new(FMemStack::Get()) FRCPassPostProcessBokehDOFRecombine(bIsComputePass)) | BokehDoFではない場合はここでSeparate Translucencyを合成する |
1465 | AddPostProcessMaterial(Context, BL_BeforeTonemapping, SeparateTranslucency) | トーンマップ前のポストプロセスマテリアルを適用する |
1469~1482 | AddTemporalAA() | TemporalAAを適用する |
1484~1544 | FRCPassPostProcessMotionBlur() | モーションブラーを適用する [A Reconstruction Filter for Plausible Motion Blur]を改良したものを利用 スケール値を変更した2パス描画によってより高クオリティのブラーも適用できる r.MotionBlurSeparable に 1 を指定すれば有効になる |
1546~1559 | FRCPassPostProcessVisualizeMotionBlur() | モーションブラーとブルームのビジュアライズ |
1562~1572 | FRCPassPostProcessDownsample() | SceneColorバッファを半解像度バッファにダウンサンプリング |
1574~1609 | FRCPassPostProcessHistogram() | ダウンサンプルしたSceneColorを利用し、画面のヒストグラムを求める |
1612~1625 | CreateDownSampleArray() | ブルーム用に半解像度SceneColorから複数回のダウンサンプリングを行う Eye Adaptationが有効な場合はこの中でセットアップを行う |
1628~1653 | AddPostProcessBasicEyeAdaptation() AddPostProcessHistogramEyeAdaptation |
Eye Adaptationを実行する ブルームが無効の場合はここでダウンサンプルを行う |
1655~1756 | AddBloom() | ブルームを適用する レンズフレア、レンズブラーもこの関数内で行う |
1760~1790 | AddTonemapper() | トーンマップを適用する |
1792~1795 | AddPostProcessAA() | FXAAを適用する |
1797~1804 | FRCPassPostProcessVisualizeDOF() | DoFのビジュアライズ |
1808~1823 | AddGammaOnlyTonemapper() | フルポストプロセスでない場合、Separate Translucencyの合成と簡易トーンマッパーを適用する |
1826~1906 | FRCPassPostProcessVisualizeComplexity()など | 各種ビジュアライズなど |
1908 | AddPostProcessMaterial(Context, BL_AfterTonemapping... | トーンマップ後のポストプロセスマテリアルを適用する |
1910~1982 | FRCPassPostProcessSubsurfaceVisualize()など | 各種ビジュアライズなど |
1986~2025 | FRCPassPostProcessUpscale() | スクリーンパーセンテージが有効な場合はここでアップスケール |
以上となります。
おつかれさまでした。
通常、UE4を使う場合はこれらのパスの順序を意識する必要もないのですが、どうしても処理を入れ替えたい、無駄な処理を省きたいなどの場合にはソースコードを読まなければなりません。
本記事がそのような方の一助になれば幸いです。
明日は @negimochi さんで、「Sequencer 構造解説とカスタムトラック追加 (UE4.18版)」です。