Help us understand the problem. What is going on with this article?

真のDynamicBranchをつくろう

More than 1 year has passed since last update.

Unreal Engine 4 (UE4) その2 Advent Calendar 2018 の23日目です

発端

ある日神は仰った
「(超ヘビー級)ブレンドレイヤーを最適化するにはどうすればいいか」
違う神は答えた。
「ブレンドレイヤーがベースレイヤーで書かれる事が多いなら動的分岐で弾くと良いのではないか」
民は思った。
「ブレンドレイヤーをいちいち全部カスタムノードに移植する作業はやりたくないなぁ」

目標

ノードに適当において動くTrueDyanmicBranchを作る。
完璧は求めない。とりあえずそれっぽく動くやつ

前知識

適当に使ってもあんまり効果は無いうえに逆に効率が低下することもありうるシェーダーの
[branch]if 命令ですが、上手に使うと最適化に効果が見込めます。

それじゃあUE4でどう使うといいのか見ていきます。

DyanmicBranchマテリアル関数

image.png

コメントだけ見ると「すごく・・・DynamicBranchです・・・」と感じますがこれはです。
よく見ると マテリアル関数 なんです。
ノード自体をダブルクリックすると・・・

image.png

ただのIfです。本当にありがとうございました。

実際にShaderDebugInfo下に出力されたシェーダーを見てみると

image.png

ただの3項演算子x2になっていることがわかります。
悲しいです。リクエスト自体は上がっているので今後実装されて書き換わるんでしょうか?
新しノードができたとしてもMaterialFunctionでの提供になってしまうとusf中で関数として分離してしまうので、
コンパイラのバックエンドが自動で最適化していくれるとかでない限り有用なシェーダーができることはないと思うので
これがリプレースされることはなさそうな予感がします。

custom nodeを使う

UE4のマテリアルにはカスタムノードという機能があります

image.png

これをに if を入れてコンパイルしてみると以下のようなコードが出力されます。

まずカスタムノードはusfファイル内に一つの関数として出力されます。

float3  CustomExpression0(FMaterialPixelParameters Parameters, float  Alpha, float3  TrueValue, float3  FalseValue)
{
float3 Result = FalseValue;
[branch]
if( Alpha > 0.0 )
{
    Result = TrueValue;
}
return Result;
}

この関数がCalcPixelMaterialInputs関数の内部から呼び出される形です。

    float3  Local20 = (Local4 + Local19);
    float3  Local21 = (Local20 * 0.20000000);
    float3  Local22 = CustomExpression0(Parameters,Local3,Local21, float3 (0.00000000,0.00000000,0.00000000));

    PixelMaterialInputs.EmissiveColor = Material_VectorExpressions[2].rgb;
    PixelMaterialInputs.Opacity = 0.50000000;
    PixelMaterialInputs.OpacityMask = 1.00000000;
    PixelMaterialInputs.BaseColor = Local22;

[branch]if 命令はそのブロックの中身だけを動的分岐の対象にするため、
分岐で省略したい命令をすべてブロック内部に書ききる必要があります
例えば上記の例は1ピコセカンドも早くなりません。

実際にUE4を使った様々なタイトルで、gpuの負荷をシコシコ削るためにCustomNodeを使った最適化が行われています。(多分)

あなたがこの仕様に完全に満足が行ってるなら、すぐさま回れ右をしてCustomNodeを書く作業に戻ってもよい。

実装

まずマテリアルエクスプレッションを足します!

要求仕様

-分岐の比較値パラメータ
-分岐が真だった場合の値
-分岐が偽だった場合の値

の三個を受け取り

[branch]
float3 Result = FalseValue;
if( InScalarValue > 0.0 )
{
    Result = TrueValue;
}

というノードを吐き出す。

参考コード

MaterialExpressionの定義は
UnrealEngine/Engine/Source/Runtime/Engine/Classes/Materials/MaterialExpression***.h
というパスに格納されています。
他のソースもこの周りにあります。

