Search Unity

シェーダーグラフのカスタムライティング:Unity 2019 でグラフを拡張する

, 7月 31, 2019

Unity エディター 2019.1 で、シェーダーグラフパッケージが正式にリリースされました!続く Unity 2019.2 では、シェーダーグラフに新機能が追加され、既存機能も強化されています。

Unity 2019 での変更点

カスタム関数とサブグラフのアップグレード

シェーダーグラフ内でカスタムコードを管理できるように、新しく Custom Function ノードが追加されました。このノードでは、カスタム入力と出力を定義したり、並べ替えたりすることができるほか、カスタム関数をノード自体に直接記入、あるいは外部ファイルを参照して挿入できます。

この変更に合わせてサブグラフもアップグレードされ、各種タイプ、カスタム名、並べ替え可能なポートを持つ独自の出力をサブグラフに対して定義できるようになりました。さらに、サブグラフのブラックボードが、メイングラフでサポートされているすべてのデータタイプに対応しました。

カラーモードと精度モード

シェーダーグラフで高度なシェーダーを作成、最適化しやすくなりました。Unity 2019.2 では、グラフ内の計算の精度を、グラフ全体またはノードごとに手動で設定できます。また、新機能のカラーモードを使用すれば、精度のフローやノードのカテゴリの視覚化、独自のカスタムカラーの表示を短時間で簡単に行えます。

これらの新機能の詳細については、シェーダーグラフに関するドキュメントを参照してください。

サンプルプロジェクト

 

新しくなったカスタム関数ワークフローをすぐに使い始められるよう、サンプルプロジェクトとステップバイステップ形式の手順説明をご用意しました。Unity のリポジトリからプロジェクトをダウンロードし、下記の説明に従って機能を確認してください。このプロジェクトでは、カスタム関数ノードを使用して、ライトウェイトレンダーパイプライン(LWRP)のカスタムライティングシェーダーを作成する方法について説明します。新規プロジェクトを作成して下記手順を実行する場合は、バージョン 2019.2 のエディターとバージョン 6.9.1 以降の LWRP パッケージを使用してください。

メインライトからデータを取得する

はじめに、シーンのメインライトから情報を取得する必要があります。「Create」>「Shader」>「Unlit Graph」の順に選択して、新しい Unlit シェーダーグラフを作成します。「Create Node」メニューで、新しく追加された「Custom Function」ノードを選択し、右上にある歯車アイコンをクリックしてノードメニューを開きます。

このメニューでは、入力と出力を追加できます。DirectionColor という名前の 2 つの出力ポートを追加し、各ポートで「Vector 3」を選択します。「undeclared identifier」というエラーフラグが表示されても、心配はいりません。コードを入力すればこのエラーは解消されます。「Type」ドロップダウンメニューで「String」を選択します。関数名を入力します。今回は、「MainLight」という名前を付けましょう。それでは、テキストボックスにカスタムコードを入力していきましょう。

まず、#ifdef SHADERGRAPH_PREVIEW という名前のフラグを作成します。ノードのプレビューボックスからライトのデータにアクセスすることはできないので、グラフ内のプレビューボックスで表示する内容をノードに指示しなければなりません。#ifdef により、状況に応じて異なるコードを使うようにコンパイラーに命令するわけです。まずは、出力ポートのフォールバック値を定義しましょう。

次に #else を使用して、プレビュー時以外に行うべき処理をコンパイラーに指示します。実際にライトのデータを取得するのはここになります。ここで使用するのは、LWRP パッケージの組み込み関数である GetMainLight() です。この情報を使用することによって、DirectionColor に出力を割り当てることができます。現段階のカスタム関数は次のようになります。

それでは、このノードが実行する処理をひと目で確認できるように、ノードをグループに追加しましょう。ノードを右クリックして「Create Group from Selection」を選択し、グループのタイトルをノードの動作内容がわかるようなものに変更します。今回は、「Get Main Light」と入力します。

