Search Unity

シェーダーグラフで実験しよう:少ないリソースで多くのことを実現する

, 2月 25, 2021

物理ベースレンダリング(PBR)マテリアルの情報を単一のテクスチャマップにまとめたり、レイヤー化してコンパクトなシェーダーに落とし込んだりして、グラフィックス品質を落とすことなく、実行時の効率を高めることができます。本記事ではそのことを検証した実験について解説を行いました。ぜひこの後の内容をお読みください。

この実験は、ユニバーサルレンダーパイプライン(URP)と HD レンダーパイプライン(HDRP)の両方に適用できます。この記事を最大限に活用するためには、シェーダーグラフにある程度慣れている必要があります。初めてシェーダーグラフを使用する方は、このツールが視覚的にシェーダーをオーサリングするためのツールということをまず念頭に置いていただいたうえで、ツールの紹介ページ詳細の解説をご覧ください。

地形のような環境でアートアセットを扱う場合、より良いブレンド結果が得られるため、一般的には複数のタイリングして使えるマテリアルを重ねて使用することが好ましいとされています。しかし、複数のテクスチャをサンプルする時の GPU パフォーマンスのコストや、シェーダーに追加された各レイヤーでのメモリ使用量の増加は、一部のデバイスではカバーしきれないほどのものであり、また、一般に非効率です。

今回の実験の狙いは以下の通りです。

  • 少ないリソースで多くのことを実現する
  • メモリフットプリントを最小化し、PBR マテリアルを表現する際のテクスチャサンプリングを簡素化する
  • シェーダー命令の数を最小化する
  • 最小限のスプラットマップおよび頂点カラーチャンネルでレイヤーブレンディングを実現する
  • スプラットマップおよび頂点カラーの機能を拡張して、いくつかおまけ機能を実現する

 

実験は目標を達成しましたが、いくつか注意点があります。どのトレードオフを許容するか決定する際に、自身のプロジェクトの要求に応じて優先順位を設定する必要があります。

 

1. 単一のテクスチャにパックされた PBR マテリアル

レイヤー化を行う前に、まず最初にしなければならないことは、PBR マテリアルのパッキングを把握することです。PBR マテリアルは通常、アルベド(ベースカラー)、スムースネスマスク、アンビエントオクルージョン、メタルネス、法線のパラメーターが定義されています。

通常、5 つのマップすべてを 3 つのテクスチャマップで表現します。テクスチャの使用量を最小限に抑えるために、今回の実験ではメタルネスアンビエントオクルージョンを犠牲にすることにしました。


残りのアルベド、スムースネス、法線の各マップの定義は、伝統的に少なくとも 2 つのテクスチャマップで表現されてきました。これを 1 つのマップに減らすには、各チャンネルの前処理が必要です。


PBR マテリアルを 1 つのテクスチャにパックした最終結果は上の画像のようになります。それぞれのチャンネルは以下のデータを表します。

Red = 法線の定義数に対する dHdu(U 軸方向の高さの導関数)
Green = 法線の定義数に対する dHdv(V 軸方向の高さの導関数)
Blue = アルベドを表す線形グレースケールのシェード(シェーダーで再構築される色情報)
Alpha = 線形スムースネスマップ(標準的なスムースネスマップ)

注意:テクスチャは sRGB のチェックを外した状態で Unity にインポートし、BC7 形式で圧縮しています。他のプラットフォームに移植する場合は、プラットフォームがサポートしている同等の 4 チャンネルテクスチャ形式に切り替えてください。

マップを処理する

アルベド

アルベドは通常、RGB テクスチャとして定義されますが、多くの地形のようなマテリアル(岩、砂、泥、草など)は限られたカラーパレットで構成されています。アルベドをグレースケールのグラデーションとして保存し、シェーダーでそれをカラーリマッピングすることで、この特性を利用することができます。

RGB アルベドをグレースケールのグラデーションに変換するための定番の方法はありません。この実験では、元のアルベドマップチャンネルとアンビエントオクルージョンを選択的にマスクしてグレースケールアルベドを作成しました。シェーダーでの色の再構成によって現れる色をマッチさせるには、手動で調整した結果を目測するだけです。