gitへのリンクについて

このページからリンクが張られているコードはEpicGames/UnrealEngineをフォークしたブランチなので
EpicGames/UnrealEngineへの登録が必要なようです。
登録されていない場合404画面になります。

https://github.com/EpicGames/UnrealEngine/compare/release...wankotank:TrueDynamicBranch

マテリアルエクスプレッションの追加

MaterialExpressionDynamicBranch.h

新規追加
特に説明はありません
コメントをリプレースできてないのでモロバレですが、MaterialExpressionFresnel.hをコピペして改変しただけです。

MaterialExpressionDynamicBranch.h on github

MaterialExpressions.cpp

マテリアルエクスプレッションの実態コードはこのソースファイルに含めるのがお作法のようなのでここに含めましたが、
既存のコードに追記するとしばしばエンジンのバージョンアップに伴ってマージするときにコンフリクトしたりするので
別ファイルに分離したほうがいい気がしますね。

MaterialExpressions.cpp on github

image.png

image.png

MaterialCompiler.h

MaterialCompiler.h on github

FMaterialCompiler::DyanmicBranch
FProxyMaterialCompiler::DyanmicBranch

を追加します。前者はpure virtualです。

HLSLMaterialTranslator.h

いわゆるusfシェーダーファイルの中核をなすクラスがあります。
超でかいです。

image.png

AddCodeChunkを二発入れているのがオシャレで気に入ってます。

一つ目のAddCodeChunkで

  float3 Local111 = Local110/*FalseValue*/;

を挿入して、すぐさまこの左辺値を次のコードで利用します。
動的分岐内では新しい変数の追加の必要はないので、左辺値は破棄します。

  float3 Local112/*Dummy*/ = 0.0f; //この行は無意味
  [branch]
  if( Local109/*比較参照値*/ > 0.0 )
  {
    Local111/*一つ目のChunkの左辺値*/ = Local108/*TrueValue*/;
  }

シェーダーファイルを見てみましょう

これでエクスプレッションの追加は終わりです。
これをマテリアルに適用してみると以下のようなusfファイルが吐き出されます。

    float  Local0 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local1 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[0].xy, float (Local0)));
    float3  Local2 = (Local1.rgb + Material_VectorExpressions[1].rgb);
    float4  Local3 = ProcessMaterialColorTextureLookup(Texture2DSampleBias(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[0].xy,View_MaterialTextureMipBias));
    float  Local4 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local5 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_0,Material_Texture2D_0Sampler,Local3.rgb.rg, float (Local4)));
    float  Local6 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local7 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[0].xy, float (Local6)));
    float3  Local8 = (Local5.rgb + Local7.rgb);
    float3  Local9 = (Local8 + Local1.rgb);
    float3  Local10 = 0.0;
    float3  Local11 = 0.0;
    [branch]
    if(Local1.a >= 0.0){
    Local10 = Local9;
    };

    PixelMaterialInputs.EmissiveColor = Local2;
    PixelMaterialInputs.Opacity = 1.00000000;
    PixelMaterialInputs.OpacityMask = 1.00000000;
    PixelMaterialInputs.BaseColor = Local10;
    PixelMaterialInputs.Metallic = 0.00000000;
    PixelMaterialInputs.Specular = 0.50000000;
    PixelMaterialInputs.Roughness = 0.50000000;
    PixelMaterialInputs.Subsurface = 0;
    PixelMaterialInputs.AmbientOcclusion = Local21;
    PixelMaterialInputs.Refraction = 0;
    PixelMaterialInputs.PixelDepthOffset = 0.00000000;

[branch]ifを挿入することができました!
合格!
ただしブランチブロックの中身がこれでは最適化の意味がないですね。
つづいてこれらを並び替えるコードを書いていきます。

USFのコード並び替えによる動的分岐の有効化

ここからが本題です。
生成されたUSFファイルを解析して並び替えます

最適化仕様