これでライトのデータが手に入ったので、シェーディングを計算できるようになりました。さっそく、標準的なランバートライティングを作成してみましょう。まず、ワールド法線ベクトルとライト方向のドット積を取ります。ドット積の出力を「Saturate」ノードに渡し、ライトのカラーを乗算します。乗算結果を「Unlit Master」ノードの「Color」ポートにつなげば、プレビューが更新され、簡単なカスタムシェーディングが表示されます!

カスタム関数のファイルモードを使用する

カスタム関数ノードを使用してライトのデータを取得する方法がわかったので、次は関数を拡張していきましょう。メインライトから方向と色だけでなく、減衰に関する値も取得するように関数を更新します。

今回は関数の複雑さが増すので、ファイルモードに切り替えて HLSL のインクルードファイルを使用することにします。こうすることで、複雑な関数を適切なコードエディターで作成してから、グラフに挿入できます。これには、コードのデバッグを一箇所で行えるようになるという利点もあります。

まず、プロジェクトの「Assets」>「Include」フォルダー内にある CustomLighting ファイルを開きます。今回は MainLight_half 関数のみに注目します。この関数は次のようになっています。

この関数に新しい入出力のデータが含まれているので、先ほどの Custom Function ノードに戻ってこれらを追加しましょう。DistanceAtten(距離減衰)と ShadowAtten(影の減衰率)用に新しい出力を 2 つを追加します。また、WorldPos(ワールド座標)の入力も新しく追加します。これで入力と出力が揃い、インクルードファイルを参照する準備が整いました。「Type」ドロップダウンメニューを「File」に変更します。「Source」入力で先ほどのインクルードファイルを見つけて、参照するアセットを選択します。次に、使用する関数をノードに指示しなければなりません。「Name」ボックスに「MainLight」と入力します。

インクルードファイルでは関数名の末尾に _half が付いていたのに、入力した名前には付けていないことに注意してください。この理由は、シェーダーグラフのコンパイラーでは、各関数名の末尾に精度形式が自動で付けられるためです。独自の関数を定義するのですから、そのソースコードでは、関数で使用する精度形式をコンパイラーに伝えなければなりません。しかし、ノードでは、メイン関数の名前を参照するだけで済みます。関数をコピーして「float」の値を使用するように変更し、float 精度モードでコンパイルすることも可能です。「精度」カラーモードを利用すれば、グラフの各ノードに設定されている精度を簡単に識別できます。青色は float、赤色は half を表します。

このカスタム関数をまた別の機会に使用したくなった場合に備えて、再利用可能にしておきましょう。方法は簡単で、関数をサブグラフにラップするだけです。ノードとそのグループを選択して右クリックし、「Convert to Sub-graph」を選択します。この例では、サブグラフの名前は「Get Main Light」にしました。作成したサブグラフを開き、サブグラフの出力ノードに必要な出力ポートを追加して、ノードの出力をサブグラフの出力に接続します。その後、ワールド座標ノードを追加して、入力に接続します。

サブグラフを保存して、Unlit グラフに戻ります。既存のロジックに、新しく 2 つの Multiply ノードを追加していきます。まず、2 つの減衰の出力を乗算します。次に、その出力にライトのカラーを乗算します。この出力を先ほど作成した NdotL と乗算すれば、基本的なシェーディングにおける減衰を適切に計算することができます。

ダイレクトスペキュラーシェーダーを作成する

これまでの手順で作成したシェーダーは、マットなオブジェクトには最適です。しかし、光沢が欲しい場合には向きません。それなら、シェーダーに独自のスペキュラー計算を追加しましょう!この手順では、サブグラフにラップされている別のカスタム関数ノード、Direct Specular を使用します。再び CustomLighting インクルードファイルを開きます。ファイル内から別の関数を参照していることに注目してください。

この関数では、単純なスペキュラー計算を行います。詳細に興味がある方は、こちらをご覧ください。この関数のサブグラフには、ブラックボード上の入力も複数含まれています。

