1. サイトトップ
  2. ブログ
  3. Unity
  4. 【Unity】シェーダーグラフのカスタムライティング

【Unity】シェーダーグラフのカスタムライティング

こんにちは!情熱開発部のボルです!

Unityのシェーダーグラフ(SG)って便利ですよね。結構いいペースでエフェクトのイテレーションができますし、アーティストさんにとっても触りやすいのでアーティストとシェーダープログラマーの情報共有にも役立ちます。ただ、SGだとできないものもあります。その中でも一番大きいのは多分、Unityのライティングの情報にアクセスできないことです。しかし、本当に無理なのでしょうか?

こちらの記事で紹介するテクニックは少しチート感があって、実装するのが楽しいのでぜひご自分の環境でもやってみてください。例として、すごく単純なセルシェーダーをSGで作ってみましょう。

今回の記事ではUnity 2021.1.7f1のURPを使っています。

カスタムノードの作り方

作りたいエフェクトによって、必要なライティング情報が違うのですが、今回は試しにMainLightの向きと色をゲットしてみましょう。まずはURPのUnlit Shader Graphを作ってください。

Unlit Shaderではvertexとベースカラーしかないので、ライティングを表示したい場合は、自分でエンジンから取り出す必要があります。そのようなカスタムノードを作りましょう。

ここからはちょっとチート感があります。Unityは普通のHLSLファイルを読み込めますが、実は、エディター上では作る方法がないです。 エクスプローラ上で作るしかない仕組みになっています。Assetフォルダーを エクスプローラで開いて、「Includes」というフォルダーを作ってください。フォルダー名がIncludesではない場合Unityがインクルードする必要があると認識しないので、もし後でエラーになった際は、フォルダー名があっているかどうかを再確認してください。

Includesフォルダーの中を右クリックして、テキストファイルを作成し、拡張子を「.hlsl」に書き換えます。ファイル名はなんでもいいのですが、今回はLightingにします。

Unityエディターを開いたら自動的にLighting.hlslがインクルードされます。以下の画像の通り「Lighting.hlsl.meta」ファイルが生成されたらオーケーです。

では、カスタムノードで呼ばれている関数を定義しましょう。Lighting.hlslを開いて、以下のコードを入れてみましょう 。

#ifndef CUSTOM_LIGHTING_INCLUDED
#define CUSTOM_LIGHTING_INCLUDED

void CalculateMainLight_float(out float3 Direction, out float3 Color) {

}

#endif

関数のタイプはvoidですが、「out」キーワードで複数のパラメータをリターンできます。今回はライトの向きと色をリターンしたいので、float3を二つ用意しました。関数名の最後の「_float」部分が必要です。こちらはUnityにどんな精度を使うかを指示します。「_half」などにも設定できます。

ライト情報

これで普通のHLSLシェーダーを書いているようにUnityのライト情報をアクセスできます。試しにMain Lightの向きと色をリターンしましょう:

void CalculateMainLight_float(out float3 Direction, out float3 Color) {
	Light mainLight = GetMainLight(0);
	Direction = mainLight.direction;
	Color = mainLight.color;
}

上記コードはゲームの中やエディター上では問題なく使えますが、SGのいいところはイテレーションの速さなので、SG内のプレビューでもちゃんと表示されるようにしたいですよね。プレビューにはライトがないので、以下のように直接に向きと色の数字を入れましょう。

#if defined(SHADERGRAPH_PREVIEW)
	Direction = float3(0.5, 0.5, 0);
	Color = 1;
#else
	Light mainLight = GetMainLight(0);
	Direction = mainLight.direction;
	Color = mainLight.color;
#endif

さて、SGに戻りましょう。右クリックで「Custom function」ノードを作って、Graph Inspectorの方でノードの設定を見てみましょう。「Precision」と「Preview」は関数の定義で扱われていますので、Inheritのままで問題ないです。インプットはなし、アウトプットはCalculateMainLightの定義通り、Vector3二つ用意して、パラメータ名を同じくDirectionとColorにしてください。

最後に、TypeをFileにして、SourceをLighting.hlslを選択しましょう。Nameのところは精度なしの関数名がそのまま必要なので、コピペした方が安全です。今回は「CalculateMainLight」ですね。全部あってたら、ノードのプレビューが黄色になります。Directionの(0, 0.5, 0.5)が色にされているでしょうか。

ここでエラーがでたら、おそらくNameの大文字などの間違えか、_floatもそのままコピペしてしまった可能性があります。
試しに単純のLambertシェーダーを以下の画像通りに作りましょう。

確かに、Unity上でテストシーンを作ってみると、問題なくライティング情報が使われてるのが確認できます。

ライト計算

もちろん、作りたいエフェクトにはピクセルのライト量だけが必要でしたら、以下の通りにLambertの計算をカスタムノードの中に移動できます。

void Lambert_float(float3 Normal, out float Brightness, out float3 Color) {
#if defined(SHADERGRAPH_PREVIEW)
	Brightness = dot(float3(0.5, 0.5, 0), Normal);
	Color = 1;
#else
	Light mainLight = GetMainLight(0);
	Brightness = dot(mainLight.direction, Normal);
	Color = mainLight.color;
#endif
}

これでSG内でなんでもできますね。Posterizeしたり、 ライトのあたりによって違うディザリングパターン表示したり、ガ〇ダムみたいに影のところだけにテクスチャをスクロールしたり。

今はMainLightの情報だけを扱っていますが、URPのデフォルトシェーダーの内容を見て、Pointlight、複数のライト、影などの対応も追加できます。こうやって、作りたいエフェクトに必要なものだけを使えます。

以上解説になりました 。

本記事を読んでくれてありがとうございます。これを読んで面白いエフェクトのアイデアや実装方法が増えたら嬉しいです。


【免責事項】
本サイトでの情報を利用することによる損害等に対し、
株式会社ロジカルビートは一切の責任を負いません。