【Unity】Surfaceシェーダーとフラグメントシェーダーの違いを軽くまとめてみた

# はじめに
Unityのシェーダーへの理解を深めるため、Surfaceシェーダーとフラグメントシェーダーの違いを軽くまとめてみました。

# Surfaceシェーダーとフラグメントシェーダー
## Surfaceシェーダー
**Surface**シェーダーを使うとライティングやシャドウを考慮したシェーダーを**簡単に**書くことができます。

## 頂点シェーダー/フラグメントシェーダー
複雑なことをやりたい場合はこちらのシェーダー。

こちらはSurfaceシェーダーと比べてコードが**複雑**になりやすいです。

UnlitシェーダーやImageEffectシェーダーはこちらに含まれます。

# 両者の書き方の違い
筆者はこれらのシェーダーのどこが違うのかよくわからなかったので、書き方の違いを自分なりに軽くまとめてみました。

## 主な違い
具体的には以下のような違いがあります。　他にもあるかもしれません。
・コンパイルディレクティブの書き方の違い
・Passブロックの有無
・構造体データの書き方の違い
・vertexシェーダー関数の有無
・vertexシェーダー関数の書き方の違い
・フラグメントシェーダー関数の書き方の違い
・ライティングの記述方法の違い

## コンパイルディレクティブの書き方の違い
<img height = 200 src = "https://qiita-image-store.s3.amazonaws.com/0/44288/c37f6c3a-2df9-fdfd-b678-665192585655.png">


#### **Surfaceシェーダーの場合**
以下のような書き方をします。

```shader:コンパイルディレクティブの書き方その1
#pragma surface surf Standard fullforwardshadows vertex vert
```

```shader:コンパイルディレクティブの書き方その2
#pragma surface surf Standard fullforwardshadows
#pragma vertex vert
```

Surfaceシェーダーでは、シェーダー関数指定時に**ライティングモデル**を指定する必要があります。

サンプルでは**Standard**がライティングモデルになっています。

参考: https://docs.unity3d.com/jp/540/Manual/SL-SurfaceShaders.html

#### **フラグメントシェーダーの場合**
以下のような書き方をします。

```shader:コンパイルディレクティブの書き方
#pragma vertex vert
#pragma fragment frag
```


参考: https://docs.unity3d.com/jp/540/Manual/SL-ShaderPrograms.html

## Passブロックの有無
#### **Surfaceシェーダーの場合**
・Passを書けない

#### **フラグメントシェーダーの場合**
・Passを書く必要がある
・複数のPassが記述可能


参考: https://docs.unity3d.com/jp/540/Manual/SL-Pass.html

## 構造体データの書き方の違い
#### **Surfaceシェーダーの場合**
以下のような書き方をします

```shader:構造体データの記述
struct Input {
  float2 uv_MainTex;
  float2 uv_BumpMap;
};
```

参考: https://docs.unity3d.com/jp/540/Manual/SL-SurfaceShaders.html

#### **フラグメントシェーダーの場合**
以下のような書き方をします

```shader:構造体データの記述
struct v2f {
    half3 worldRefl : TEXCOORD0;
    float4 pos : SV_POSITION;
};
```

```TEXCOORD0``` のようなセマンティクスを書く必要があります。

参考: https://docs.unity3d.com/jp/540/Manual/SL-VertexFragmentShaderExamples.html

## vertexシェーダー関数の有無
#### **Surfaceシェーダーの場合**
vertexシェーダー関数は**書かなくてもいい**

#### **フラグメントシェーダーの場合**
vertexシェーダー関数を**書く必要がある**

## vertexシェーダー関数の書き方の違い
#### **Surfaceシェーダーの場合**
以下のような書き方をします

```shader:vertexシェーダー関数の書き方その1
void vert (inout appdata_full v) {
    v.vertex.xyz += v.normal * _Amount;
}
```

```shader:vertexシェーダー関数の書き方その2
void myvert (inout appdata_full v, out Input data)
{
    UNITY_INITIALIZE_OUTPUT(Input,data);
    float4 hpos = UnityObjectToClipPos(v.vertex);
    hpos.xy/=hpos.w;
    data.fog = min (1, dot (hpos.xy, hpos.xy)*0.5);
}
```