入力されたusfファイルに含まれる動的分岐ブロックの中にその分岐ブロックにしか依存しないコード行を突っ込む

入力はマテリアルから変換されたファイル出力前の文字列で
出力は最適化された後の文字列。

コードはおおざっぱに4つのブロックからなります。

DynamicBranchReorderer.cpp解説

1.入力を行単位に分割する

codes on github

image.png
説明は特になし

2.行ごとの左辺値と右辺値、属性を調べる

まず、正規表現で左辺値が見つかった場合はその変数番号を記憶します。
usfはLocal\d+の形で変数を連番で出力していくのでそれを使って抽出します。
次に右辺値をを同様に解析して行情報に追加していきます。

codes on github

3.依存関係を考慮して、ソート用キー値を行情報に付与する

2.で作った情報をもとに先頭からイテレーションして分岐内部に差し掛かったところで
最適化対象のコードをブラックリストと照合しながら分別します。
ブラックリストは分岐の後のコードと分岐条件のコード、すでに動的分岐ブロックに入っているコードの右辺値です。

codes on github

image.png

このようなusfの場合
オレンジのブランチ内部ブロックの位置に
グリーンのブロック中からコードを移動します。
青枠及び赤枠の中にあるコードは変更しません。
加えて青枠と赤枠のなかから参照されている値は分岐ブロック以外に依存関係をもつので移動できません。

githubのコードではサンプラーを含む行のコードがあります。
これはサンプラー行の右辺に変数参照があった場合にdevergence gradientエラーがしてしまうのを、
その参照元だけブランチブロックの外側においておけばエラーを回避できるのではないかと思って足してみたのですが
結果はダメでした。
devergence gradient エラーを回避するためには 右辺値を持つサンプラー行はブランチブロックに含めない対処が必要です。

4.3で作成したソート用キーでstablesortして出力

特筆する点はなし。

コード並び替えの処理結果

二つのDynamicBranchを含むMaterialをコンパイルした結果です。
もともとは変数の番号順にならんでいたものが最適化されてブランチブロックの中に移動しているのがわかります。

    float  Local0 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local1 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[0].xy, float (Local0)));
    float  Local2 = (Local1.r - 0.50000000);
    float3  Local8 = 0.00000000;
    [branch]
    if(Local2 >= 0.0){
    float  Local3 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local4 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_1,Material_Texture2D_1Sampler,Parameters.TexCoords[0].xy, float (Local3)));
    float  Local5 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local6 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_2,Material_Texture2D_2Sampler,Parameters.TexCoords[0].xy, float (Local5)));
    float3  Local7 = (Local4.rgb + Local6.rgb);
    float3  Local9 = 0.0;
    Local8 = Local7;
    };
    float3  Local10 = (Local8 + Material_VectorExpressions[1].rgb);
    float  Local11 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local12 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_3,Material_Texture2D_3Sampler,Parameters.TexCoords[0].xy, float (Local11)));
    float  Local13 = (Local12.r - 0.20000000);
    float3  Local32 = 0.00000000;
    [branch]
    if(Local13 >= 0.0){
    float  Local14 = MaterialExpressionNoise(GetWorldPosition(Parameters),1.00000000,1.00000000,0.00000000,1.00000000,6.00000000,-1.00000000,1.00000000,2.00000000,0.00000000,0.00000000,512.00000000);
    float  Local15 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local16 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_4,Material_Texture2D_4Sampler,Parameters.TexCoords[0].xy, float (Local15)));
    float  Local17 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local18 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_5,Material_Texture2D_5Sampler,Parameters.TexCoords[0].xy, float (Local17)));
    float3  Local19 = (Local16.rgb + Local18.rgb);
    float  Local20 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local21 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_6,Material_Texture2D_6Sampler,Parameters.TexCoords[0].xy, float (Local20)));
    float3  Local22 = (Local19 + Local21.rgb);
    float  Local23 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local24 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_7,Material_Texture2D_7Sampler,Parameters.TexCoords[0].xy, float (Local23)));
    float  Local25 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local26 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_8,Material_Texture2D_8Sampler,Parameters.TexCoords[0].xy, float (Local25)));
    float3  Local27 = (Local24.rgb + Local26.rgb);
    float3  Local28 = (Local27 + Local12.rgb);
    float3  Local29 = (Local22 + Local28);
    float3  Local30 = (Local14 + Local29);
    float3  Local31 = (Local30 * 0.20000000);
    float3  Local33 = 0.0;
    Local32 = Local31;
    };

    PixelMaterialInputs.EmissiveColor = Local10;
    PixelMaterialInputs.Opacity = 0.50000000;
    PixelMaterialInputs.OpacityMask = 1.00000000;
    PixelMaterialInputs.BaseColor = Local32;
    PixelMaterialInputs.Metallic = 0.00000000;
    PixelMaterialInputs.Specular = 0.50000000;
    PixelMaterialInputs.Roughness = 0.50000000;
    PixelMaterialInputs.Subsurface = 0;
    PixelMaterialInputs.AmbientOcclusion = 1.00000000;
    PixelMaterialInputs.Refraction =  float2 ( float2 ( float2 (1.00000000,0.00000000).r,0).r,Material_ScalarExpressions[0].x);
    PixelMaterialInputs.PixelDepthOffset = 0.00000000;

