【UnityShader】MatCap #62
前回の成果
Behavior Treeでサンプルクラスを実装した。
今回やること
MatCapについて勉強します。
事前準備
以下アセットをAssetStoreからダウンロードします。
そして、BarrelをScene上に配置します。
また、MatCapに使用するテクスチャはこちらになります。
Call for Content: MatCaps - User Feedback - Blender Developer Talk:より引用
ソースコード
Shader "Unlit/Matcap" { Properties { _MainTex ("Texture", 2D) = "white" {} _MatCapTex ("MatCap Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"} LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MatCapTex; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); float3 normal = UnityObjectToWorldNormal(v.normal); normal = mul((float3x3)UNITY_MATRIX_V, normal); o.uv = normal * 0.5 + 0.5; return o; } fixed4 frag (v2f i) : SV_Target { return tex2D(_MatCapTex, i.uv); } ENDCG } } }
Vertex Shaderで主な処理を行なっていています。
Flagment Shaderでは、tex2Dを行なっているだけになります。
MatCap
球のようなテクスチャから色を取得することにより、モデルの表面に対して質感の表現を行う手法のことです。
MatCap Shaderは別名スフィア環境マッピングとも呼ばれます。
MatCapのテクスチャ例
Call for Content: MatCaps - User Feedback - Blender Developer Talk:より引用
テクスチャから色を取得するので、ライトの影響を受けることはありません。
ですので、MatCapはライティングを軽い処理で行うことができます。
しかし、どの方向から見てもライティングの色が共通なため、カメラやライトが複数個ある場合などに表現が異なる場合があります。
奥のオブジェクトがライティングの影響を受けるマテリアル、手前がMatCap
MatCapの考え方
考え方については、こちらのサイト様がわかりやすいです。
自分の言葉でも、簡単にまとめます。
オブジェクトの法線のxyを取得し、それをテクスチャのUV値として扱います。
法線をUVとして使用するイメージ
しかし、そのままオブジェクトの法線を使用してしまうとオブジェクトが回転した際に描画が正しくないものとなってしまいます。
何も考慮せずに法線を使用するイメージ
ですので、カメラが回転した時に法線も同じように回転してあげると正しくテクスチャが貼られることになります。
ソースコード解説
では、ソースコードの解説に移ります。
v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); float3 normal = UnityObjectToWorldNormal(v.normal); normal = mul((float3x3)UNITY_MATRIX_V, normal); o.uv = normal * 0.5 + 0.5; return o; }
UnityObjectToWorldNormal
引数をワールド座標へと変換する関数となります。
今回の場合、法線をワールド座標へと変換しています。
mul((float3x3)UNITY_MATRIX_V, normal)
UNITY_MATRIX_Vは、現在のビュー行列を取得出来る定義済みの値となります。
UNITY_MATRIX_Vと先ほどワールド座標へと変換した法線を乗算します。
そうすることにより、カメラの回転を考慮した法線となります。
normal * 0.5 + 0.5
法線は-1~1で表されます。
UV値は0~1ですので、そちらに合わせるための計算となります。
結果
Texture通りの光沢がついていれば成功です。
inspector
横に普通のオブジェクトを配置して、Lightを無くすと違いが顕著にわかると思います。
左がデフォルトのマテリアルのオブジェクト
右がMatCapがマテリアルのオブジェクトとなっています。
ライトあり
ライトなし
また、ライトが回転している時もわかりやすいです。
元のテクスチャとブレンドする
以下ソースコードのようにすることにより元のテクスチャとMatCapをブレンドすることができます。
Shader "Unlit/MatcapBlend" { Properties { _MainTex ("Main Texture", 2D) = "white" {} _MatCapTex ("MatCap Texture", 2D) = "white" {} _Blend ("Blend", Range(0, 1)) = 0 } SubShader { Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"} LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; }; struct v2f { float2 mainUv : TEXCOORD0; float2 matUv : TEXCOORD1; float4 vertex : SV_POSITION; }; sampler2D _MatCapTex; sampler2D _MainTex; float4 _MainTex_ST; fixed _Blend; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); float3 normal = UnityObjectToWorldNormal(v.normal); normal = mul((float3x3)UNITY_MATRIX_V, normal); o.matUv = normal * 0.5 + 0.5; o.mainUv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 mainColor = tex2D(_MainTex, i.mainUv); fixed4 matColor = tex2D(_MatCapTex, i.matUv); fixed4 col = fixed4(lerp(mainColor, matColor, _Blend)); return col; } ENDCG } } }
処理は難しいことをしていないんので説明を省かさせて頂きます。
結果
メインのテクスチャとMatCapがブレンドされていれば成功となります。
inspector
ソースコードにコメントを付与
Shader "Unlit/MatcapBlend" { Properties { _MainTex ("Main Texture", 2D) = "white" {} _MatCapTex ("MatCap Texture", 2D) = "white" {} _Blend ("Blend", Range(0, 1)) = 0 } SubShader { Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"} LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; }; struct v2f { float2 mainUv : TEXCOORD0; float2 matUv : TEXCOORD1; float4 vertex : SV_POSITION; }; sampler2D _MatCapTex; sampler2D _MainTex; float4 _MainTex_ST; fixed _Blend; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); // 法線をワールド座標へと変換 float3 normal = UnityObjectToWorldNormal(v.normal); // ビュー行列へ変換 normal = mul((float3x3)UNITY_MATRIX_V, normal); // 法線(-1~1)をUV(0~1)として扱う o.matUv = normal * 0.5 + 0.5; o.mainUv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 mainColor = tex2D(_MainTex, i.mainUv); fixed4 matColor = tex2D(_MatCapTex, i.matUv); // メインテクスチャとMatCapを補完する fixed4 col = fixed4(lerp(mainColor, matColor, _Blend)); return col; } ENDCG } } }
今回は以上となります。
ここまでご視聴ありがとうございました。