【UE5】シェーディングモデルを追加してみた – Unreal Engine 5.4

Unreal Engine
目次
  1. 水ノ茉の宣伝
  2. 始まり
  3. シェーディングモデルとは?
  4. エンジン改造とは?
    1. 前髪を透かしたり
    2. 毛先まで綺麗に表現できるポストプロセスなアウトラインを描いたり
    3. キャラと背景を別々のシャドウマップに書き込んでセルフシャドウを抑制したり
    4. 髪影を落としたり
    5. 頂点IDノードを追加して、それを元に鼻に線を引いたり
  5. Substrate (旧: Strata) 非対応
  6. レイトレース、パストレースなどは非対応
  7. リポジトリ
  8. 実装
    1. Engine/Source/Runtime/Engine/Classes/Engine/EngineTypes.h
    2. Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionShadingModel.h
    3. Engine/Source/Runtime/Engine/Private/Materials/HLSLMaterialTranslator.cpp
    4. Engine/Source/Runtime/Engine/Private/Materials/MaterialHLSLEmitter.cpp
    5. Engine/Source/Runtime/Engine/Private/Materials/MaterialShader.cpp
    6. Engine/Source/Runtime/Engine/Private/ShaderCompiler/ShaderGenerationUtil.cpp
    7. Engine/Source/Runtime/RenderCore/Private/ShaderCore.cpp
    8. Engine/Source/Runtime/RenderCore/Public/ShaderCore.h
    9. Engine/Source/Runtime/RenderCore/Public/ShaderMaterial.h
    10. Engine/Source/Editor/PixelInspector/Private/PixelInspectorDetailsCustomization.cpp
    11. Engine/Source/Editor/PixelInspector/Private/PixelInspectorResult.cpp
    12. Engine/Source/Editor/PixelInspector/Private/PixelInspectorResult.h
    13. Engine/Shaders/Private/Definitions.usf
    14. Engine/Shaders/Private/PostProcessGBufferHints.usf
    15. Engine/Shaders/Private/ReflectionEnvironmentPixelShader.usf
    16. Engine/Shaders/Private/PlanarReflectionShaders.usf
    17. Engine/Shaders/Private/ShadingCommon.ush
    18. Engine/Shaders/Private/DeferredLightPixelShaders.usf
    19. シェーダー対応諸々
  9. おわり!!!
  10. 関連するエンジン改造
  11. 雑談
  12. お洋服

水ノ茉の宣伝

準備中...
ゲームを作る予定なの
水ノ茉こおり

始まり

シェーディングモデルを追加するエンジン改造をしていきます。

シェーディングモデルとは?

シェーダーです。

シェーディングモデルとライティング関数が紐付けされているので、独自のライティングをしたい場合は、シェーディングモデルを追加する必要があります。

エンジン改造とは?

ゲームエンジンのコードを書き換える行為をエンジン改造と呼びます。

尚エンジン改造という呼称が正式かは知らんです。

これ以上の真面目な説明はググればヒットするので割愛します。

以下、実際の描画まわりの改造事例です。

前髪を透かしたり

毛先まで綺麗に表現できるポストプロセスなアウトラインを描いたり

キャラと背景を別々のシャドウマップに書き込んでセルフシャドウを抑制したり

髪影を落としたり

頂点IDノードを追加して、それを元に鼻に線を引いたり

Substrate (旧: Strata) 非対応

セル・トゥーンのルック開発で超現実的なライティング計算をサポートしたSubstrateを併用するケースが無さ過ぎるので、当然の如く非対応です。少なくとも筆者はこれまで一度も遭遇したことないです。

レイトレース、パストレースなどは非対応

セル・トゥーンのルック開発でレイトレやパストレースを使うこともハード性能次第では稀にありますが、あまりにも商用的な内容のため非対応(非公開)です。

リポジトリ

最新バージョンのみ保守しています。
過去バージョンが動かないと言われても基本的には知らんがなスタイルです。

実装

シェーディングモデルの追加は、専用のライティングをしたり、ポストプロセスでシェーディングモデルIDを元に特殊な処理をしたい場合に必要です。

UnrealEngineはリアル調が主戦場なので、セル・トゥーン調なルック開発においては、既存でやりくりするより、シェーディングモデルを追加して魔改造した方が、処理負荷的にも表現力的にも都合が良いです。

Engine/Source/Runtime/Engine/Classes/Engine/EngineTypes.h

シェーディングモデルの列挙体に追加しています。

DisplayNameがリストボックスの表示名に、/** ~~~ */がカーソルを合わせた際の説明表記・ツールチップに該当します。

