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

Unreal Engine
目次
  1. 水ノ茉(こおり)の宣伝
    1. 同人作品の宣伝
    2. プラグインの宣伝
  2. 始まり
  3. シェーディングモデルとは?
  4. エンジン改造とは?
    1. 前髪を透かしたり
    2. 毛先まで綺麗に表現できるポストプロセスなアウトラインを描いたり
    3. キャラと背景を別々のシャドウマップに書き込んでセルフシャドウを抑制したり
    4. 髪影を落としたり
    5. 頂点IDノードを追加して、それを元に鼻に線を引いたり
    6. 独自のトゥーンパス群ですべて描いたり
  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/RenderCore/Public/ShaderCore.h
    6. Engine/Source/Runtime/RenderCore/Private/ShaderCore.cpp
    7. Engine/Source/Runtime/Engine/Private/Materials/MaterialShader.cpp
    8. Engine/Source/Runtime/RenderCore/Public/ShaderMaterial.h
    9. Engine/Source/Runtime/Engine/Private/ShaderCompiler/ShaderGenerationUtil.cpp
    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/ReflectionEnvironmentPixelShader.usf
    15. Engine/Shaders/Private/ScreenSpaceDenoise/SSDMetadata.ush
    16. Engine/Shaders/Private/ShadingCommon.ush
    17. Engine/Shaders/Private/ShadingFurnaceTest.usf
    18. Engine/Shaders/Private/VirtualShadowMaps/VirtualShadowMapPageMarking.usf
    19. Engine/Shaders/Private/VirtualShadowMaps/VirtualShadowMapProjection.usf
    20. Engine/Shaders/Private/DeferredLightPixelShaders.usf
    21. Engine/Shaders/Private/HeterogeneousVolumes/HeterogeneousVolumesLiveShadingUtils.ush
    22. Engine/Shaders/Private/Histogram.usf
    23. Engine/Shaders/Private/Lumen/LumenMaterial.ush
    24. Engine/Shaders/Private/Lumen/LumenShortRangeAOHardwareRayTracing.usf
    25. Engine/Shaders/Private/PlanarReflectionShaders.usf
    26. Engine/Shaders/Private/PostProcessEyeAdaptation.usf
    27. Engine/Shaders/Private/SSRT/SSRTDiffuseIndirect.usf
    28. Engine/Shaders/Private/VolumetricCloud.usf
    29. Engine/Shaders/Private/VolumetricFogVoxelization.usf
  9. おわり!!!
  10. 雑談
  11. エロゲバフを使い切った感
  12. 共通ルート編
  13. SDFテクスチャ作成ツールの準備

水ノ茉(こおり)の宣伝

叡智でえっちな同人作品を予定中…

Ci-en R18

同人作品の宣伝

  • 準 備 中 . . .

プラグインの宣伝

  • 準 備 中 . . .
  • 準 備 中 . . .

始まり

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

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

シェーダーです。

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

エンジン改造とは?

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

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

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

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

前髪を透かしたり

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

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

髪影を落としたり

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

独自のトゥーンパス群ですべて描いたり

Substrate (旧: Strata) 非対応

セル・トゥーンのルック開発で超現実的なライティング計算をサポートしたSubstrateを併用するケースが無さ過ぎるので、当然の如く非対応です。

少なくとも筆者はこれまで一度も遭遇したことないです。

レイトレース 非対応

RayTracingフォルダに含まれる少なくないファイルに手を入れる必要があり大変面倒なため非対応とします。

リポジトリ

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

実装

前置きが思い付かないので実装を進めていきます。

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

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

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

尚EngineTypes.hを書き換えると長めのエンジンビルドが要求されるため、名称は先に決めておいた方が幸せと思います。

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

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

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

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

githubで実装を見る(L37-L43)

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

シェーディングモデルにToonLitを選択したマテリアルは、シェーダー側に#define MATERIAL_SHADINGMODEL_TOON_LIT 1という定義を書き込む?埋め込む?ようにしている箇所です。要はバリエーション立てているですね。

githubで実装を見る(L2213-L2217)

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

HLSLMaterialTranslatorと同様です。

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

どっちが新でどっちが旧か、忘れました。

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

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

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

githubで実装を見る(L626-L630)

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

ToonLitのStatですね。

githubで実装を見る(L83)

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

ToonLitのStatですね。

githubで実装を見る(L100)

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

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

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

githubで実装を見る(L118)

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

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

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

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

githubで実装を見る(L257-L260)

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

