【UE5】マテリアルピン(アウトラインマスクとカラー)を追加してみた – Unreal Engine 5.4

Unreal Engine
目次
  1. 水ノ茉の宣伝
  2. 始まり
  3. マテリアルピンとは?
  4. エンジン改造とは?
    1. 前髪を透かしたり
    2. 毛先まで綺麗に表現できるポストプロセスなアウトラインを描いたり
    3. キャラと背景を別々のシャドウマップに書き込んでセルフシャドウを抑制したり
    4. 髪影を落としたり
    5. 頂点IDノードを追加して、それを元に鼻に線を引いたり
    6. 独自のトゥーンパス群ですべて描いたり
  5. Substrate (旧: Strata) 非対応
  6. リポジトリ
  7. 実装
    1. Engine/Source/Runtime/Engine/Public/SceneTypes.h
    2. Engine/Source/Runtime/Engine/Classes/Materials/Material.h
    3. Engine/Source/Runtime/Engine/Private/Materials/Material.cpp
    4. Engine/Source/Runtime/Engine/Private/Materials/MaterialAttributeDefinitionMap.cpp
    5. Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpressionMakeMaterialAttributes.h
    6. Engine/Source/Runtime/Engine/Private/Materials/MaterialCachedData.cpp
    7. Engine/Source/Runtime/Engine/Private/Materials/MaterialExpressionHLSL.cpp
    8. Engine/Source/Runtime/Engine/Private/Materials/MaterialExpressions.cpp
    9. Engine/Source/Runtime/Engine/Private/Materials/MaterialHLSLEmitter.cpp
    10. Engine/Source/Runtime/Engine/Private/Materials/HLSLMaterialTranslator.cpp
    11. Engine/Source/Editor/MaterialEditor/Private/MaterialEditor.cpp
    12. Engine/Source/Editor/UnrealEd/Private/MaterialGraph.cpp
    13. Engine/Source/Editor/UnrealEd/Private/MaterialGraphNode_Root.cpp
    14. Engine/Shaders/Private/MaterialTemplate.ush
  8. 簡単な動作確認
  9. おわり!!!
  10. 関連するエンジン改造
  11. UMaterialExpressionBreakMaterialAttributes::Serializeで気になっていたことをようやく調べたの
  12. ゆっくりと ゆっくりと▼ ゆっくりと ゆっくりと▼

水ノ茉の宣伝

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

始まり

マテリアルピンを追加するエンジン改造をしていきます。

マテリアルピンとは?

Base ColorやMetallicなどの最終的な計算結果を接続する部分のことをマテリアルピンと呼びます。

実装を覗いてみるとマテリアルプロパティやインプットという名称も使われているので、またしても正式名称は知らんです。

相手に伝わるレベルの言葉の揺れは許してね。

パラメーターをシェーダーに渡したい場合は、マテリアルピンを追加する必要があります。

独自のコンスタントバッファを追加すればいいじゃんと思うかもしれませんが、Unreal Engineでそのやり方はまぁまぁな手間を要するので、一般的には実装までの導線が最低限確保されているマテリアルピンの追加が選ばれます。

エンジン改造とは?

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

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

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

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

前髪を透かしたり

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

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

髪影を落としたり

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

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

Substrate (旧: Strata) 非対応

Substrateはマテリアルピンの構成が異なり内部でピンの再接続が行われています。

再接続部分を対応していないので、Substrateは非対応という感じです。

リポジトリ

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

実装

アウトラインマスクとアウトラインカラーを格納するピンを追加していきます。

アウトラインマスクの型はfloat、アウトラインカラーの型はfloat3を想定します。

Engine/Source/Runtime/Engine/Public/SceneTypes.h

マテリアルプロパティの列挙体にアウトラインマスクとアウトラインカラーを追加します。

MaterialPropertyの頭文字を取ってMPですね。

githubで実装を見る(L199-L202)

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

ピンの接続関係なを保持する変数です。

型によって使用するFMaterialInput構造体が異なります。

カラーはベクターとかでもたしか問題なかった気がしますが、精度がfloatとuintで面倒なことになるので、基本的にはカラーの方を使います。

githubで実装を見る(L404-L408)

DEPRECATEDの対応は不要と思いますがメモ程度に書いています。

githubで実装を見る(L2039-L2043)

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

static_assertの対応です。

要素の増減によってはシリアライズの互換性が無くなるため、static_assertで「対応忘れずにね♪」というスタイルだと思います。

githubで実装を見る(L2841-L2845)

非推奨な要素からお引越ししている箇所ですが、先ほどと同様に対応不要です。

書いておいても特に影響が見られなかったのでコメントアウトせずに直書きメモなのです。