githubで実装を見る(L637-L638)

Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionShadingModel.h

Shading Modelノードから選択可能なシェーディングモデルにToonLitを追加しています。

Shading Modelノードを使う機会に巡り合ったことないので用途と需要が分からない子です。

githubで実装を見る(L38-L44)

Engine/Source/Runtime/Engine/Private/Materials/HLSLMaterialTranslator.cpp

シェーディングモデルにToonLitが含まれるマテリアルは、シェーダー側に#define MATERIAL_SHADINGMODEL_TOON_LIT 1が書き込まれるように設定している箇所です。要はバリエーションですね。

5.4からはシェーディングモデルの有無をMaterialからではなく、EnvironmentDefinesから取得するようにしてコンパイルとの齟齬を最小限に抑えているっぽいですね。元々この辺りはかなり安全な設計だったと思うのですが、より強固になった感じでしょうか。

githubで実装を見る(L2644-L2647)

Engine/Source/Runtime/Engine/Private/Materials/MaterialHLSLEmitter.cpp

HLSLMaterialTranslatorと同様です。

違いはシェーダーコンパイル処理に新旧バージョンがあり、そのバージョン次第でTranslatorとEmitterが使い分けられている感じです。どっちが新でどっちが旧か、忘れました。

興味のある方はブレークポイントを置いてみてくださいな。

Unreal Engineは基礎部分はしっかり組み込まれているので、わざわざ全確認しなくても動作しちゃうので、把握がおろそかになりがちなんですよね。

てか、1回は追った記憶があるんだけど、流石に数か月前の記憶で且つ記録にも残していないから忘れちゃった。

githubで実装を見る(L584-L588)

Engine/Source/Runtime/Engine/Private/Materials/MaterialShader.cpp

シェーディングモデルを文字列で返す関数です。

名目上は説明ですが、意味合い的にはTooltipではなく、列挙体をそのまま文字列化したモノという方が強いようです。

githubで実装を見る(L118)

なんとなくUnlitとDefaultLitとToonLitでStatを分割して見たのですが、もしかしたら別途追加対応が必要かもしれないので、無難にDefaultLitを使うのがいいのかもと、今は思います。

個人開発環境だと全キャラをこのシェーディングモデルで描画する予定なので、その布石として用意しました。

用意しただけで使用経験ないんですよね。

ぶっちゃけドローコール単位の負荷はStatでざっくり見るよりプロファイラツールで詳細確認した方が確実なので。

githubで実装を見る(L259-L262)

Engine/Source/Runtime/Engine/Private/ShaderCompiler/ShaderGenerationUtil.cpp

FShaderCompilerEnvironmentMATERIAL_SHADINGMODEL_TOON_LITが含まれる場合は、その結果をFShaderMaterialPropertyDefinesに伝播しています。

冗長的な処理に思えますが、おそらくGameとRenderスレッドで競合しないようにしている関係なのかな。

ブレークポイント張ってまで確認するのは面倒なので事例で語りますが、シェーダーコンパイルは別スレッドで動かしていることが多いです。

例を出すと、NiagaraのGPUシミュレーション系のコンピュートシェーダーは、ゲームスレッドでコンパイルの必要性を判断した結果をNiagara Systemに格納して、Niagara ModuleのWorldTickでSystem全収集してコンパイル必要なものは別のスレッドというかキューに突っ込んでコンパイルしてもらって、その結果を受け取って・・・みたいにGameとRenderスレッドを行き来しています。

githubで実装を見る(L132)

シェーダー側の挙動はUnlitに寄せていますが、GBufferの書き込みはDefaultLitに寄せています。

これは次々回あたりにGBuffer書き込みをすることになるので、まぁ対応面倒だし、今のうちに入れておけって感じのやつです。仮に書き込んでもフェッチしなければ意味のない行為なので、そこまで気にしなくていいと思います。

githubで実装を見る(L1742-L1744)

5.4からはVelocityはReadとWriteでそれぞれ判定してより安全性を高めたようです。UEはバリエーション次第ではBasePassでGバッファと一緒に速度書き込みが出来ちゃうので、衝突の危険性が秘められていたのでしょう。

githubで実装を見る(L1894-L1897)

Engine/Source/Runtime/RenderCore/Private/ShaderCore.cpp

ToonLitのStatですね。

githubで実装を見る(L111)

Engine/Source/Runtime/RenderCore/Public/ShaderCore.h

ToonLitのStatですね。

githubで実装を見る(L95)

Engine/Source/Runtime/RenderCore/Public/ShaderMaterial.h

変数定義、特に無し。

githubで実装を見る(L110)

