🌔

【Unity】Unlit ShaderGraphで影を受ける【Shader Graph】

2023/07/05に公開

はじめに

Lit ShaderGraphでは、
ライティング結果を加工するような表現(トゥーンシェーダーなど)ができません。

そのようなシェーダーを書くためには、Unlit ShaderGraph に自力でライティングし、
ライティングしたものを加工する必要があります。
しかし、Shader Graph には、ライトの情報を取得するノードが無いため、
ライトを取得するためには CustomFunctionノードを自分で書く必要があります。

そのため、本記事では、MainLightを取得するCustom Functionを定義し、
陰と影を計算する方法について紹介します。

※陰と影の違いについてはこちらの記事が参考になります。

対象読者

ShaderGraphはそれなりに使えるけどShaderLabやHLSLはよくわからない人

Shader Graphの作成

URPを導入し、Unlit Shader Graphを作成します。

陰の描画

STEP1 HLSLファイルの作成

CustomFunction ノードに使う関数を置いておくHLSLファイルを作成します。
名前はなんでも構いませんが、ここではCustomLighting.hlslとします。
CustomLighting.hlslには、以下の内容を入力してください。

CustomLighting.hlsl
#ifndef CUSTOM_LIGHTING_INCLUDED
#define CUSTOM_LIGHTING_INCLUDED

#ifndef SHADERGRAPH_PREVIEW
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#endif

void MainLight_float(out float3 Direction, out float3 Color)
{
    #ifdef SHADERGRAPH_PREVIEW
    Direction = float3(0.5, 0.5, 0);
    Color = 1;
    #else
    Light mainLight = GetMainLight();
    Direction = mainLight.direction;
    Color = mainLight.color;
    #endif
}

#endif

詳細な説明は省きますが、
Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl
にあるGetMainLight()という関数を呼び出し、ライトの情報を取得しています。

ただ、シェーダーグラフのプレビュー画面ではライトを取得できないため、
シェーダーグラフのプレビュー時には仮の値を返すようにしています。

STEP2 CustomFunction ノードの作成

次に、ShaderGraphに移り、CustomFunctionノードを追加します。
CustomFunctionノードの設定は画像の通りです。

  1. TypeをFileにする
  2. Sourceに先ほど作成したHLSLファイルを指定する
  3. Nameに関数名(上記コードではMainLight)を入力する
  4. Outputsに二つ追加し、Vector3型を指定する

プレビュー画面に黄色が表示されたら成功です。

STEP3 MainLightを使用してライティングする

本題ではないので詳細は省略しますが、
受け取ったデータを使ってDiffuseを計算します。
(ライトの方向と法線方向の内積にライトの色を掛けるだけです)

適当なマテリアルを作成して描画するとこうなります。

これで陰を描画することができました。
現時点で影は描画されていないことに注意してください。

影の描画

STEP4 HLSLファイルへの追記

影の描画に対応するために、hlslファイルを変更します。
CustomFunctionノードの設定は画像の通りです。

CustomLighting.hlsl
#ifndef CUSTOM_LIGHTING_INCLUDED
#define CUSTOM_LIGHTING_INCLUDED

#ifndef SHADERGRAPH_PREVIEW
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#endif

void MainLight_float(float3 WorldPos, out float3 Direction, out float3 Color, out float DistanceAtten, out float ShadowAtten)
{
#ifdef SHADERGRAPH_PREVIEW
    Direction = float3(0.5, 0.5, 0);
    Color = 1;
    DistanceAtten = 1;
    ShadowAtten = 1;
#else
    float4 shadowCoord = TransformWorldToShadowCoord(WorldPos);

    Light mainLight = GetMainLight(shadowCoord);
    Direction = mainLight.direction;
    Color = mainLight.color;
    DistanceAtten = mainLight.distanceAttenuation;
    ShadowAtten = mainLight.shadowAttenuation;
#endif
}

このコードは、影が有効な場合、シャドウマップから影をサンプリングして減衰率を出します。

STEP5 CustomFunction ノードの編集

先ほどと同様に、CustomFunctionノードを編集します。

  1. Inputに一つ追加し、Vector3型を指定する
  2. Outputに二つ追加し、float型を指定する

STEP6 ShaderGraphを編集する

まず、CustomFunctionノードの入力にPositionノードを接続します。

次に、DistanceAttenとShadowAttenを掛け算し、Diffuseの出力結果に掛け算します。


完成した全体像がこちらになります。
まだ影は描画されないことに注意してください。

STEP7 Keywordを設定する

影を描画する処理を書いたのに影が描画されないのは、
影を使うシェーダーであることを設定できていないからです。
キーワードを設定する方法は二種類あります。

STEP7-1 Brackboardsに追加する(非推奨)

一つ目は、ShaderGraphのBrackboardsにあるKeywordを設定する方法です。
設定するために、BrackboardsにBooleanのKeywordを追加します。

Keywordは以下のように設定して下さい。

  1. Referenceを_MAIN_LIGHT_SHADOWSにする
  2. DefinitionをMultiCompileにする
  3. ScopeをGlobalにする
  4. StagesをFragmentにする

    これを、
    _MAIN_LIGHT_SHADOWS_CASCADE
    _ADDITIONAL_LIGHT_SHADOWS
    _SHADOWS_SOFTDでも繰り返します。

STEP7-2 hlslファイルに定義する(23/9/6追記)

以下のように、hlslファイルに追記するだけでも影が受けられるようです。
こちらの方が簡単なので、本記事ではこちらの方法を推奨しておきます。

// CustomLighting.hlsl
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile _ _SHADOWS_SOFTD

完成

これで影が表示されるはずです。

影が表示されないときは

この方式は、ShadowCascadeに対応していません。URP AssetのCascade Countが1であることを確認してください。
(というか対応のさせ方がよくわからなかったので調査中です
もしご存じの方がいれば教えていただけるとありがたいです)

対応しました(23/9/6追記)

また、Directional LightのShadow TypeがNoShadowsになっていないかや、URPAssetのCast Shadowsにチェックが入っているかも確認してください。

参考

https://blog.unity.com/engine-platform/12-recipes-for-popular-visual-effects-using-universal-render-pipeline

Discussion