アルベドの再構成のためのランプコントラスト調整によるハイライトとダークカラーリング。


スムースネス

スムースネスは PBR マテリアルの定義において非常に重要であると考えられています。スムースネスをより正確に定義するために、スムースネス専用のチャンネルが存在します。

シェーダーのスムースネスに単純な乗算器を追加して、マテリアルに多少のバリエーションを持たせるようにしました。
(スムースネスの乗算器をスキップしてシェーダー命令を減らすこともできる)


法線マップの定義

法線マップは、表面の詳細な特性を示すために重要です。典型的な PBR マテリアルでは、接線空間法線マップを使用します。この実験では、以下の理由から、表面勾配フレームワークを使用した事前変換済みの導関数マップを選択しました。詳細は Morten Mikkelsen による表面勾配フレームワークに関する記事を参照してください。

接線空間法線マップを導関数マップに事前変換するには、この Photoshop アクションを使用します。

事前変換済み導関数マップを使うことにはいくつかの利点があります。

  • シェーダー内で導関数への変換が必要な標準的な接空間法線マップよりも少ない命令数で、表面勾配マップに直接変換できます。
  • 2 つのチャンネル(dHdu と dHdv)に保存することができ、実行時のメモリとテクスチャキャッシュのフットプリントを削減することができます。
  • 表面勾配フレームワークが法線の再構成を行うので、接線空間法線マップを処理する際に典型的に必要だった、シェーダーでのブルーチャンネルの再構築が必要なくなります(シェーダー命令が少なくなります)。
  • Photoshop で強度をブレンドしたり、マスクしたり、減らしたりして調整した場合にも正しく動作し、かつ正規化を再度行う必要がありません。たとえば、強度を下げるにはマップに対して RGB(128,128,0) をブレンドするだけです。

表面勾配フレームワークと組み合わせると、さらに以下のような利点が生じます。

  • 法線バンプマップの情報は、アルベドのブレンドおよびコンポジットと同じようにシェーダーでブレンドとコンポジットができ、正しい結果が得られます。
  • バンプマップの寄与を増やしたり、減らしたり、逆転させたりすることが簡単かつ正確にできます。

ただし、接空間法線マップから事前に変換された導関数には、いくつかの欠点もあります。

  • Photoshop 変換を使用すると、8 ビットテクスチャの精度のバランスを取るために、角度が 45 度を超える範囲では法線の定義がクランプされます。
  • アーティストは接空間の法線マップでの作業に慣れているので、ワークフローの一部として Photoshop を介してマップを事前に変換する必要があります。

注:45 度を超える角度でのクランプは、シェーダーベースの導関数変換には適用されません。

ユースケースに応じて、制限の影響の程度は変わります。今回の実験では、45 度未満の法線方向は、最終結果に目立った悪影響を及ぼしません。それどころかこの場合、極端な法線方向からの不要な反射を減らすという利点があります。

事前に変換された導関数マップテクスチャと接空間法線マップテクスチャの比較。

 

事前変換された導関数と接空間法線マップのストレステストの結果の差。球体のバンプマップでは、事前に変換された導関数マップの 45 度のクランプにより、アーティファクトが発生していることに注意してください。

 

事前に変換された導関数マップを表面勾配に再変換するグラフ。

 

注:マスタースタックフラグメントの法線空間が、デフォルトの接線空間法線ではなく、ワールド空間法線を受け入れることを確認してください。


完全なアンパックの手順

コンパクト PBR テクスチャを解凍して、色付きのアルベド、滑らかさ、表面のグラデーションを出力するサブグラフの全体図。

注:表面グラデーションの法線への変換はサブグラフの外側で行われるため、UnpackedSubGraph の出力に基づいてマテリアルを簡単にブレンドできます。

アンパックされたシェーダーからのマテリアルサンプル。


さまざまな角度を組み合わせることで、光に対する各マテリアルの応答がより良くわかる。


