UnityのShader Graphを少し触ってみる

はじめに

UnityのShader Graphをちょっとだけ触ってみたのでメモをします。

23

  • Unityのバージョン:2018.3.0b8

LWRPのプロジェクトの作成

Shader Graphを簡単に試すにはLWRPのプロジェクトを作るのが楽なようです。

0

テンプレートから作成したままだとバージョンが古いようなので LWRPのバージョンをPackage Managerウィンドウから上げます。

1

Shader Graphを触ってみる

「Create > Shader > PBR Graph」を選んでみます。

2

インスペクタに「Open Shader Editor」というボタンがあります。 このボタンを押してエディターを開きます。

3

4

「PBR Master」というノードがあります。 Surface ShaderのようにPBRのパラメータを渡すとライティングを行ってくれるようです。

Spaceキーを押すか右クリックから「Create Node」で新しくノードが作れます。

5

「Procedual」から「Checkerboard」ノードを作成してAlbedoにつないでみます。

6

「Save Asset」を押すことで変更が保存されるようです。

7

通常のシェーダと同じようにマテリアルを作成して割り当ててみます。

8

Shader Graphで作成したシェーダのマテリアルを使うことができました。

左上のウィンドウの「+」を押すとプロパティが追加できるようです。

9

テクスチャのプロパティを追加してみました。

10

追加したプロパティは「Properties」から利用できます。

11

「Sample Texture 2D」ノードも追加してつないでみます。

12

保存をするとマテリアルのインスペクタにプロパティが追加されます。

13


「Create > Shader > Sub Graph」を選んでみます。 「Sub Graph」は.shaderファイルではないようです。

14

さきほどと同様に「Open Shader Editor」でエディタを開きます。

15

SubGraphOutputsというノードがあります。 とりあえず適当にノードを作成します。

16

適当にプロパティのVector4を2つ足し合わせるようなノードを作りました。


「Create > Shader > Unlit Graph」を選んでみます。

17

「Unlit Master」ノードがあります。 「PBR Graph」のときとは異なり、ライティングは行われなくて 直接カラーを出力するようになっています。

「Sub Graph」から先ほど作成したSub Graphを追加します。

18

19

ちゃんと加算されているのがわかります。

生成されるコードの確認

簡単なノードを作成して生成されるコードを確認してみます。 次のようなノードを作成しました。

20

「Unlit Graph」でVector4を2つ用意してBlendで混ぜてカラーにつないでいます。

Masterノードを右クリックして「Show Generated Code」をクリックします。

21

生成されたコードから関係している部分を抜き出してみると次のとおりです。

void Unity_Blend_Overwrite_float4(float4 Base, float4 Blend, out float4 Out, float Opacity)
{
    Out = lerp(Base, Blend, Opacity);
}

...

SurfaceDescription PopulateSurfaceData(SurfaceDescriptionInputs IN)
{
    SurfaceDescription surface = (SurfaceDescription)0;
    float4 _Vector4_B16DF71F_Out = float4(0,0,0,0);
    float4 _Vector4_EE968469_Out = float4(1,1,1,1);
    float4 _Blend_39388718_Out;
    Unity_Blend_Overwrite_float4(_Vector4_B16DF71F_Out, _Vector4_EE968469_Out, _Blend_39388718_Out, 0.5);
    surface.Color = (_Blend_39388718_Out.xyz);
    surface.Alpha = 1;
    surface.AlphaClipThreshold = 0;
    return surface;
}

...

half4 frag (GraphVertexOutput IN ) : SV_Target
{
    UNITY_SETUP_INSTANCE_ID(IN);

    // Pixel transformations performed by graph
    float3 WorldSpacePosition = IN.WorldSpacePosition;
    float3 WorldSpaceNormal = IN.WorldSpaceNormal;
    float3 WorldSpaceTangent = IN.WorldSpaceTangent;
    float3 WorldSpaceBiTangent = IN.WorldSpaceBiTangent;
    float3 WorldSpaceViewDirection = IN.WorldSpaceViewDirection;
    float4 uv1 = IN.uv1;


    SurfaceDescriptionInputs surfaceInput = (SurfaceDescriptionInputs)0;
    // Surface description inputs defined by graph


    SurfaceDescription surf = PopulateSurfaceData(surfaceInput);
    float3 Color = float3(0.5, 0.5, 0.5);
    float Alpha = 1;
    float AlphaClipThreshold = 0;
    // Surface description remap performed by graph
    Color = surf.Color;
    Alpha = surf.Alpha;
    AlphaClipThreshold = surf.AlphaClipThreshold;


#if _AlphaClip
    clip(Alpha - AlphaClipThreshold);
#endif
#ifdef _ALPHAPREMULTIPLY_ON

    Color *= Alpha;
#endif
    return half4(Color, Alpha);
}

PopulateSurfaceDataでつないだノードのとおりに計算されているようです。

サンプルをみてみる

Unity公式のサンプルがあるのでそれを見てみることにします。

ダウンロードしてAssets以下をコピーします。

Assets/Scenesにシーンが置かれています。 Assets/ShaderGraph内にシェーダが置かれています。

さまざまなサンプルが用意されていて参考になります。

ノードを作成する

Code Function Node APIを利用すると独自のノードも作れるようです。

試しに簡単なノードを作成してみます。

using System.Reflection;
using UnityEditor.ShaderGraph;
using UnityEngine;

[Title ("Custom", "My Custom Node")]
public class MyCustomNode : CodeFunctionNode {

    public MyCustomNode () {
        name = "My Custom Node";
    }

    protected override MethodInfo GetFunctionToConvert () {
        return GetType ().GetMethod ("MyCustomFunction",
            BindingFlags.Static | BindingFlags.NonPublic);
    }

    static string MyCustomFunction (
        [Slot (0, Binding.None)] DynamicDimensionVector A,
        [Slot (1, Binding.None)] DynamicDimensionVector B,
        [Slot (2, Binding.None)] Vector1 C,
        [Slot (3, Binding.None)] out DynamicDimensionVector Out) {
        return @"
{
    // カスタムノードのテスト
    Out = lerp(A, B, C);
}
";
    }

}

作成したノードを利用してみます。

23

生成されたコードを確認してみます。

...
void MyCustomFunction_float4(float4 A, float4 B, float C, out float4 Out)
{
    // カスタムノードのテスト
    Out = lerp(A, B, C);
}
...
SurfaceDescription PopulateSurfaceData(SurfaceDescriptionInputs IN)
{
    SurfaceDescription surface = (SurfaceDescription)0;
    float4 _Vector4_3B7E9305_Out = float4(0,0,0,0);
    float4 _Vector4_7C700306_Out = float4(1,1,1,1);
    float4 _MyCustomNode_77275424_Out;
    MyCustomFunction_float4(_Vector4_3B7E9305_Out, _Vector4_7C700306_Out, 0.5, _MyCustomNode_77275424_Out);
    surface.Color = (_MyCustomNode_77275424_Out.xyz);
    surface.Alpha = 1;
    surface.AlphaClipThreshold = 0;
    return surface;
}

確かに自分の作成したノードが使われていることがわかります。

おわりに

ノードベースでシェーダをいじるのは手軽で面白いですね。 自分でノードを作成できるのも拡張性があってすばらしいです。