新しいノードに、関数に対応した適切な入力ポートと出力ポートを設定しましょう。ブラックボードにプロパティを追加する方法はシンプルで、右上にある「Add (+)」アイコンをクリックし、データタイプを選ぶだけです。カプセルをダブルクリックして入力の名前を変更してから、ドラッグアンドドロップしてグラフに追加します。最後に、サブグラフの出力ポートを更新して保存します。

スペキュラー計算の用意ができたので、Unlit グラフに戻って「Create Node」メニューからこの計算を追加しましょう。「Attenuation」の出力を「Direct Specular」サブグラフの「Color」入力に接続します。次に、Get Main Light 関数の「Direction」出力をサブグラフの「Direction」入力に接続します。最後に、「NdotL」と「Attenuation」の乗算結果を「Direct Specular」サブグラフの出力に加算し、その結果を「Color」出力に接続します。

これで光沢を追加できました!

複数のライトを使用する

LWRP のメインライトとは、オブジェクトに対して最も明るいディレクショナルライト(通常は太陽)のことを指します。性能の低いハードウェアでのパフォーマンスを改善するため、LWRP ではメインライトと他のライトの計算を分けて行っています。最も明るいディレクショナルライトだけでなく、シーン内のすべてのライトについてシェーダーで適切な計算を行うには、関数内にループを作成する必要があります。

別のライトのデータを取得するために、新しいサブグラフを使用して新しいカスタム関数ノードをラップします。まずは CustomLighting インクルードファイルの AdditionalLight_float 関数を見てください。

先ほどと同様に、カスタム関数ノードのファイル参照で AdditionalLights 関数を指定し、適切な入力と出力をすべて作成してください。ノードをラップするサブグラフのブラックボードで、スペキュラーのカラースペキュラーのスムースネスを公開してください。サブグラフで、Position ノード、Normal Vector ノード、View Direction ノードをそれぞれワールド座標ワールド法線ワールド空間のビュー方向に接続します。

関数の用意ができたら、さっそく使いましょう!まず、前手順までのメインの Unlit グラフを選択し、まとめてサブグラフに変換します。ノードを選択して右クリックし、「Convert to Sub-graph」を選択します。最後の「Add」ノードを削除し、出力をサブグラフの出力ポートに接続します。この際には、スペキュラースムースネス用の入力プロパティも作成することをお勧めします。

次に、メインライトの計算と他のライトの計算を組み合わせます。メインの Unlit グラフで、メインライトの計算と並行して他のライトの計算を実行するためのノードを作成してください。そのうえで、メインライトと残りのライトの「Diffuse」出力どうし、「Specular」出力どうしを、それぞれ足し合わせます。これで完成です!

シンプルなトゥーンシェーダーを作成する

さて、LWRP プロジェクトのシーン内の全ライトからデータを取得する方法はわかりましたが、これをどのように使えばいいのでしょう?シェーダーでカスタムライティングを利用する場面として最もよくあるものとしては、従来のトゥーンシェーダーが挙げられます。

すべてのライトデータがあれば、トゥーンシェーダーはとても簡単に作成できます。まず、ここまでに作成したライト計算をすべて選択し、再びサブグラフにラップします。こうすることで、完成版のシェーダーが見やすくなります。最後の「Add」ノードを削除し、「Diffuse」と「Specular」をそれぞれサブグラフの別々の出力ポートに接続することも忘れないでください。

トゥーンシェーディングの作成方法は数多くありますが、今回は、ライトの輝度を基にランプテクスチャーからカラーを検索することにします。一般的には、この手法はランプライティングと呼ばれています。ランプライティングに必要なテクスチャーアセットのサンプルは、サンプルプロジェクトに含まれています。自身でグラデーションをサンプリングして、ランプライティングで動的なランプを使用してもかまいません。

最初の手順は、DiffuseSpecular の強度を RGB 値から HSV 値に変換することです。こうすることで、ライトのカラーの強度(HSV)値を基にシェーダーの輝度を決定できるようになります。また、アセットの水平軸に沿ったさまざまなスポットでテクスチャをサンプリングしやすくなります。UV の Y チャンネルには静的値を使用して、画像の上から下までのどの部分をサンプリングの対象とするかを指定します。

