10
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Unreal Engine (UE)Advent Calendar 2023

Day 15

[UE5]Lumenのチラチラの原因を調べてみる

Last updated at Posted at 2023-12-14

はじめに

こちら記事はUnreal Engine (UE) Advent Calendar 2023 の記事になります。
また、こちらはUE5.3.2のレンダリングの不具合をもとにした記事です。

結論を先に書いておくと、Githubなどから見ることの出来る最新のエンジンではここで紹介する問題は修正されています。
Epic Games Launcherからダウンロードできる最新の5.3.2ではまだ改善されていないので、調査の過程でどのようなことを行ったのか含め参考になれば幸いです。

環境

  • OS:Windows10
  • GPU:RTX2070
  • エンジンバージョン:UE 5.3.2
  • プロジェクト:建築インテリアレンダリング(Epic Games Launcharのサンプルから無料で入手可能)
    HighresScreenshot00000.png
    • ライトをディレクショナルライトのみに変更し明るさ調整
    • Reflection Captureの削除
    • Post Processs VolumeでGIとReflectionの計算方法をLumenに変更
      • 特にReflectionに関しては同じ設定にしないと問題を再現できない可能性が高いです。
        image.png

また、プロジェクトではハードウェアレイトレを使用しています。
image.png

Lumenのライティングがチラチラする

早速本題に入ります。先ほど紹介したプロジェクトでの一場面です。こちらの動画をご覧ください。
金属材質の花びんの表面にチラチラとした明るいノイズが確認できます。中央向って左側あたりが分かりやすいかも。
Jonathan2023.gif

カメラを動かしておらず、かつ見ているものが変わらないのにチラチラとしたノイズが見えるのは違和感を覚えます。

今回はこの原因と改善方法を探っていきます。

エディタでちらつきの原因を調べてみる

レンダリングの問題(今回で言えばチラチラノイズ)にぶつかった際にどんな問題なのかを調べる必要があります。
そこで今回はUE5のツールの一つであるGPUDumpという機能を使いレンダリングのデバックを行います。

GPUDumpを使ってみる

GPUDumpは1フレームのグラフィック処理の手軽なデバックが出来るツールです。サードパーティーの同じようなツールもありますが、今回はこちらを使います。

詳しい機能は上のドキュメントなどを見て頂ければと思いますが、ここではこちらを使用してチラチラノイズがどのような問題なのかを見つけていきます。
エディタでCTRL + Shift + / を押すとGPUDumpが実行されます。(暫く待つ必要があります。)
実行されるとプロジェクトのルートディレクトリのSaved/GPUDumpsに1フレームのキャプチャ結果が格納されます。また、出力が完了すると自動でこのダンプの場所がエクスプローラーで開きます。

結果を見てみましょう。
OpenGPUDumpViewer.batをダブルクリックするとWebブラウザなどでキャプチャした結果が見れるかと思います。
2023-12-09_15h07_26.png

こちらは上から下に向ってGPUの処理を確認することが出来ます。
image.png

それぞれのコマンドをポチポチをクリックしていくと、それぞれのステップでどのような描画が行われているかが確認できます。最初は調べるの時間がかかりますが気合です。
今回のチラチラとした問題は、LumenReflections->ReflectionResolveという箇所で確認できます。どうやら、このあたりの処理が怪しそうです。
image.png

LumenReflectionsで計算されているものはスぺキュラ反射というものです。
本当にチラチラノイズの問題がLumenのスぺキュラ反射なのかをエディタで確認してみましょう。
エディタのViewportのShowリストからスぺキュラ反射をオフにした表示ができます。
確認してみるとモヤモヤとしたノイズがでません。(スぺキュラ反射すべてがオフになるので真っ黒になります。)
確かにGPUDumpで確認した通りモヤモヤの原因はスぺキュラ反射で間違いなさそうです。
Editor_SpecularOff.gif

ここまでの調査で「今回のLumenのチラチラノイズ」=「Lumenのスぺキュラ反射処理の問題」であることが分かりました。
(一応すべてのチラチラしているノイズが「Lumenのスぺキュラ反射」が原因ではないので太字にしています。)

近くの処理も見てみる

せっかく出力したGPUDumpがあるので、試しに近くの処理の結果も見てみましょう。もしかしたら原因が分かるかもしれません。
LumenReflections->GenerateRaysという処理を見てみます。何やらカラフルのバッファーが表示されました。実はこのバッファーはスぺキュラ反射を計算する方向を示しています。別の言い方をすれば、ピクセルの色がどの方向からの光をスぺキュラ反射の計算に使用するかを表しているということです。

2023-12-09_15h36_46.png

拡大してみてみると、緑のピクセルが殆どを占めている中に幾つかポツポツと色の異なるピクセルが存在しています。先ほどピクセルの色はスぺキュラ反射の光を計算する方向を示していると言いました。つまり色が周囲と大きく異なるピクセルは、スぺキュラ反射を計算する際に周囲とは大きく違う方向からの光をスぺキュラ反射の光として計算しているということです。(この辺りはやや内容が難しいかもしれません。)
2023-12-09_15h37_46.png

もしも、緑色ピクセルが表す方向から来る光が暗くそれ以外のピクセルが表している方向から来る光が明るい場合を考えてみましょう。
スぺキュラ反射のライティングの結果として殆どが暗い結果の中に、一部明るいピクセルがまじりそうです。

先ほどのLumenReflections->ReflectionResolveの結果を見てみましょう。
考えた通り、殆どが暗い結果のピクセルの中に明るい結果が表れていて、チラチラノイズの原因になっていそうです。
2023-12-09_16h05_49.png