githubで実装を見る(L3858-L3859)

これも上記同様ですね。BreakMaterialAttributesノードが主にバージョンアップによって並び順に破壊的な変更が発生した際に、互換性の処理を行うのですが、中身の実装をしていないのでハリボテです。この辺りはエンジンのバージョンアップによって場合によっては発生する作業なのでかなり業務寄りです。

githubで実装を見る(L3894-L3895)

マテリアルピンの接続を取得する関数です。

ツールチップ的な説明ではなく、構成要素の型とかの内部情報の説明ですね。

githubで実装を見る(L5963-L5964)

ピンに刺さっているノードのコンパイルまわりです。

接続されている場合はそれが展開されて、未接続の場合はFMaterialAttributeDefintion::CompileDefaultValueに飛ばされて任意の初期値が適用されます。

githubで実装を見る(L6578-L6579)

ピンの有効性です。

シェーディングモデルがToonLitの場合のみ有効、それ以外はグレーアウトするように指定しています。

githubで実装を見る(L7235-L7238)

UMaterialEditorOnlyDataのコンストラクタは5.3では一部で且つ決め打ちな大雑把な対応でしたが、5.4からはアトリビュートから共通の初期値・デフォルト値を取得するように改修されたようです。

適当にToFColorSRGBにしていますが、リニア運用の場合はToFColor(false)に変えてくださいね。

githubで実装を見る(L7483-L7484)

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

先ほど触れたピン未接続時の初期値を指定しています。

アウトラインマスクはマスクをしないが初期値なのでCompiler->Constant(1.0f)を指定、アウトラインカラーは黒色が初期値なのでCompiler->Constant3(0.0f, 0.0f, 0.0f)を指定しています。

Compiler引数からシェーディングモデルの情報が取得可能なため、条件に応じて初期値を変えることもできます。

githubで実装を見る(L74-L84)

アトリビュートマップに追加したマテリアルプロパティを登録しています。

左からGUIDが個体識別、表示名等、紐付けするマテリアルプロパティ、型の種類、初期値、それを使用するシェーダーステージって感じです。

他にもデフォルト引数で隠れている要素もあるので、覗いてみると面白いかもです。

そしてシェーダーステージを指定している通り、頂点シェーダーで扱うピンを追加する場合はまた違う種類を指定しないとなのですよね。

地味に面倒ですが、マテリアルエディタという面倒なシステムに対応する都合上、これくらい明示的な方がいいんでしょうね。

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

これもアトリビュート関連ですね。

LOCTEXTはたしか文字列のキャッシュなので頻繁に使う場合メモリに載せておいて確保と解放の手間を省くとかだった気がします。

間違えてたらすまん。

githubで実装を見る(L463-L466)

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

ピンの接続関係などを保持する変数のMakeMaterialAttributesノード版です。

ちなみに使ったことないです。

githubで実装を見る(L78-L82)

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

MakeMaterialAttributesノードに接続されているピンの更新まわりです。

githubで実装を見る(L527-L528)

上記同様です。

githubで実装を見る(L722-L723)

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

MakeMaterialAttributesノード関連です。

使わないから語りたいことがない件について。

githubで実装を見る(L3162-L3163)

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

マテリアルプロパティに対応した入力情報が詰まった変数を返している部分です。

githubで実装を見る(L6813-6814)

ピンの接続性を64ビットフラグに突っ込んで返している箇所です。

githubで実装を見る(L6862-L6863)

static_assertは同様に要素数を変更したら対応してね♪の静的な通知機能です。

githubで実装を見る(L6874-L6878)

シェーダーコンパイル部分ですね。

githubで実装を見る(L6909-L6910)

static_assertは同様に要素数を変更したら対応してね♪の静的な通知機能です。

ちなみに以降はMaterialExpressionMakeMaterialAttributesではなく、それをばらすMaterialExpressionBreakMaterialAttributesノードです。

githubで実装を見る(L6971-L6975)

BreakMaterialAttributesノードの各ピンが出力するチャンネル数を指定しています。

githubで実装を見る(L7009-L7010)

原始的な紐づけ方法で草ですわ。

githubで実装を見る(L7096-L7097)

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

アウトラインマスクとアウトラインカラーのシェーダーコードを常時展開として指定しています。

理想はCustomDataと同様に使用する場合のみ展開なのですが、ぶっちゃけ実装が面倒だし、大してシェーダーランタイム負荷にもならんので、保守性を優先して簡単な常時展開を採用しています。

ちなみにココココを見ると分かりやすいと思います。

判定が偽の場合は、以降に進まないのでシェーダーコードが展開されないって感じです。

githubで実装を見る(L778-L779)

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