おまちかねの計測

さっそくマテリアルを作って効果のほどを見てみましょう!
※計測結果はハードウェアやマテリアルなど、いろいろな要素が絡みます。
数字は参考程度のものですので正しい方法でプロファイルを取ることをお勧めします。

ブレンドレイヤー編

マテリアル全景

※ベースレイヤー以外のテクスチャーサンプルはdevergence gradient エラー対策で MipLevel設定になっています

image.png

レベルの外観

ペイントされたランドスケープだけです。

image.png

計測条件

ウィンドウ解像度1280x720
r.ScreenPercentage 400
t.MaxFPS 600
Geforce1060
Ryzen1600
F2キー押し下げにより、Unlit表示に
ColorGrading以外のポストプロセス等はなし
SampleLevelをつかってMipLevel0を強制している関係で表面のサーフェスに接写

計測

TrueDynamicBranchなし

image.png

image.png

TrueDynamicBranchあり

image.png

image.png

ブレンドレイヤー計測結果

マテリアル ベースレイヤー部 非ベースレイヤー部
ブランチなし 3.6ms 3.6ms
ブランチあり 3.2ms 3.8ms

ベース部分だけのQuadGroup(Warp?WaveFront?)の部分は負荷軽減が望めるが、非ベースレイヤー部の負荷は増加してしまう。
ブレンドレイヤーはUVを加工するために通常のMipmap処理が適用できずにLevel指定版のサンプラを使っているのが根本的に
パフォーマンスを劣化させるので、VertexInterpolatorなどを使ってPixelShader内でのUVの加工を回避するなどの対策が必要。

うん。なんともいえない結果になりました。ワーストケースで負荷が増してしまうのはかなり適用しにくいです。
やっぱり簡単ではないですね。

フォーラムでもいくつか議論があるようですね。
https://forums.unrealengine.com/development-discussion/rendering/29346-landscape-layer-switch

【余談】
手元でLandSpaceCoordにVertexInterpolatorを指してみたところシェーダーコンパイルエラーが発生した。
LandScapeVertexFactory.ushがVertexInterpolatorに対応していない模様。ushを修正してビルドは通すようにすることは可能だがマテリアルのUVが正しく伝播しない。追加調査が必要だが、時間がないので割愛。

生成されたUSFファイル比較

TrueDynamicBranchなし

