3 / 4 Shader Graphとの照明比較
このステップでは、HLSLで行った明暗計算をShader Graphへ置き換え、同じ入力に対する結果を比較します。
比較条件をそろえる
まずはURPのUnlit Shader Graphを用意し、Graph SettingsのSurface TypeをOpaque(不透明)に設定します。HLSL側も同様に、明暗を自前で計算する不透明シェーダーを使用します。
描画結果を正しく比較するために、同じメッシュを使い、オブジェクトの回転や拡大縮小をそろえておきます。また第14回と同様に、最小構成のHLSLを使用する比較環境では、Universal Rendererの設定でDepth Priming ModeをDisabledにしておきます。今回作成するHLSLには、Depth Primingで使うDepthOnlyパスを用意していないためです。
なお、Scene内のライトを回転させても、今回のマテリアルプロパティである_LightDirの値は自動的には連動しません。比較する二つのマテリアルには、同じ光の方向を直接手動で設定してください。
法線と光の方向を接続する
Shader Graphのエディター上で、次の順序でノードを配置して接続していきましょう。
- Normal Vectorノードを作成し、SpaceをWorldに設定します。
- Normal Vectorノードの出力を、Normalizeノードへ接続します。
- 光の方向を指定するVector3プロパティを作成し、初期値を
(0, 1, 0)に設定します。 - 作成したプロパティのノードを、別のNormalizeノードへ接続します。
- これら二つのNormalizeノードの出力を、Dot Productノードへ接続します。
- Dot Productノードの出力を、Saturateノードへ接続します。
Normal Vectorノードは、メッシュの法線を指定した空間で使用できるように取り出すノードです。HLSL側では、その役割を入力属性、頂点関数の変換、Varyingsによる受け渡しに分けて記述していました。
白黒の表示を対応付ける
続いて、Saturateノードの出力をUnlitのBase Colorへ接続します。値がRGBへ同じように使われることで、HLSLの白黒表示と対応します。
| Shader Graphの処理 | HLSLの対応箇所 |
|---|---|
| Normal Vector(World指定)でWorld空間の法線を取得する | TransformObjectToWorldNormalによる変換とVaryings.normalWSによる受け渡し |
| Normalizeで法線の長さを1にする | normalize(input.normalWS)が対応します。 |
| 光の方向に対するNormalizeで長さを1にする | normalize(_LightDir.xyz)が対応します。 |
| Dot Productで二つの方向の内積を求める | dot(normalWS, lightDirWS)が対応します。 |
| Saturateで結果を0〜1へ制限する | saturate(...)が対応します。 |
このように、Shader Graphのノード一つが常にHLSLの一行と対応するわけではありません。Normal Vectorノードのように、必要な受け渡しや空間変換をShader Graph側が裏で自動的に組み込んでくれる場合もあります。
三つの変更で結果を確かめる
オブジェクトを回転させる
光の方向を固定したまま、オブジェクトを回転させてみましょう。両方の実装で明るい面が同じように変われば、法線の空間と向きが正しく対応しています。
光の方向を変える
二つのマテリアルに設定した光の方向を、同じ値へ変更してみましょう。もし片方だけ結果が反転してしまう場合は、片方の実装で「光が進む方向」を使っていないか確認してください。
カメラの位置を変える
オブジェクトと光の方向を固定し、カメラだけを移動させてみましょう。このランバート照明の計算には、視線の方向が入っていません。そのため、同じ表面上の点の明るさは、カメラの位置だけでは変わりません。
カメラを動かすと、画面内で見える面やその面積は変化します。画面全体の印象の変化と、同じ面における照明の値を混同しないように区別して観察しましょう。
- Unlitを土台にして、自前で照明を計算できていますか。
- 二つの実装に対して、同じ光の方向を設定して比較できていますか。
- HLSLの各計算に対応するノードを正しく示せますか。
- この明暗計算にカメラの位置が含まれない理由を説明できますか。