この静的値をインデックスとして使用することで、単一のテクスチャアセット内でプロジェクトの複数のライティングランプを参照できます

UV 値を設定したら、Sample Texture 2D LOD ノードを使用してランプテクスチャのサンプリングを行います。この LOD というのがポイントです。通常の Sample Texture 2D ノードを使用した場合、シーン内で自動的にランプにミップマッピングが適用されてしまい、遠くにあるオブジェクトではライティングの挙動が変わってしまうからです。Sample Texture 2D LOD ノードを使用すれば、ミップレベルを手動で指定できます。さらに、ランプテクスチャーの高さはわずか 2 ピクセルなので、テクスチャー用に独自の SamplerState も作成します。テクスチャのサンプリングが適切に行われるように、「Filter」は「Point」、「Wrap」は「Clamp」に設定します。テクスチャアセットを変えた場合に設定を変更できるように、これをブラックボードのプロパティとして公開しておきます。

最後に、オブジェクトの色を変えられるように、ディフューズ計算のランプサンプルと色のプロパティである Diffuse を乗算します。スペキュラー計算のランプサンプルを Diffuse 出力に加算して、最終的なカラーをマスターノードに接続します。

カスタムライティングを拡張する

今回作成したカスタムライティング設定はシンプルなものですが、拡張すれば、あらゆるシーンのいろいろなユースケースに応用できます。サンプルプロジェクトには、このカスタムライティング設定を使用したシェーダーで構成した完成版シーンが含まれています。また、頂点アニメーションやシンプルなサブサーフェススキャッタリング近似のほか、深さを利用した屈折とカラーリングも含まれています。もっと高度なテクニックを身につけたい方は、プロジェクトをダウンロードして、Example アセットをご覧ください。

学び続けよう

シェーダーグラフとこの機能で作成できるシェーダーについて質問や意見がある場合は、新しく生まれ変わったフォーラムスペースにアクセスしてください!また Discord のコミュニティでも、コミュニティのメンバーや(ときおり)開発者が交流していますので、こちらもぜひご覧ください。

最後に、SIGGRAPH 2019 のセッションをお忘れなく!シェーダーグラフを使用したカスタムライティングの作成方法をさらに詳しく説明します。

23 コメント

コメントの配信登録