2. 素材の重ね合わせ

この実験では、単一チャンネルの再マップに関して、層ベースで重ね合わせを行う方法を選択しました。サブグラフは、5 つの線形補間を実行します(ベースを加えて、6 つのレイヤーを形成します)。

レイヤーの重みづけをブレンドする方法はたくさんあります。この方法は、単一のベクトル入力で済むという単純さがあり、これは実験の目標と合致しています。これにより、スプラットマップまたは頂点チャンネルの複数のチャンネルを使い切ることなく、多くのレイヤーの重ね合わせが可能になりました。


この方法の欠点は、個別のレイヤーの寄与の重みを制御できないことです。ブレンディングは常に前のレイヤーからの遷移になります。ユースケースによっては、これは従来のチャンネルごとのブレンドと比較して制限を与える要因になることもあります。

 

6 つのレイヤーを表すために単一のチャンネルを再マップするサブグラフ

 

上に示したサブグラフは、層ベースのブレンドを施した 6 つのレイヤーに合わせて事前定義されています。さらにレイヤーを作成するには、1 を必要なブレンドレイヤー数で割り、1 を引いてから、その値の範囲に基づいて各レイヤーを再マップします。

たとえば、9 つのレイヤーをブレンドして作るマテリアルの場合、各レイヤーの再マップ範囲は 1 / (9-1) = 0.125 です。

レイヤー In Min In Max
1 0 0.125
2 0.125 0.25
3 0.25 0.375
4 0.375 0.5
5 0.5 0.625
6 0.625 0.75
7 0.75 0.875
8 0.875 1

1 つのチャンネルが細かく分割されるほど、シェーディング範囲が狭くなることに注意してください。

ブレンディングの特性を示すために色を使用してテストされた重ね合わせ手法。

 

アンパックされたマテリアルの 6 つのレイヤーを単一の頂点カラー(この場合は赤のチャンネル)で合成するマテリアル。

Note: 注:3 つの PBR テクスチャをブレンドするよりも、シェーダーグラフでサンプルがパックされた単一の PBR マテリアルのレイヤーをブレンドする方が簡単です。

レイヤーブレンディングは層ベースであるため、ブレンドしたマテリアルを設定する前に、レイヤーの順序についてきちんと考えておくことをお勧めします。


3. マテリアルの機能を拡張する

レイヤーブレンディングに必要なのは、チャンネル 1 つ(赤の頂点チャンネル)だけです。残りの 3 つの頂点チャンネルは、追加の機能を提供します。

最終的なシェーダーグラフは、残りの頂点チャンネルを使用して結果を生成します。

この実験では、Polybrush(パッケージマネージャーから入手可能)を使用して Unity エディター内で頂点ペイントを実行しました。

このシェーダーで推奨される VertexPaint カラーパレットは以下の通りです。

 

レイヤーの寄与を重み付けするために使用されます。

赤の頂点チャンネルでペイントを行うデモ


表面のグラデーションプロパティを設定して、法線バンプの寄与を反転、削減、または追加します(-1 および 1 に再マップされます)。

  • 0 は法線バンプを反転します(-1)
  • 0.5 は、法線バンプをゼロにします(0)
  • 1 は、法線バンプを元の値(+1)に設定します

緑の頂点チャンネルでペイントを行うデモ

 

スムースネスと表面のグラデーションバンプスケールを制御して、水で湿った外観を作成します

  • 0 = 変更なし
  • 255 = 最大のスムースネスとフラットな法線マップ(湿った外観)

青の頂点チャンネルでペイントを行うデモ

 

アルファ:アルベドレイヤーの重みを制御し、ベースカラーを白に設定します。寄与は表面の法線の y 軸に基づいています。スムースネスを変えることはなく、元の表面レイヤーのスムースネスとバンプのプロパティを利用します。

  • 0 = 雪なし
  • 255 = 完全に雪で覆われている

すべてのレイヤーが雪とどのように相互作用するかを示すために、ここまで示した 3 つのチャンネルと組み合わせてアルファ頂点チャンネルでペイントを行う