Engine/Source/Editor/PixelInspector/Private/PixelInspectorDetailsCustomization.cpp

PixelInspectorの対応です。

RenderDoc&PIX派なので特にコメントないです。

githubで実装を見る(L168)

Engine/Source/Editor/PixelInspector/Private/PixelInspectorResult.cpp

PixelInspectorの対応です。

githubで実装を見る(L271-L272)

githubで実装を見る(L321)

Engine/Source/Editor/PixelInspector/Private/PixelInspectorResult.h

PixelInspectorの対応です。

githubで実装を見る(L29)

Engine/Shaders/Private/Definitions.usf

先ほどHLSLMaterialTranslator/EmitterでToonLitを使用している場合は定義が立てられると書きました。

では、ToonLit以外を選択した場合、その定義はどうなるでしょうか?

答えは#ifndef、つまり未定義です。

使用箇所の全てで#if definedをするのも面倒なので、未定義の場合は#define MATERIAL_SHADINGMODEL_TOON_LIT 0となるようにしています。

githubで実装を見る(L117-L119)

Engine/Shaders/Private/PostProcessGBufferHints.usf

マウスカーソルの位置のGバッファを可視化するデバッグ機能のToonLitの対応です。

デバッグ機能は r.PostProcessing.GBufferPicking 1 で有効化されます。

githubで実装を見る(L142)

Engine/Shaders/Private/ReflectionEnvironmentPixelShader.usf

Unlitと同様に環境光(反射とか)の適用を無効にしています。

シェーディングモデルの追加が主軸であり、追加したシェーディングモデルの利用用途は各々に任せたいので、一番汎用性の高いUnlitに寄せた対応を初期としています。

SSRTやRayTracing、VSMなども可能な限りUnlitと同挙動を目指していますが、全チェックは怠くてしていないので、見逃しはあるかもしれないです。

githubで実装を見る(L509-L513)

Engine/Shaders/Private/PlanarReflectionShaders.usf

こっちは平面反射の方です。

githubで実装を見る(L175-L181)

Engine/Shaders/Private/ShadingCommon.ush

ToonLitの定義を記述しています。

ナンバリングはEngineTypes.hと同値にしてくださいね。

githubで実装を見る(L34-L39)

ToonLitのシェーディングモデルカラーを設定しています。

主にデバッグで使用する際に呼ばれる関数ですね。

黄色っぽくしています。好きな色に変えて頂いて大丈夫です。

通常
デバッグ描画

githubで実装を見る(L73)

githubで実装を見る(L93)

Engine/Shaders/Private/DeferredLightPixelShaders.usf

Unlitと同様にディファードライティングせずにブチ切りスタイルです。

githubで実装を見る(L340)

シェーダー対応諸々

適当にUnlitと同等のフローを辿るようにしています。

動作確認はしていないので作業箇所の参考程度に留めてください。

githubで実装を見る

おわり!!!

描画まわりのエンジン改造の基礎、入門と言っても過言ではないシェーディングモデルの追加でした。

実はシェーダーの対応をサボっちゃえば大したことない変更量なんですよね。

関連するエンジン改造

雑談

ReinCarnationという名称はエンジン改造の検証用のプロジェクト名なのです。

面倒だからゲーム開発用のプロジェクトでもそのリポジトリを流用してたんだけど、プロジェクトに沿った描画機能や最適化をする必要が出てきたので専用リポジトリを用意したの。

そのついでに5.4のエンジン改造の纏めという流れです。

実装自体は5.5が公開されたタイミングあたりで解禁していたんだけど、記事に落とし込むのは気が向かなかったんだよね。

ちなみに5.5の改造方法や変更点も当然把握していますが、公開するのは5.6がリリースされた頃になります。

エンジン改造って別に簡単なんだけど、商業以外では必要性が低いことが多いから情報量が極端に少ないんだよね。

筆者がエンジン改造を触り始めた頃は4.25あたりなんだけど、当時マジで碌に記事がなくて、ひたすらエンジンコードを読むというバカ苦行をしたのです。

それ自体は勉強になるから有益なんだけど、業務においては時間の無駄としか思えんので、少しだけお裾分けなのです。

お洋服

以前登場してもらったトウリさんに雰囲気が似ている方のお洋服をえちえちにしてみた。

元々は濡れ表現を作ってたんだけど、その副産物として生まれたの。衣類のシワを綺麗に作れたら後は透かすだけなんだけど、プロシージャルな設計って地味に面倒なの。でもDCCツールを使うのはもっと面倒だからせめぎあい。作ろうとしているものは全年齢だけど、雨に濡れたら衣類は濡れるよね。透けるよねという合法的なえち。たのしい。