void CalcPixelMaterialInputs(in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs)
{

    float4  Local0 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local1 = dot(Local0, Material_VectorExpressions[1]);
    float  Local2 = (0.00000000 + Local1);
    float4  Local3 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local4 = dot(Local3, Material_VectorExpressions[2]);
    float  Local5 = (Local2 + Local4);
    float4  Local6 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local7 = dot(Local6, Material_VectorExpressions[3]);
    float  Local8 = (Local5 + Local7);
    float  Local9 = (1.00000000 / Local8);
    float  Local10 = dot(Parameters.TexCoords[0].xy,  float2 (-0.00000000,1.00000000));
    float  Local11 = dot(Parameters.TexCoords[0].xy,  float2 (1.00000000,0.00000000));
    float2  Local12 = (1.00000000 *  float2 (Local11,Local10));
    float2  Local13 = (Local12 +  float2 (0.00000000,0.00000000));
    float4  Local14 = UnpackNormalMap(Texture2DSampleBias(Material_Texture2D_1,Material_Texture2D_1Sampler,Local13,View_MaterialTextureMipBias));
    float3  Local15 = (Local14.rgb * Local1);
    float3  Local16 = (0.00000000 + Local15);
    float  Local17 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local18 = UnpackNormalMap(Texture2DSampleLevel(Material_Texture2D_2,Material_Texture2D_2Sampler,Local13, float (Local17)));
    float  Local19 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local20 = UnpackNormalMap(Texture2DSampleLevel(Material_Texture2D_3,Material_Texture2D_3Sampler,Local13, float (Local19)));
    float3  Local21 = (Local18.rgb + Local20.rgb);
    float3  Local22 = (Local21 * Local4);
    float3  Local23 = (Local16 + Local22);
    float3  Local24 = (Local20.rgb * Local7);
    float3  Local25 = (Local23 + Local24);


    PixelMaterialInputs.Normal = Local25;



    float3 MaterialNormal = GetMaterialNormal(Parameters, PixelMaterialInputs);
#line 1892 "/Engine/Generated/Material.ush"
    MaterialNormal = normalize(MaterialNormal);




    Parameters.WorldNormal = TransformTangentNormalToWorld(Parameters.TangentToWorld, MaterialNormal);
#line 1907 "/Engine/Generated/Material.ush"
    Parameters.WorldNormal *= Parameters.TwoSidedSign;


    Parameters.ReflectionVector = ReflectionAboutCustomWorldNormal(Parameters, Parameters.WorldNormal, false);


    Parameters.Particle.MotionBlurFade = 1.0f;



    float4  Local26 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local27 = dot(Local26, Material_VectorExpressions[1]);
    float  Local28 = (0.00000000 + Local27);
    float4  Local29 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local30 = dot(Local29, Material_VectorExpressions[2]);
    float  Local31 = (Local28 + Local30);
    float4  Local32 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local33 = dot(Local32, Material_VectorExpressions[3]);
    float  Local34 = (Local31 + Local33);
    float  Local35 = (1.00000000 / Local34);
    float4  Local36 = ProcessMaterialColorTextureLookup(Texture2DSampleBias(Material_Texture2D_4,Material_Texture2D_4Sampler,Local13,View_MaterialTextureMipBias));
    float3  Local37 = (Local36.rgb * Local27);
    float3  Local38 = (0.00000000 + Local37);
    float  Local39 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local40 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_5,Material_Texture2D_5Sampler,Local13, float (Local39)));
    float3  Local41 = (Local40.rgb * Local30);
    float3  Local42 = (Local38 + Local41);
    float  Local43 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local44 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_6,Material_Texture2D_6Sampler,Local13, float (Local43)));
    float3  Local45 = (Local44.rgb * Local33);
    float3  Local46 = (Local42 + Local45);

    PixelMaterialInputs.EmissiveColor = Material_VectorExpressions[5].rgb;
    PixelMaterialInputs.Opacity = 1.00000000;
    PixelMaterialInputs.OpacityMask = 1.00000000;
    PixelMaterialInputs.BaseColor = Local46;
    PixelMaterialInputs.Metallic = 0.00000000;
    PixelMaterialInputs.Specular = 0.50000000;
    PixelMaterialInputs.Roughness = 0.50000000;
    PixelMaterialInputs.Subsurface = 0;
    PixelMaterialInputs.AmbientOcclusion = 1.00000000;
    PixelMaterialInputs.Refraction = 0;
    PixelMaterialInputs.PixelDepthOffset = 0.00000000;