**計算結果を構造体データへ格納**しているのが大きな特徴です。

参考:https://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html

#### **フラグメントシェーダーの場合**
以下のような書き方をします。

```shader:vertexシェーダー関数の書き方その1
v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
}
```

```shader:vertexシェーダー関数の書き方その2
v2f vert (float4 vertex : POSITION, float3 normal : NORMAL)
{
    v2f o;
    o.pos = UnityObjectToClipPos(vertex);
    o.worldPos = mul(_Object2World, vertex).xyz;
    o.worldNormal = UnityObjectToWorldNormal(normal);
    return o;
}
```

**計算結果を構造体データとしてreturn**しているのが大きな特徴です。

参考: https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html

## サーフェースシェーダー関数(フラグメントシェーダー関数)の書き方の違い
#### **Surfaceシェーダーの場合**

``` shader:サーフェースシェーダー関数の書き方その１
void surf (Input IN, inout SurfaceOutput o) {
    o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
```

``` shader:サーフェースシェーダー関数の書き方その２
void surf (Input IN, inout SurfaceOutput o) {
    clip (frac((IN.worldPos.y+IN.worldPos.z*0.1) * 5) - 0.5);
    o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
    o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
}
```

**計算結果を構造体データの中へ格納**しているのが特徴です。


参考: https://docs.unity3d.com/Manual/SL-SurfaceShaderExamples.html

#### **フラグメントシェーダーの場合**

``` shader:フラグメントシェーダー関数の書き方その１
fixed4 frag (v2f i) : SV_Target
{
    fixed4 col = tex2D(_MainTex, i.uv);
    col *= i.diff;
    return col;
}
```

``` shader:フラグメントシェーダー関数の書き方その２
fixed4 frag (v2f i) : SV_Target
{
    fixed4 col = tex2D(_MainTex, i.uv);
    fixed shadow = SHADOW_ATTENUATION(i);
    fixed3 lighting = i.diff * shadow + i.ambient;
    col.rgb *= lighting;
    return col;
}
```

**計算結果をカラー値としてreturn**しているのが特徴です。

参考: https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html

## ライティングの記述方法の違い
#### **Surfaceシェーダーの場合**
以下のように**パラメータを指定するだけ**でライティングを記述することができます。

```shader:Surfaceシェーダーのライティング例
#pragma surface surf SimpleLambert
  
half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) {
    half NdotL = dot (s.Normal, lightDir);
    half4 c;
    c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
    c.a = s.Alpha;
    return c;
}
```

参考: https://docs.unity3d.com/jp/540/Manual/SL-SurfaceShaderLightingExamples.html

#### **フラグメントシェーダーの場合**
ライティング処理を**自分で書く**必要があります。

参考: https://docs.unity3d.com/ja/540/Manual/SL-VertexFragmentShaderExamples.html

#その他
## appdataについて
**appdata**の正式な名前は **Application to Vertex Shader Structure** らしいです。

参考: http://wiki.unity3d.com/index.php?title=Shader_Code#Application_to_Vertex_Shader_Structure_.28appdata.29

## v2fについて
**v2f**の正式な名前は**Vertex Shader to Fragment Shader Structure**らしいです。

参考: http://wiki.unity3d.com/index.php?title=Shader_Code#Vertex_Shader_to_Fragment_Shader_Structure_.28v2f.29

#さいごに
筆者はシェーダーに関しての知識が浅いので、間違っているところがあるかもしれません。
間違いを見つけた場合は教えていただけると嬉しいです。


# 参考URL
Unity のシェーダの基礎を勉強してみたのでやる気出してまとめてみた
http://tips.hecomi.com/entry/2014/03/16/233943

Shader Code - Unify Community Wiki
http://wiki.unity3d.com/index.php?title=Shader_Code#Application_to_Vertex_Shader_Structure_.28appdata.29

[Unity] Vertex/Fragment shaderで通常のライティングとシャドウを適用するサンプル
http://qiita.com/edo_m18/items/ee4b9018e860c11199b0