ここまででLumenのスぺキュラ反射がチラチラする原因が何となく特定出来てきました。
では、ここからは実際の処理を行っているシェーダー内部を見てみましょう。

原因をシェーダーから調べてみる

先ほどのDumpGPUの処理からLumenReflections->GenerateRaysに問題があることが分かりました。
ここからは実際のGPUの処理が書かれているシェーダーを見たり触ってこの問題の原因を調べていきます。

エンジンがインストールされているディレクトリ内のEngine/Shaders/の中を何かしらのコードエディタで検索してみます。

Engine/Shaders/はEpic Games Launcherからエンジンを落とすだけで見ることが出来ます。Githubなどからエンジンのソースを入手する必要はありません。

調べるのは先ほどの、スぺキュラ反射の方向を生成するGenerateRays処理が良さそうです。全検索などで調べてみると、LumenReflections.usf内にReflectionGenerateRaysCSという処理があります。この中身を見てみましょう。

詳細は割愛しますが、反射方向の計算はこちらで行われています。GGXSampleというところで方向をサンプルして、それをワールド空間での反射計算の方向に変換している処理になります。

LumenReflections.usf
void ReflectionGenerateRaysCS(uint GroupId : SV_GroupID,uint GroupThreadId : SV_GroupThreadID)
{    
    ...
    float4 GGXSample = ImportanceSampleVisibleGGX(E, Alpha, TangentV);
    float3 WorldH = mul(GGXSample.xyz, TangentBasis);
    RayDirection = reflect(CameraVector, WorldH);
    ...
}

この中のImportanceSampleVisibleGGXが物体のマテリアルの粗さをもとに方向を計算している関数になります。
ImportanceSampleVisibleGGXの中身を見てみましょう。こちらの関数の定義はMonteCarlo.ushにあります。

MonteCarlo.ush
float4 ImportanceSampleVisibleGGX(float2 E, float2 Alpha, float3 V)
{
	// stretch
	float3 Vh = normalize(float3(Alpha * V.xy, V.z));
 
	// "Sampling Visible GGX Normals with Spherical Caps"
	// Jonathan Dupuy & Anis Benyoub - High Performance Graphics 2023
	float Phi = (2 * PI) * E.x;
	float Z = lerp(-Vh.z, 1.0, E.y);
	float SinTheta = sqrt(saturate(1 - Z * Z));
	float X = SinTheta * cos(Phi);
	float Y = SinTheta * sin(Phi);
	float3 H = float3(X, Y, Z) + Vh;

	// unstretch
	H = normalize(float3(Alpha * H.xy, max(0.0, H.z)));

	return float4(H, VisibleGGXPDF_aniso(V, H, Alpha));
}

実装の詳細説明は割愛しますがこちらは、Jonathan Dupuy & Anis Benyoub 2023, "Sampling Visible GGX Normals with Spherical Caps"という論文をもとに実装されています。論文の中に実装例が紹介されているので転載します。

// Sampling the visible hemisphere as half vectors (our method)
vec3 SampleVndf_Hemisphere(vec2 u, vec3 wi)
{
     // sample a spherical cap in (-wi.z, 1]
     float phi = 2.0f * M_PI * u.x;
     float z = fma((1.0f - u.y), (1.0f + wi.z), -wi.z);
     float sinTheta = sqrt(clamp(1.0f - z * z, 0.0f, 1.0f));
     float x = sinTheta * cos(phi);
     float y = sinTheta * sin(phi);
     vec3 c = vec3(x, y, z);
     // compute halfway direction;
     vec3 h = c + wi;
     // return without normalization (as this is done later)
     return h;
}

変数の表記が違いますが、実装の中身を比較するとエンジン実装と論文実装とでZの結果が異なります。

エンジン実装のEが論文実装のuVhwiに相当します。

比較してみると

//エンジン実装
Z = lerp(-Vh.z, 1.0, E.y) = E.y - Vh.z - Vh.z*E.y

//論文実装
z = fma((1.0f - u.y), (1.0f + wi.z), -wi.z) = 1.0 - u.y - wi.z*u.y

このような違いです。エンジン側の実装を以下のように変更すると論文実装と同じ結果になります。

//エンジン実装(変更後)
float Z = lerp(1.0, -Vh.z, E.y); //変更前 : float Z = lerp(-Vh.z, 1.0, E.y);

ソースの変更を保存してエディタでCTRL + Shift + .を行うとシェーダーのコンパイルが行われます。暫くコンパイルを待って描画の比較をしてみましょう。

描画の比較

変更前
Jonathan2023.gif

変更後
Heitz2018.gif

違いが分かるでしょうか?分かりづらいかもしれませんが 金属の花びんの中央向って左の部分を見るとチラチラとしたノイズが無くなっていることが分かります。多少の改善にはなったのではないでしょうか。

また、GPUDumpで反射方向のバッファーも見てみるとピクセルの色のエラーが無くなっています。
image.png

こちらの修正は冒頭でも言ったとおりGithubなどから見ることのできる最新のエンジンソースでも同様の修正が行われています。恐らくUE5.4などの後のバージョンでは反映されていると思われます。

何か問題に遭遇した場合に最新のエンジンソースを見ると実は解決しているかもしれません。自身で調査するよりも速く解決できるかもしれないので、そちらを確認することも重要かと思います。

最後に

レンダリングの気になる箇所を見つけ、GPUDumpなどのデバック機能を使い問題点を見つけ、シェーダーを改善することでレンダリング品質を改善するという流れが紹介できたかなと思います。

実際のゲーム開発などでは、より詳しい調査が必要な場面や原因が分かりづらい問題が殆かと思います。この記事は最初の一歩の一つの例として参考になれば幸いです。

10
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?