GeometryShaderでFlatShading

前回に引き続きGeometryShaderを使ってシェーディングを実装していきます。
今回実装するShaderはFlatShadingと呼ばれるシェーデイング方法になります。
FlatShadingとは
簡単に説明すると陰影の計算をPrimitiveごとに算出する方法です。


Shader "FlatShading"
{
	Properties
	{
		_Color("Color", Color) = (1,1,1,1)
		_MainTex("MainTex", 2D) = "white" {}
	}

	SubShader
	{
		Tags{ "Queue" = "Geometry" "RenderType" = "Opaque" "LightMode" = "ForwardBase" }
		Pass
		{	
			CGPROGRAM
			#include "UnityCG.cginc"
			#pragma vertex vert
			#pragma geometry geom
			#pragma fragment frag

			float4 _Color;
			sampler2D _MainTex;

			struct v2g
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 vertex : TEXCOORD1;
			};

			struct g2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float light : TEXCOORD1;
			};

			v2g vert(appdata_full v)
			{
				v2g o;
				o.vertex = v.vertex;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv = v.texcoord;
				return o;
			}

			[maxvertexcount(3)]
			void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
			{
				g2f o;
				float3 normal = cross(IN[1].vertex - IN[0].vertex, IN[2].vertex - IN[0].vertex);
				normal = normalize(mul(normal,unity_WorldToObject));
				float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
				o.light = max(0., dot(normal, lightDir)* 0.5 + 0.5);
				o.uv = (IN[0].uv + IN[1].uv + IN[2].uv) / 3;
				for (int i = 0; i < 3; i++)
				{
					o.pos = IN[i].pos;
					triStream.Append(o);
				}
			}

			half4 frag(g2f i) : COLOR
			{
				float4 col = tex2D(_MainTex, i.uv);
				col.rgb *= i.light * _Color;
				return col;
			}
			ENDCG
		}
	}
	Fallback "Diffuse"
}

それでは一つずつ見ていきましょう。
前回に無かった部分だけ説明していきます。

float3 normal = cross(IN[1].vertex - IN[0].vertex, IN[2].vertex - IN[0].vertex);
normal = normalize(mul(normal,unity_WorldToObject));

一行目は前回でも説明した方法で
Primitiveの法線ベクトルを変数normalに入れています。
二行目は法線ベクトルを座標変換して正規化しています。

float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

_WorldSpaceLightPos0.xyzというのはライトのベクトル(ワールド座標)の値が入っていて、これをnomalize関数で正規化して変数lightDirに入れます。
陰影の計算をする時はベクトルの長さはいらないので必ず正規化して0~1の値にしておきます。

o.light = max(0., dot(normal, lightDir)* 0.5 + 0.5);

ここはHalfLambertを実装しています。
HalfLambertとは通常-1~1の値で計算される陰影を0.5~1で計算してしまい
陰影を柔らかくする方法です。

まず法線ベクトルとライトベクトルの内積
取って-1~1の値を出して
それをmax関数の最大値(第二引数)に設定
最小値(第一引数)に0を設定することで陰影が0~1の値になります。
この値に0.5を乗算して0.5を加算すると0.5~1になります。

o.uv = (IN[0].uv + IN[1].uv + IN[2].uv) / 3;

Primitiveのuv値を計算してout

float4 col = tex2D(_MainTex, i.uv);
col.rgb *= i.light * _Color;
return col;

次にFragmentShaderを見てみましょう。
tex2D関数でテクスチャを貼り付けて変数colに入れます。
i.lightにはGeometryShaderでPrimitiveごとに計算した陰影の値が入っているので
この値とpropertiesで設定した_Colorの値を乗算して
さらにcol.rgbに乗算してoutします。