変数定義、特に無し。

githubで実装を見る(L107)

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で実装を見る(L145)

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

これは次々回あたりにGBuffer書き込みをすることになるので、まぁ対応面倒だし、今のうちに入れておけって感じのやつです。

仮に書き込んでもフェッチしなければ意味のない行為なので、そこまで気にしなくていいと思います。

githubで実装を見る(L1683-L1685)

githubで実装を見る(L1806-L1809)

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で実装を見る(L124-L126)

Engine/Shaders/Private/ReflectionEnvironmentPixelShader.usf

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

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

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

githubで実装を見る(L360-L364)

Engine/Shaders/Private/ScreenSpaceDenoise/SSDMetadata.ush

汎用的なScreenSpaceDenoiserもUnlitと同じ挙動に合わせます。

githubで実装を見る(L45-L49)

Engine/Shaders/Private/ShadingCommon.ush

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

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

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

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

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

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

通常
デバッグ描画

githubで実装を見る(L73)
githubで実装を見る(L93)

Engine/Shaders/Private/ShadingFurnaceTest.usf

シェーダーコンパイルが正常に通過するかのテストコード部分なので書いても書かなくても大丈夫です。

githubで実装を見る(L77-L81)

Engine/Shaders/Private/VirtualShadowMaps/VirtualShadowMapPageMarking.usf

VSMの挙動をUnlitと同様にしています。

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

Engine/Shaders/Private/VirtualShadowMaps/VirtualShadowMapProjection.usf

VSMの挙動をUnlitと同様にしています。

githubで実装を見る(L72-L76)

Engine/Shaders/Private/DeferredLightPixelShaders.usf

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

githubで実装を見る(L316-L318)

Engine/Shaders/Private/HeterogeneousVolumes/HeterogeneousVolumesLiveShadingUtils.ush

githubで実装を見る(L31-L32)
githubで実装を見る(L60-L61)

Engine/Shaders/Private/Histogram.usf

githubで実装を見る(L331-L335)

Engine/Shaders/Private/Lumen/LumenMaterial.ush

Lumenの処理は割とIsValid関数の結果を格納して、それを元に計算処理に侵入するか否かしているので、ここでUnlitと同様に切っておくと大体は対応完了しちゃいます。

汎用的な実装はこういう時に便利ですね。

反面、分岐処理を組み込みたい場合は地獄を見ますが。

githubで実装を見る(L445-L448)
githubで実装を見る(L485-L489)
githubで実装を見る(L537-L541)

Engine/Shaders/Private/Lumen/LumenShortRangeAOHardwareRayTracing.usf

Lumenさん、ソフトウェアとハードウェアレイトレの両対応なの、頑張ってますよね。

プラットフォームに依存するから対応必須でしょという現実問題はさておき、面倒なことを頑張っているのは凄いと思います。

新規実装する前に安定性をどうにかしろと常々思うので、評価と批判で結局の印象はプラマイ、マイナスです。嫌い。

githubで実装を見る(L37-L41)

Engine/Shaders/Private/PlanarReflectionShaders.usf

平面反射、当然重いのですが、綺麗ですよね。

まぁ、切っちゃいますが。

githubで実装を見る(176-L177)

Engine/Shaders/Private/PostProcessEyeAdaptation.usf

たしか露出まわりでしたっけ?

セル・トゥーンルック開発と主に戯れている筆者さんは、自動露出を切りがちなので、深掘りする機会があんまりありません。

経験値を積みたいという意味では、海外産ゲームに多いリアル調なルックデヴも興味ありますね。

githubで実装を見る(L499-L503)

Engine/Shaders/Private/SSRT/SSRTDiffuseIndirect.usf

Screen Space RayTracingの略でSSRTだったかな。

Unlitは材質存在しないのと同等なので、反射系が考慮されているリアル調に特化したUEさん、結構影響範囲が広いのよね。怠い。

githubで実装を見る(L286-L290)

Engine/Shaders/Private/VolumetricCloud.usf

Volumetricは当然密度計算とかが影響しますからね。

計算要素の無いUnlitはSSRT同様に無効化しちゃいましょう。

githubで実装を見る(L95-L96)
githubで実装を見る(L124-L125)

Engine/Shaders/Private/VolumetricFogVoxelization.usf

同文。

githubで実装を見る(L37-L38)
githubで実装を見る(L66-L67)

おわり!!!

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

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

今回は律儀にVSMやLumenなどにUnlitと同様の計算手順を追うようにしちゃったので、長くなっちゃいましたが。