TrueDynamicBranchあり

void CalcPixelMaterialInputs(in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs)
{

    float4  Local0 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local1 = dot(Local0, Material_VectorExpressions[1]);
    float  Local2 = (1.00000000 - Local1);
    float  Local13 = dot(Parameters.TexCoords[0].xy,  float2 (-0.00000000,1.00000000));
    float  Local14 = dot(Parameters.TexCoords[0].xy,  float2 (1.00000000,0.00000000));
    float2  Local15 = (1.00000000 *  float2 (Local14,Local13));
    float2  Local16 = (Local15 +  float2 (0.00000000,0.00000000));
    float4  Local17 = UnpackNormalMap(Texture2DSampleBias(Material_Texture2D_1,Material_Texture2D_1Sampler,Local16,View_MaterialTextureMipBias));
    float  Local20 = (-1.00000000 + View_MaterialTextureMipBias);
    float  Local22 = (-1.00000000 + View_MaterialTextureMipBias);
    float3  Local29 = Local17.rgb;
    [branch]
    if(Local2 > 0.0){
    float4  Local3 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local4 = dot(Local3, Material_VectorExpressions[1]);
    float  Local5 = (0.00000000 + Local4);
    float4  Local6 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local7 = dot(Local6, Material_VectorExpressions[2]);
    float  Local8 = (Local5 + Local7);
    float4  Local9 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local10 = dot(Local9, Material_VectorExpressions[3]);
    float  Local11 = (Local8 + Local10);
    float  Local12 = (1.00000000 / Local11);
    float3  Local18 = (Local17.rgb * Local4);
    float3  Local19 = (0.00000000 + Local18);
    float4  Local21 = UnpackNormalMap(Texture2DSampleLevel(Material_Texture2D_2,Material_Texture2D_2Sampler,Local16, float (Local20)));
    float4  Local23 = UnpackNormalMap(Texture2DSampleLevel(Material_Texture2D_3,Material_Texture2D_3Sampler,Local16, float (Local22)));
    float3  Local24 = (Local21.rgb + Local23.rgb);
    float3  Local25 = (Local24 * Local7);
    float3  Local26 = (Local19 + Local25);
    float3  Local27 = (Local23.rgb * Local10);
    float3  Local28 = (Local26 + Local27);
    float3  Local30 = 0.0;
    Local29 = Local28;
    };


    PixelMaterialInputs.Normal = Local29;



    float3 MaterialNormal = GetMaterialNormal(Parameters, PixelMaterialInputs);
#line 1901 "/Engine/Generated/Material.ush"
    MaterialNormal = normalize(MaterialNormal);




    Parameters.WorldNormal = TransformTangentNormalToWorld(Parameters.TangentToWorld, MaterialNormal);
#line 1916 "/Engine/Generated/Material.ush"
    Parameters.WorldNormal *= Parameters.TwoSidedSign;


    Parameters.ReflectionVector = ReflectionAboutCustomWorldNormal(Parameters, Parameters.WorldNormal, false);


    Parameters.Particle.MotionBlurFade = 1.0f;



    float4  Local41 = ProcessMaterialColorTextureLookup(Texture2DSampleBias(Material_Texture2D_4,Material_Texture2D_4Sampler,Local16,View_MaterialTextureMipBias));
    float  Local44 = (-1.00000000 + View_MaterialTextureMipBias);
    float  Local48 = (-1.00000000 + View_MaterialTextureMipBias);
    float3  Local52 = Local41.rgb;
    [branch]
    if(Local2 > 0.0){
    float4  Local31 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local32 = dot(Local31, Material_VectorExpressions[1]);
    float  Local33 = (0.00000000 + Local32);
    float4  Local34 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local35 = dot(Local34, Material_VectorExpressions[2]);
    float  Local36 = (Local33 + Local35);
    float4  Local37 = Texture2DSample(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[3].xy);
    float  Local38 = dot(Local37, Material_VectorExpressions[3]);
    float  Local39 = (Local36 + Local38);
    float  Local40 = (1.00000000 / Local39);
    float3  Local42 = (Local41.rgb * Local32);
    float3  Local43 = (0.00000000 + Local42);
    float4  Local45 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_5,Material_Texture2D_5Sampler,Local16, float (Local44)));
    float3  Local46 = (Local45.rgb * Local35);
    float3  Local47 = (Local43 + Local46);
    float4  Local49 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_6,Material_Texture2D_6Sampler,Local16, float (Local48)));
    float3  Local50 = (Local49.rgb * Local38);
    float3  Local51 = (Local47 + Local50);
    float3  Local53 = 0.0;
    Local52 = Local51;
    };

    PixelMaterialInputs.EmissiveColor = Material_VectorExpressions[5].rgb;
    PixelMaterialInputs.Opacity = 1.00000000;
    PixelMaterialInputs.OpacityMask = 1.00000000;
    PixelMaterialInputs.BaseColor = Local52;
    PixelMaterialInputs.Metallic = 0.00000000;
    PixelMaterialInputs.Specular = 0.50000000;
    PixelMaterialInputs.Roughness = 0.50000000;
    PixelMaterialInputs.Subsurface = 0;
    PixelMaterialInputs.AmbientOcclusion = 1.00000000;
    PixelMaterialInputs.Refraction = 0;
    PixelMaterialInputs.PixelDepthOffset = 0.00000000;

}

