search
LoginSignup
45

More than 5 years have passed since last update.

posted at

updated at

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

はじめに

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

Surfaceシェーダーとフラグメントシェーダー

Surfaceシェーダー

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

頂点シェーダー/フラグメントシェーダー

複雑なことをやりたい場合はこちらのシェーダー。

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

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

両者の書き方の違い

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

主な違い

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

コンパイルディレクティブの書き方の違い

Surfaceシェーダーの場合

以下のような書き方をします。

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

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

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

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

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

以下のような書き方をします。

コンパイルディレクティブの書き方
#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シェーダーの場合

以下のような書き方をします

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

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

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

以下のような書き方をします

構造体データの記述
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シェーダーの場合

以下のような書き方をします

vertexシェーダー関数の書き方その1
void vert (inout appdata_full v) {
    v.vertex.xyz += v.normal * _Amount;
}
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

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

以下のような書き方をします。

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;
}
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シェーダーの場合

サーフェースシェーダー関数の書き方その1
void surf (Input IN, inout SurfaceOutput o) {
    o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
サーフェースシェーダー関数の書き方その2
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

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

フラグメントシェーダー関数の書き方その1
fixed4 frag (v2f i) : SV_Target
{
    fixed4 col = tex2D(_MainTex, i.uv);
    col *= i.diff;
    return col;
}
フラグメントシェーダー関数の書き方その2
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シェーダーの場合

以下のようにパラメータを指定するだけでライティングを記述することができます。

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
45