上記の様々な頂点ペイントチャンネルの結果を組み合わせたものを以下に示します。

プロジェクトの要件に応じて、シェーダーのブレンド方法と頂点チャンネルおよびスプラットマップのさまざまな機能の設定を調整できます。


4. パフォーマンスの比較

この実験の目的は、リソースを最小限に抑えながら、シェーダーグラフの機能を拡張することでした。テクスチャは前処理されてアンパックされましたが、実行時の効率への見返りはあるでしょうか。

パフォーマンスプロファイリングは、これらの取り組みが生み出した効率を示しています。

コンパクトな 6 つのレイヤーからなるブレンドシェーダーと比較するために、標準的な 6 つのレイヤーを持つブレンドシェーダーを作成しました。両方のシェーダーは、同じ機能を持つ同じブレンド方法を使用して作成されました。主な違いは、標準のシェーダーが 3 つの異なるテクスチャを使用して単一のレイヤーを表すことです。

プロファイリングでは、ターゲットプラットフォームでユニバーサルレンダーパイプラインを使用して、ブレンドされたマテリアルを使用して単一のメッシュを画面にレンダリングしました。


モバイルメモリとパフォーマンスプロファイル

モバイル用のテクスチャ圧縮(Android):

モバイル用の 1024×1024 のアルベド、マスク、法線マップを備えた標準 PBR:

  • 6 枚のアルベドマップ ASTC 10×10 = 6 x 222.4 KB
  • 6 枚のマスクマップ ASTC 8×8 = 6 x 341.4 KB
  • 6 枚の法線マップ ASTC 8×8 = 6 x 341.4 KB

テクスチャの合計メモリ使用量 = 5.431MB

 

モバイル用の 1024×1024 のコンパクト PBR:

  • 6 枚の PackedPBR テクスチャ ASTC8x8 = 6 x 341.4 KB

テクスチャの合計メモリ使用量 2.048MB

 

コンパクトな 6 つのレイヤーからなるマテリアルにより、モバイル(Android)でのテクスチャメモリ消費量が約 62% 削減と、半分以上のメモリが節約されました。


Adreno 630(Snapdragon 845)を搭載したモバイル Android / Vulcan で、Snapdragon プロファイルを行った結果:
:

  • 実行時のテクスチャメモリリードが約 70% 少なくなりました。
  • 標準的なマテリアル(Standard)は、レンダリングに 9971020 クロックかかりました。
  • コンパクトなマテリアル(Compact)は、レンダリングに 6951439 クロックかかりました。

コンパクトなマテリアルは、画面上で約 30% 速くレンダリングされます。


Snapdragon プロファイラーからのプロファイリング結果。


PC のメモリとパフォーマンスプロファイル

1024 x 1024 のアルベド、マスク、法線マップを備えた標準 PBR:

  • 6 枚のアルベド DTX1 = 6 x 0.7 MB
  • 6 枚のマスク DXT5 / BC7 = 6 x 1.3 MB
  • 6 枚の法線マップ DXT5 / BC7 = 6 x 1.3 MB
    テクスチャの合計メモリ使用量 19.8MB

 

1024 x 1024 のコンパクト PBR:

  • 6 枚の PackedPBR テクスチャ BC7 = 6 x 1.3 MB

テクスチャの合計メモリ使用量 7.8MB

 

コンパクトな 6 レイヤーのマテリアルを使うと、PC でのテクスチャメモリ消費量が 60% 削減されました(メモリ消費量を半分以上節約)。

 

2880 x 1800 でレンダリングされた Radeon460Pro を搭載した PC ラップトップで、RenderDoc プロファイルを行った結果:

  • 6 つのレイヤーをブレンドした標準的なマテリアルによる不透明色の描画:5.186 ミリ秒
  • 6 つのレイヤーをブレンドしたコンパクトなマテリアルによる不透明色の描画:3.632 ミリ秒
    コンパクトなマテリアルは、画面上で約 30%* 速くレンダリングされます。
    * RenderDoc プロファイル値は変動します。30% はサンプルの平均です。

 