でもアーカイブとしては上出来でしょう。

未来の私は大体、記憶喪失してますからね。

雑談

汎用的な改造については、今後、気が乗ったタイミングでバージョンごとに書いていく予定です。

今回はアウトラインのエンジン改造編の前提実装として、シェーディングモデルとマテリアルピンとGBufferレイアウトカスタムが必要だったので、タイミングが合いました。

エロゲバフを使い切った感

ちょっと火力を出し過ぎてMPならぬエロゲが枯渇しました。EPって略すとそれっぽいね。ふふ。

トウリさんとくくるさんのお陰で3週間ぐらいバフが継続しました。奏命様と蓼科さんも勿論素敵でしたけどね。

さーて、シーラブでも遊ぶか。

手持ちのバフはまだまだありますからね。ふふふ。

エロゲするたびに休暇を取ったりキリがないので、毎日1時間ぐらい遊んで分散させましょうか。エロゲ成分を少量接種です。用法容量を守って楽しく摂取しましょう、ってね。

やっぱり素敵な物語と可愛くてカッコよくて素敵な方々を同時に摂取できるエロゲは最高のコンテンツなのです。

共通ルート編

隠れてえっちする物語ってことでおっけいかな?(事前知識皆無)

声漏れそうで漏れないっていいよね。わかる。

あーだめだ、書く時間を弁えないと性癖さんが出てくる。

読み上げツール準備よーしっ。

ん?フォントの初期設定がMS ゴシックじゃないって?
句点がドットに誤分類されたから、安定性が一番高いモトヤLマルベリに変えちゃいました。

んじゃ、プライベートタイムは、コード書きながらエロゲ流しますわ。

オートモードの時の待機時間いいですね。

読み上げ速度を爆速にしなくても最後まで読める。

すごい助かるわ。

シークレットラブ(仮)のネタバレを含みます。

おおおおおおい。

またかああああ。

ヒロイン視点の台詞表記、これじゃなあああああい。

(名前)
「テキスト」

こう!!!

たのむ!!!

もおおおおおおお。

ふんす。

私も同感です。特にないなら一旦大卒取っておいた方が無難な気がする。

うちの場合は早く固定給が欲しかったから、専門行ったけど。あとシンプルに頭悪すぎて試験とか無理だった。

『あなたの』っていうかな、『キミ』じゃない?学生なら、というかキャラ的に。うーん。

あーほらー、『キミ』呼びしたじゃん。

テキストに起こすと直前まで連続して『キミ』が出てくるから、『あなた』に変えた方が並び的には綺麗だし会話の雰囲気にあった呼び方ではあるけど、口語だとやっぱり敬称変えるのはなんか違和感あるよな。

こういうの引っ掛かる時とスルーしちゃう時、なんなんだろうね。自分でもよく分からない。気分とか集中度合いなんだろうけど。

――なんか、生温いな。

ゲーム画面と向き合ったらなんか物足りない気がするけど、流す分には適切ですね。

『幼馴染は負けヒロイン』という圧倒的な様式美。

なんかこういうタイトルの作品ありそう。語呂いいし。

セレオブの時も書いたけど、妹枠や幼馴染枠って気持ちを溜めていた分、解放した時のデレ甘具合がまーじで最高過ぎて見てて幸せ過ぎる。

SDFテクスチャ作成ツールの準備

CSでSDFテクスチャを作成するための技術検証です。

まずはUVバッファを渡して展開できるか。

できた。

次はラインを引いてみる。

できた。

残りは適当にスキャンラインしてローカル座標を一緒に求めれば計算に必要な要素は揃うかな?

SDF計算どうしようかな。

面倒だし以前作ったやつでいいか。

データ残ってるかな。

よかった、あった。

久々に触ったけどFacial proportionsを参考にパラメータを作ったから結構分かりやすいな。

当時の僕、流石ですわ。

ベースの形状はこれを使って描いて、そこにSDF Combinationで追加したり、マスクしたりが使い勝手いいかな。

この形状作成計算、我ながらいい出来とは思ったんだけど、当然ながら全角度で共通のパラメータだから、特定の視点で理想的な数値にし過ぎると、ある角度から破綻し始めたりするから地味に扱いに困ってたのよね。SDFテクスチャとしてベイクしちゃえば、それを解消しつつ理想的な曲線を描けるので、いいとこどりですね。ふふ。過去の私の成果物を引き継いで上げますわ。