コメント受付を終了しました。

  1. Hello there! I’m trying to follow the tutorial, but when I try to use the Subgraph created to calculate the additional lights an error shows up. It sais that is an invalid subgraph asset. I tried to change things to make it work but there is no way. Maybe it’s something about the version (2019.2.6f1) because I can’t create a subgraph from a graph and have to do it manualy or maybe I don’t understand that part of the tutorial. Anyway, any help will be well recibed

  2. Noah Bannister

    9月 23, 2019 3:22 pm

    I have done a quick write-up on creating an intermediate Lightweight PBR node for use in unlit graphs to achieve custom effects. I’m hoping it can help some people out. https://noahbannister.blog/2019/09/23/unity-lwrp-pbr-shader-node/

  3. lars bertram

    9月 9, 2019 8:54 pm

    looking for a way to add custom lighting i gave this approach a try.
    unfortunately i had to realize that shadows actually do not work as already posted by Fries.
    the reason is obvious (missing _MAIN_LIGHT_SHADOWS keyword), so i also had a look into the project of natalie burke which somehow solves this problem to a certain degree: it does not support cascaded shadows. and soft shadows of course.
    so i looked into another approach – whcih turned out to work well: using the pbr master node instead!
    more about this soon.

  4. This is a nice and helpful tutorial. However I am stuck with the subgraphs as one other user commented already below: “the subgraph is an invalid asset with some GUID number”. I can’t get the subgraphs to work at all because of this. I am using Unity 2019.2.1f1 with LWRP.

  5. Shadows are not working in Unity 2019.2.0f1 in the custom shaders.
    For some reason the shadowAttenuation is always 1.
    I noticed that the custom shaders don’t get the keyword “_MAIN_LIGHT_SHADOWS” set to true.

    A workaround I found is to edit the file “Packages/Lightweight RP/ShaderLibrary/Lighting.hlsl”:

    Line 185:
    #if !defined(_MAIN_LIGHT_SHADOWS) || defined(_RECEIVE_SHADOWS_OFF)

    changed to:
    #if defined(_RECEIVE_SHADOWS_OFF)

    This is not a viable solution as this is only working in editor

    1. Sorry, the line is not found in …/Lighting.hlsl but in
      ““Packages/Lightweight RP/ShaderLibrary/Shadows.hlsl”

  6. With this version, I can’t open any 2019.1 graphs. Are they definitly incompatible ? Thanks.

  7. Looks nice and the instructions are clear, but even when I download the project folder I can’t manage to get this working. Everything is just purple and when I try to do the hlsl and subshader by myself, I alsways get an error that the subgraph is an invalid asset with some GUID number. Unity 2019.2.0f1 and ShaderGraph 6.9.1 are installed. But im fairly new to Unity, maybe I miss something, so I will try later again.
    Nontheless, thanks for this cool tutorial. I was a looking for a tutorial like this, for the current Unity Version, for a long time..

  8. Does this work with the new 2D lights?

  9. nice

  10. Bastien Giafferi

    8月 1, 2019 10:58 am

    Is there any to have similar results with HDRP but I can’t manage to get the main light direction. Any ideas?

  11. Felix Herbst

    8月 1, 2019 10:43 am

    It would be great if you could, in such blog posts, actually show examples that don’t work by “magic”. The one above just works because the ShaderGraph around is #including all the necessary files itself, automagically making all those Lighting calls work.

    However, if you want to do something a bit more involved – say, access something that is not included already – this structure breaks pretty quickly with redefinitions, multiple definitions of the same stuff, … At least you should point out why stuff “just works” in this example.

  12. It would be great to have an ability to set/get variables in Shader Graph to better organaize everything.

  13. Is this even possible to do in HDRP?

  14. Pierre-Henri BARRALIS

    7月 31, 2019 5:44 pm

    “LWRP 6.10.0 or higher.”,
    6.10.0 isn’t out yet (at the time I’m writing this), the highest is 6.9.1.

    1. Alex Lindman

      7月 31, 2019 5:59 pm

      Sorry, looks like a typo. The project and examples are compatible with 6.9.1 .

      1. Pierre-Henri BARRALIS

        7月 31, 2019 7:14 pm

        thanks for the clarification ! Also the unity project on github is targetting 2019.2.0b10, you might want to change it :)
        Otherwise, the project and examples are awesome! Thanks a lot!
        At first I’d hoped that we could change how the lighting is computed in the for loop (LightingLambert and LightingSpecular calls) directly in ShaderGraph. I know this would require being able to do for loops in ShaderGraph, which is difficult itself. Do you see this as something that could be done in the future or is it never going to happen ?
        Thanks

      2. Pierre-Henri BARRALIS

        7月 31, 2019 7:19 pm

        Thanks for the clarification Alex! Also the unity github project is targetting 2019.2.0b10, you might want to change it :)
        Otherwise the project and examples are awesome, thanks for the work!
        I’d hoped that we could change how the lighting is computed in the for loop (changing LightingLambert and LightingSpecular) without having to write any HLSL (directly in SG). I know this would require the ability to write for loop in nodes, and it’s complicated.
        Do you see this as a something that could be done in the future, or is it never going to happen ?
        Thanks Alex

      3. Pierre-Henri BARRALIS

        7月 31, 2019 7:20 pm

        Doing a triple-post to ask if you can remove this and the double post above (as I can’t do it), as it took a few minutes to appear I thought it was a bug and rewrote it.

  15. Wendelin REICH

    7月 31, 2019 5:21 pm

    Does this update include the ability to turn off ‘Receive Shadows’? Not having this basic ability is currently a dealbreaker for me.

    I’m asking this here because I’m not getting a response anymore on this issue from your colleagues (see the following thread: https://forum.unity.com/threads/turn-off-receive-shadows-on-custom-pbr-graph-lwrp.657814/).