MaterialHLSLEmitterと同様です。

どちらが新コンパイラで旧コンパイラかは忘却しました。

両方把握していれば別にどうということはないという脳筋スタイルです。

githubで実装を見る(L707-L708)

Chunkも上記と似たようなものです。

新旧で設計が多少違うのでしょう。

githubで実装を見る(L1503-L1504)

Engine/Source/Editor/MaterialEditor/Private/MaterialEditor.cpp

アウトラインマスクの型であるfloatは、UMaterialExpressionScalarParameterに区分されます。

githubで実装を見る(L4902)

アウトラインカラーの型であるfloat3は、UMaterialExpressionVectorParameterに区分されます。
float2float4も同様にUMaterialExpressionVectorParameterに区分されます。

githubで実装を見る(L4914)

デフォルト値に戻す処理の対応です。

githubで実装を見る(L5110-L5118)

githubで実装を見る(L5164-L5165)

Engine/Source/Editor/UnrealEd/Private/MaterialGraph.cpp

ピンにカーソルを重ねた際に表示されるツールチップ部分です。

ついでにピンの並び順も兼ねています。

5.4からは拡張したものより後にFront Materialを配置するようになったらしいですね。

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

Engine/Source/Editor/UnrealEd/Private/MaterialGraphNode_Root.cpp

Constantノードが接続されているかの対応です。

シェーダーコンパイルの最適化に必要なのでしょう。

githubで実装を見る(L58-L59)

デフォルト値をUIから書き換えた場合の処理です。

githubで実装を見る(L130-L131)

マテリアルエディタを開いた際のピンの属性だったり、デフォルト値だったりの対応です。

エディタまわりはバージョンアップによる機能拡張の影響が、作業範囲の拡大として顕著に表れるので大変怠いです。

githubで実装を見る(L229)

githubで実装を見る(L267)

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

Engine/Shaders/Private/MaterialTemplate.ush

ピクセルシェーダーでアウトラインマスクとアウトラインカラーを取得する関数部分です。

関数名の末尾にRawがある場合はノードの結果をそのまま取得、Rawがない場合はGBuffer等に格納するのを想定してクランプ済み、または正規化済みな値を取得する、といった感じで使い分けます。

マテリアルノード側でsaturateする必要が無いように整備してあげるイメージです。

githubで実装を見る(L3485-L3503)

簡単な動作確認

追加したOutline MaskやOutline Colorを目に見える形で動作確認するためにはGBufferに格納しないといけないのですが、そこまでやるのは面倒なので簡単に確認できる方法を軽く紹介します。

型、ピンに対応したCustomノードを作成、接続

Window > Shader Code > HLSL Code をクリック

下の方にスクロールするとCustomノードの計算結果がそれぞれのピンに格納されていることが見れる

おわり!!!

スカラーパラメータであるアウトラインマスク、ベクターパラメータであるアウトラインカラーをピクセルシェーダーに渡すためのマテリアルピンの追加でした。

関連するエンジン改造

UMaterialExpressionBreakMaterialAttributes::Serializeで気になっていたことをようやく調べたの

前々から気になっていたUMaterialExpressionBreakMaterialAttributes::SerializeにDisplacementがない問題について、ようやくちゃんと調べました。

MaterialAttributesノードに興味がないので事務的に対応していましたが、内容を読むと互換性対応なんですよね。

つまりは古いバージョンから新しいバージョンに変える際の対応。

ということでバージョンアップ対応が導入された境界を調べました。

4.15から4.16にアップデートするタイミングで導入されたようです。

4.15ではFixedLegacyMaterialAttributeNodeTypesが未定義。

4.16でFixedLegacyMaterialAttributeNodeTypesが定義且つシリアライズが実装されている。

筆者は4.25あたりから参入した新参者なので存じていませんが、この頃はDisplacementピンが無かったのでしょう。

無いものを繋ぎ直すことは出来ないので未記載。

納得できました。

ゆっくりと ゆっくりと▼ ゆっくりと ゆっくりと▼

流石に草。

特定の最後の文字を削除する機能を追加して対応完了。

直後の双眸(そうぼう)が余裕で読めなかったわ。

読み上げツールはこういう時に助かりますね。

「▼」がピコピコする所為で遷移検出が頻繁に発生するの地味に困るな。

除外文字に紐づく領域は黒塗りして差分から外すか。

遷移処理の頻発は抑制できたけど、ピコピコの所為で差分が安定するまでに若干のタイムラグが起きてる。

UpdateTimeとStableCountを適当に調整して乗り切るかぁ。

内製のツールはこういった少し前の読み上げが搭載されていない作品と相性抜群ですね。

過去の私の作業が報われております。