nVidia GTX1080 を搭載した PC デスクトップで、2560 x 1440 でレンダリングした場合の、nSight プロファイルの結果:

  • 6 つのレイヤーからなる標準的なマテリアルによる不透明色のレンダリング:0.87 ミリ秒
  • 6 つのレイヤーからなるコンパクトなマテリアルによる不透明色のレンダリング:0.48 ミリ秒

コンパクトなマテリアルは、画面上で約 45% 速くレンダリングされます。


nSight のプロファイリング結果。

 

コンソールのパフォーマンスプロファイル

PlayStation 4 では、コンパクトなマテリアルを使用すると 60% のメモリ節約になります。PS4 は PC と同じ圧縮形式を使うため、結果が同じになっています。

 

1920 x 1080 での PS4 ベースレンダリングで、Razer プロファイルを行った結果:

  • 6 つのレイヤーからなる標準的なマテリアルによる不透明色のレンダリング:2.11 ミリ秒
  • 6 つのレイヤーからなるコンパクトなマテリアルによる不透明色のレンダリング:1.59 ミリ秒
  • コンパクトなマテリアルは、画面上で約 24.5% 高速にレンダリングされます。

PS4Razor プロファイラーのプロファイリング結果。


要約すると、6 つのレイヤーからなるコンパクトな PBR シェーダーを使用すると、パフォーマンスが向上し、メモリが大幅に節約されます。GPU パフォーマンスの変動は興味深いものですが、あらかじめ予見できたことでもあります。マテリアルのアンパックには、テクスチャを多数サンプリングするよりも多くの ALU が消費されるためです。

サンプルプロジェクトをダウンロードする

シェーダーグラフとサブグラフを使用したこのサンプルプロジェクトは、以下の場所からダウンロードできます。

[ダウンロード]:Unity2020.2.5f1 + HDRP 10.3.1 版のプロジェクト
[ダウンロード]:Unity2020.2.5f1 + URP 10.3 版のプロジェクト
[ダウンロード]:接空間法線マップを導関数に事前変換する Photoshop アクション


まとめ

ユニバーサルレンダーパイプライン版プロジェクトからのスクリーンショット。

この実験の主な構成要素は以下のとおりです。

  • カスタムマテリアルのシェーダーグラフ
  • 事前に変換された導関数
  • 表面勾配フレームワーク
  • アルベドカラーの再構成
  • 単一チャンネルのレイヤーブレンディング
  • UpVector ブレンド手法、頂点チャンネルのブレンドによるスムースネスとバンプの制御

この実験では、シェーダーグラフを使用して、システムリソースの使用効率がよく、かつ美しいグラフィックを作成する方法を紹介しました。このサンプルがアーティストや開発者に刺激を与え、Unity プロジェクトにおける美的表現の限界を押し上げる活力を生むことを期待します。

 

このブログの著者紹介

Rinaldo Tjan(テクニカルアートディレクター、R&D、Spotlight チーム)は、リアルタイムライティングとレンダリングシステムに尋常ならざる情熱を注ぐリアルタイム 3D アーティストです。

PlayStation 2 の時代にキャリアをスタートさせ、それからテクスチャリングから最終レンダリングされたシーンの制作まで通して、アーティストワークフローの知識を 10 年以上にわたって積み上げてきました。Unity Technologies に入社する前は、『BioShock2』、『The Bureau: XCOM Declassified』、『Mafia III』などの AAA タイトルのリリースを支える立場にいました。

現在は、Unity の顧客と協力してプロジェクトを強化し、Unity を使って顧客の真の可能性を引き出す支援を行うとともに、Unity のレンダリング機能の内部開発と標準化を推進しています。

21 replies on “シェーダーグラフで実験しよう:少ないリソースで多くのことを実現する”

Hello! great job!
I am really curious to adding this some real time mesh gen for close up camera.
I noticed that this terrain is realistic from far but not from close up camera.

I want to make a tech that converts these texture 3d effect to convert 3d high res vertex like tessellation when player look in front of terrain . So then whole world becomes Realistic!