ヤケクソ半透明編

もやっとする結果に終わったのでわかりやすい例も作って自己満に浸る

マテリアル全景

むやみにテクスチャをサンプルして適当に合成
TrueDyanmicBranchと組み込みの偽DyanmicBranchを比較する

image.png

レベル外観

プレーンにマテリアルを張り付けて、それぞれ10枚コピペします。
image.png

計測条件

ブレンドレイヤーとほぼ同じ。
F2は押してない

計測

TrueDynamicBranchあり

image.png

TrueDynamicBranchなし

image.png

やけくそ半透明マテリアル計測結果

マテリアル Translucent
ブランチなし 55msくらい
ブランチあり 14msくらい

わかりやすく変わりました。
サーフェスの一部を選択してサンプラーを酷使するようなカスタムポストプロセスマテリアルなんかでも有用な気がします!

やけくそ半透明マテリアルのUSF

void CalcPixelMaterialInputs(in out FMaterialPixelParameters Parameters, in out FPixelMaterialInputs PixelMaterialInputs)
{



    PixelMaterialInputs.Normal =  float3 (0.00000000,0.00000000,1.00000000);



    float3 MaterialNormal = GetMaterialNormal(Parameters, PixelMaterialInputs);
#line 1863 "/Engine/Generated/Material.ush"
    MaterialNormal = normalize(MaterialNormal);




    Parameters.WorldNormal = TransformTangentNormalToWorld(Parameters.TangentToWorld, MaterialNormal);
#line 1878 "/Engine/Generated/Material.ush"
    Parameters.WorldNormal *= Parameters.TwoSidedSign;


    Parameters.ReflectionVector = ReflectionAboutCustomWorldNormal(Parameters, Parameters.WorldNormal, false);


    Parameters.Particle.MotionBlurFade = 1.0f;



    float  Local0 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local1 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_0,Material_Texture2D_0Sampler,Parameters.TexCoords[0].xy, float (Local0)));
    float  Local2 = (Local1.r - 0.50000000);
    float  Local3 = (-1.00000000 + View_MaterialTextureMipBias);
    float  Local5 = (-1.00000000 + View_MaterialTextureMipBias);
    float3  Local8 = Local1.rgb;
    [branch]
    if(Local2 > 0.0){
    float4  Local4 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_1,Material_Texture2D_1Sampler,Parameters.TexCoords[0].xy, float (Local3)));
    float4  Local6 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_2,Material_Texture2D_2Sampler,Parameters.TexCoords[0].xy, float (Local5)));
    float3  Local7 = (Local4.rgb + Local6.rgb);
    float3  Local9 = 0.0;
    Local8 = Local7;
    };
    float3  Local10 = (Local8 + Material_VectorExpressions[1].rgb);
    float  Local11 = (-1.00000000 + View_MaterialTextureMipBias);
    float4  Local12 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_3,Material_Texture2D_3Sampler,Parameters.TexCoords[0].xy, float (Local11)));
    float  Local13 = (Local12.r - 0.20000000);
    float  Local15 = (-1.00000000 + View_MaterialTextureMipBias);
    float  Local17 = (-1.00000000 + View_MaterialTextureMipBias);
    float  Local20 = (-1.00000000 + View_MaterialTextureMipBias);
    float  Local23 = (-1.00000000 + View_MaterialTextureMipBias);
    float  Local25 = (-1.00000000 + View_MaterialTextureMipBias);
    float3  Local32 = Local12.rgb;
    [branch]
    if(Local13 > 0.0){
    float  Local14 = MaterialExpressionNoise(GetWorldPosition(Parameters),1.00000000,1.00000000,0.00000000,1.00000000,6.00000000,-1.00000000,1.00000000,2.00000000,0.00000000,0.00000000,512.00000000);
    float4  Local16 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_4,Material_Texture2D_4Sampler,Parameters.TexCoords[0].xy, float (Local15)));
    float4  Local18 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_5,Material_Texture2D_5Sampler,Parameters.TexCoords[0].xy, float (Local17)));
    float3  Local19 = (Local16.rgb + Local18.rgb);
    float4  Local21 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_6,Material_Texture2D_6Sampler,Parameters.TexCoords[0].xy, float (Local20)));
    float3  Local22 = (Local19 + Local21.rgb);
    float4  Local24 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_7,Material_Texture2D_7Sampler,Parameters.TexCoords[0].xy, float (Local23)));
    float4  Local26 = ProcessMaterialColorTextureLookup(Texture2DSampleLevel(Material_Texture2D_8,Material_Texture2D_8Sampler,Parameters.TexCoords[0].xy, float (Local25)));
    float3  Local27 = (Local24.rgb + Local26.rgb);
    float3  Local28 = (Local27 + Local12.rgb);
    float3  Local29 = (Local22 + Local28);
    float3  Local30 = (Local14 + Local29);
    float3  Local31 = (Local30 * 0.20000000);
    float3  Local33 = 0.0;
    Local32 = Local31;
    };

    PixelMaterialInputs.EmissiveColor = Local10;
    PixelMaterialInputs.Opacity = 0.50000000;
    PixelMaterialInputs.OpacityMask = 1.00000000;
    PixelMaterialInputs.BaseColor = Local32;
    PixelMaterialInputs.Metallic = 0.00000000;
    PixelMaterialInputs.Specular = 0.50000000;
    PixelMaterialInputs.Roughness = 0.50000000;
    PixelMaterialInputs.Subsurface = 0;
    PixelMaterialInputs.AmbientOcclusion = 1.00000000;
    PixelMaterialInputs.Refraction =  float2 ( float2 ( float2 (1.00000000,0.00000000).r,0).r,Material_ScalarExpressions[0].x);
    PixelMaterialInputs.PixelDepthOffset = 0.00000000;

}

おわりに

ながながと取り留めのないことをつらつらと書いてきましたが
ある程度成果がでてよかったです。

修正すべき点はまだまだあるのでもう少し改良を加えたらプルリクエストしてみようと思います。

明日は@nano06126728さんです!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした