QUEST LAB

十二支 × 協力 × リアルタイムバトル『Play The Fox』を作っています。

【Unity】プログラミングを知っている人向けシェーダーのきほんのき

敵は赤みを帯びせて、見方は青みを帯びせたいなぁ。

と考えて、ふと思ったのがシェーダー。

聞いたことはあるものの一切触った事が無かったので、今回は『基本のき』レベルの内容について書いてみようと思う。

対象は、Unityは触ってるけどシェーダーは使った事ない人です。

シェーダーとは

この記事にたどり着いている時点で、知っている人が多いと思うので軽い説明を。

シェーダーはUnityのオブジェクトを実際に画面に表示する機能を担っています。

つまり、Rendererコンポーネントのあるオブジェクトはすべて標準でシェーダーを持っています

 

このシェーダーを換える事で画面に物体を表示するときに、見た目(主に色)をいじる事が出来ます。

くすんだ感じにしたいとか、光らせたいとか、何でもできます。

それを全部プログラミングで書けます。

簡単に説明

結局、Shaderはどこどんな色で塗るかを決めるだけです。

Shaderの持つ機能は次の3つ。

  1. Unityのゲームオブジェクトから、色を塗るべき「場所」と「画像の色」を取得する
  2. Unityのインスペクターで決めた値をもとに、その「場所」や「画像の色」に変更を加える
  3. 最終的に決まった「場所」と「色」の情報をUnityに出力する

シェーダーの書き方

Shader "Custom/Sprite-Minimum" {
    Properties {
        _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
    }

    SubShader{
        Tags { 
            "Queue"="Transparent"
        }
       
    ZWrite Off
        Blend One OneMinusSrcAlpha //乗算済みアルファ

        Pass {
        CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct VertexInput {
                float4 pos  :   POSITION;    // 3D座標
                float4 color:   COLOR;
                float2 uv   :   TEXCOORD0;   // テクスチャ座標
            };

            struct VertexOutput {
                float4 v    :   SV_POSITION; // 2D座標
                float4 color:   COLOR;  
                float2 uv   :   TEXCOORD0;   // テクスチャ座標
            };

            //プロパティの内容を受け取る
sampler2D _MainTex; float4 _Color;
VertexOutput vert (VertexInput input) { VertexOutput output; output.v = UnityObjectToClipPos(input.pos); output.uv = input.uv; //もとの色(SpriteRendererのColor)と設定した色(TintColor)を掛け合わせる output.color = input.color * _Color; return output; } float4 frag (VertexOutput output) : SV_Target { float4 c = tex2D(_MainTex, output.uv) * output.color; c.rgb *= c.a; return c; } ENDCG } } }

これがUnityのSprite Shaderの簡易版になります。

これを書くことで、Spriteの画像を画面に表示する事が出来ます。

基本的に緑色変数定義青色関数になります。

こちらのページよりコードを抜粋させていただいてます。

[http://hitsub.net/blog/2017/07/post-98/:embed:cite]

シェーダーにおける変数と関数

ここからコードを変数と関数で分けて説明します。

変数

コードの緑色で書いた部分です。

上から順に説明していきます。

Properties {
   _MainTex ("Sprite Texture", 2D) = "white" {}
   _Color ("Tint", Color) = (1,1,1,1)
}

ここでは、Unityからプロパティを受け取っています。

変数名 (インスペクターでの表示名, 型) = 初期値

のような形で記述します。

このPropertiesで書くと、public変数のようにUnityのInspectorから値を設定する事が出来るようになります。

struct VertexInput {
   float4 pos   :   POSITION;    // 3D座標
   float4 color:    COLOR;
   float2 uv    :   TEXCOORD0;   // テクスチャ座標
};

struct VertexOutput {
   float4 v :   SV_POSITION; // 2D座標
   float4 color:    COLOR;  
   float2 uv    :   TEXCOORD0;   // テクスチャ座標
};

ここで記述されたVertexInputVertexOutputは構造体です。

VertexInputはUnityから標準で渡される値で必要なものを、

VertexOutputはUnityへの出力の際に必要なものを定義しています。

VertexInputは上述のProperties同様Unityからの入力に使いますが、こちらはユーザーが設定するものではなくもともとUnityが用意しているものになります。

struct 構造体名 {
   型 変数名  :   Unityが用意した変数名(セマンティクス);
};

 のような形で記述します。

最後に、以下の文です。

//プロパティの内容を受け取る 
sampler2D _MainTex;
float4 _Color;

これは始めに書いた、Propaties内の_MainTex_Colorを関数内でも利用できるようにする為に書きます。

今はおまじないのようにPropaties同じ変数名を書いておけばよいです。

ただし、型名は少し変わっているので注意が必要です。

 

関数

コードの青色で書いた部分です。

VertexOutput vert (VertexInput input) {
   VertexOutput output;
   output.v = UnityObjectToClipPos(input.pos);
   output.uv = input.uv;

   //もとの色(SpriteRendererのColor)と設定した色(TintColor)を掛け合わせる
   output.color = input.color * _Color; 

   return output;
}

C#とほぼ同じ書き方なので何となく分かると思います。

vertはUnityから呼び出され、上記で定義した構造体VertexInputを引数として与えられます。

vertでは主に色を塗る「場所」に関する情報を編集します。

3D座標から、画面に出力する2D座標への変換などです。

float4 frag (VertexOutput output) : SV_Target {
   float4 c = tex2D(_MainTex, output.uv) * output.color;
   c.rgb *= c.a;
   return c;
}

fragvertの終了後に呼ばれ、引数としてvertの戻り値が与えられます。

こちらは、構造体VertexOutputが引数になります。

fragでは実際に塗る「」の情報をpixel単位で編集する事が出来ます。

 

vert頂点シェーダーfragフラグメントシェーダーと呼ばれています。

vert場所fragと覚えていれば大丈夫です。

 

おまけ

 

シェーダーについて、「きほんのき」の説明を書きました。

実際にシェーダーをUnityで利用するには、Materialを作成してシェーダーを設定するなどの作業が必要ですがその辺は別の記事で。

また、今回は変数と関数についての説明しかしていません。

それ以外の黒色で書かれたコードにも全て意味がありますので、徐々に理解していければと思います。

多分「何となくは分かったけど、で?」状態だとは思いますが、多少でもシェーダーの理解の手助けになれば幸いと思います。

 

いないとは思いますが、もし続きを書いてほしい人がいればコメントなりDMなりで教えてください。