For the moment, there’s no timeline I can give you for the tessellation shader graph functionality.
However there’s definitely a few things you can try to experiment with your idea.

For the vertex displacement, it is already supported at the moment with Shader Graph.
Just need to apply it to a highly tessellated mesh.
Then to test it even further, use the LOD Group system that has multiple progression of polygon complexity on the mesh to see if the result is as desired.
While this doesn’t give you the smooth transition of the auto tessellation system, it gives you a general idea on how the 3D displacement will look like.

Looking at your video, you might be interested in looking at Gradient Map technique.
You can try this in Shader graph.

Connect Sample 2D of a Grayscale image (R) to -> (Time) input of “Sample Gradient” node with a Gradient property connected to it’s Gradient input -> output of the sample gradient to base color to see the results.

This technique is known in compositing software and Photoshop (I believe GIMP has similar functionality). That way you can preview how the gradient get’s mapped to color the grayscale images.
Give it a shot.

Good article but relying on photoshop actions is a negative point.

It would be better to do a quick shader inside Unity to do the same result to process textures. It’s more reliable this way, can easily be absorbed into existing toolchains and can be saved as you’d like inside Unity.

I do this for my own conversions and so on, plus most people do not have an adobe subscription, only professionals tend to.

Feedback taken. The same concern was posted by mylk below in comment section. In hindsight I should have included the shader sub-graph (or at least a screenshot) to show what the ts normal to derivatives action try to accomplish. For now, please follow the link I posted in mylk reply to get the subgraph.
In any case, thank you for taking the time to read the blog and posting feedback.

No problem, I felt the article was one of the best I’d ever seen in recent times from Unity, and hope there will be more. I could only find one minor problem as above :-)

Pretty nice article and clever techniques! Normal derivates? I learned something new today :) Keep it up!

When i convert my project to the latest unity all my shaders become more shiny !
i think need more work to stabilize.

Nice writeup!

A few thoughts:
– Texture Compression favors the green channel, and packs alpha separately. So your most important information should be in the green and alpha channels, with the least important in blue.
– Splat map texturing is almost always memory bound – in most cases, the cost difference of avoiding a few instructions is negligible, so focusing on better packing of textures is the way to go.
– Consider generating your smoothness/ao from your luminance and normal, with user controls to range map the smoothness. You should be able to get a good approximation from both with only a few instructions.
– In the Walking Dead game I did for mobile, we reduced our terrain to a single texture sample. We packed 4 luminance channels into a single texture, then stored 4 Cb and Cr in the vertices (yCbCr color space). We then generated a height value from the luminance for height blending, unpacked the 4 colors, and blended. We also produced a simple normal from the luminance, though for more modern devices I’d like just use the derivatives of the worldPos to get a decent approximation if I didn’t need to get close to the terrain.
– Being able to properly branch in the shader and use dynamic flow control would have seen a similar reduction in memory bandwidth usage. It’s sad that no shader graph I’ve seen can handle this properly. You would have hit six samples without any packing of textures, and 2 with packing.
– Given that your shader only ever uses 2 of the samples on any given pixel (due to the 1 channel splat map), you should never have to sample all 6 textures. You could achieve this in two ways- the first is to use a texture array to store your textures and determine which two texture indexes need to be sampled for the pixel, and only sample those. The second would be to branch around the unused textures, which unfortunately requires proper dynamic flow control.
– Note that getting down to two samples and using dynamic flow control, you’d be able to add stochastic height map sampling or triplanar texturing to this and have a similar cost to what you have now, both of which greatly help with the quality of terrain texturing. Properly branch sampled, these would be 4-6 samples per pixel using the combined approaches.

Really nice to get some insights from real world experience.
There are definitely alternatives trade-offs that can be made.
One of the priority in my experiment is to preserve the smoothness information as best as possible.
It would be interesting to experiment with what you suggest on generating smoothness/ao from luminance and normal, I have never tried that.

Is it possible to share all the needed actions to pre-convert tangent space normal maps to derivatives, for those who don’t use Photoshop?

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です