Unityのシェーダの自由研究。SurfaceShader

#趣旨
うちの会社では、シェーダ等の案件が多いので、メンバーの教育のため
UNITYを使ってシェーダを勉強しよう！ 
という目的だったのだが
UNITYのシェーダは、組み込み関数など、独自に覚えることも多かったので
その部分を 勉強しながらまとめていく


とりあえず　今回は去年のUNITY
UNITY2017.1.0f3
なので　今は少しコードが変わっているかもしれない

#シェーダーの種類
UNITYでは

1. お手軽な SurfaceShader
1. 本格的な UnlitShader
1. ポストプロセス用 ImageEffectShader
1. GPGPU ComputeShader
1. シェーダーのプリロードを行う　ShaderVariantCollection

があるが、とりあえずは通常使う UnlitShaderから覚える
ただし、SurfaceShaderも簡単にふれておく


#SurfaceShaderを試す
##概要
本来は シェーダーでは　最低２つのシェーダ、頂点シェーダと フラグメントシェーダが必要である
が、SurfaceShaderでは、頂点シェーダーだけ記述し、フラグメントシェーダはビルトインを指定するだけで書かなくていい
もちろん　フラグメントシェーダも、カスタムライティングを使えば 独自に作る事ができるが
個人的には　それなら あとで話す UnlitShaderで　頂点シェーダとフラグメントシェーダを作ればいいと思う

##サンプルのモデル読み込み
今回は中野シスターズを使う（UnityChanをあえて使わない）
http://nakasis.com/

ファイルをダウンロードしてUNITYにインポートし、Sceneに貼り付ける

![1.png](https://qiita-image-store.s3.amazonaws.com/0/13916/7c1fa958-c316-9c12-6700-69c523058194.png)

##シェーダの確認
中野シスターズには　Standardというビルトインシェーダーが使われている
ビルトインシェーダーは下記からダウンロードできる
https://unity3d.com/jp/get-unity/download/archive

Standardシェーダの中身は

```cpp
Shader "Custom/NewSurfaceShader" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
       #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
       #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_CBUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_CBUFFER_END

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
```

##解析

###シェーダー名
```cpp
Shader "Custom/NewSurfaceShader" {  
```

シェーダー名。UNITYからはこの名前で呼べる

###Properties

```cpp
 Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
```

インスペクターで設定する値（パラメータ）

シェーダ内で識別する名前 ("インスペクターに表示する名前", 型 ) = デフォルト値

という形式だ


###条件

```cpp
 SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
```

SubShaderは複数作れる。
TagsやLODに条件を指定しており
SubShaderを上から順番に　最初に一致したSubShaderを実行する
全てに一致しない場合は 下記のFallbackが実行される

```cpp
 FallBack "Diffuse"
```

###コンパイルディレクティブ

```cpp
     CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
       #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
       #pragma target 3.0
```

UNITYマニュアルによると

```cpp
# pragma surface surfaceFunction lightModel [optionalparams]
```

上記の場合は 
サーフェースシェーダは surf　という関数
ライトモデルは Standard
Optionは　fullforwardshadows

ということになる

ライトモデルは　下記の４つがビルトインで用意されている

* Standard: 物理ベースレンダリング
* StandardSpecular： 物理ベース with スペキュラー
* Lambert： 非物理ベース
* BlinnPhong： 非物理ベース with スペキュラー

ちなみに fullforwardshadows のオプションは
フォワードレンダリング時の　影（ドロップシャドウ）を 通常はディレクショナルライト以外は表示しないが
このオプションがあれば　スポットライトやポイントライトでも影がつくらしい

###変数部

```cpp
    sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;


        UNITY_INSTANCING_CBUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_CBUFFER_END
```

変数（uniform）部分
_MainTex　等は　上記のインスペクターの値をここで定義してはじめて使えるようになる

UNITY_INSTANCING_CBUFFER_START
は c_bufferを使う時に使うマクロ

struct Input
は、SurfaceShaderにわたす入力構造体（attribute）だ
セマンティクスがないのも特徴だ


###Surfaceシェーダ本体

```cpp
        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
```

中身はパット見でわかるとおもうが、構造体に各パラメータを渡している

```cpp
struct SurfaceOutputStandard
{
    fixed3 Albedo;      // ベース (ディフューズかスペキュラー) カラー
    fixed3 Normal;      // 書き込まれる場合は、接線空間法線
    half3 Emission;
    half Metallic;      // 0=非メタル, 1=メタル
    half Smoothness;    // 0=粗い, 1=滑らか
    half Occlusion;     // オクルージョン (デフォルト 1)
    fixed Alpha;        // 透明度のアルファ
};
```

今回は ライトモデルに Standardを使っているので SurfaceOutputStandardを returnする

また
StandardSpecula、Lambert、BlinnPhong　の場合は　

```cpp 
struct SurfaceOutputStandardSpecular
{
    fixed3 Albedo;      // ディフューズ色
    fixed3 Specular;    // スペキュラー色
    fixed3 Normal;      // 書き込まれる場合は、接線空間法線
    half3 Emission;
    half Smoothness;    // 0=粗い, 1=滑らか
    half Occlusion;     // オクルージョン (デフォルト 1)
    fixed Alpha;        // 透明度のアルファ
};
```

```cpp
struct SurfaceOutput
{
    fixed3 Albedo;  // ディフューズ色
    fixed3 Normal;  // 書き込まれる場合は、接線空間法線
    fixed3 Emission;
    half Specular;  //  0..1 の範囲のスペキュラーパワー
    fixed Gloss;    // スペキュラー強度
    fixed Alpha;    // 透明度のアルファ
};
```

の構造体になる