初めまして、クライエントエンジニアの中島龍清(ナカシマリュウセイ)です。
エンジニア内に同姓の方がいるのでフルネームで名乗らせていただきました。
ブログを書くのが初めてのため拙い箇所が多々あることを予めご了承ください。
今回はタイトルにもある通りMatCapについて紹介いたします。
プロジェクトでMatCapに触れる機会があり、
その便利さを知ったので覚え書きも兼ねてブログ記事としてまとめてみようと思いました。
MatCapとは?
Material Captureの略でライティング済みの球体が描かれたテクスチャのこと、
またはそれを使用して疑似的にライティング表現をおこなう手法のことです。
今回は主に後者の意味で話を進めていきます。
特徴
先ほど述べたようにライティング済みの球体が描かれたテクスチャを使用した表現であり、
テクスチャに描かれたライティングに依存するため通常のライティングの影響を受けません。
リアルな表現をしたい場合では大きなデメリットとなってしまいますが、
ライティング処理を自作することが多いトゥーン的な表現とは相性が良いと言えます。
また、球体であれば作成したテクスチャとほぼ同じ見た目になります。
実際にMatCapシェーダーを球体に適用するとこんな感じです。
使用したMatCapテクスチャはこちらです。
若干の歪みはありますが、MatCapシェーダーを適用したものは
MatCapテクスチャとほぼ同じ見た目になっていることが分かると思います。
実装手順
今回はUnityを使って実装していきます。
シェーダーについてあまり詳しくない方でもUnityとVisualStudioのインストールさえしていれば
実装できるような説明になるように努力いたします。
実装準備
まずはUnlitシェーダーを作成します。
作成したらシェーダーを編集し、フォグなどの不要な処理を削除して下記のような状態にしてください。
Shader "Unlit/MatCapShader" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); return col; } ENDCG } } }
この状態から必要となるパラメーターを追加していきます。
パラメーターの追加
MatCapを作成する上で必要最低限のパラメーターは以下になります。
・UV
・頂点法線(NormalVector)
・MatCapテクスチャ
コード上に追加する場合は下記のようになります。
Properties { _MainTex ("BaseTexture", 2D) = "white" {} _MatcapTex ("MatcapTexture",2D) = "white" {} //MatCapテクスチャ } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; //頂点法線を取得 float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 viewNormal : TEXCOORD1; //頂点法線変換用 }; sampler2D _MainTex; sampler2D _MatcapTex; //MatCapテクスチャ変数 float4 _MainTex_ST;
一番重要なMatCapテクスチャですが自分で製作するのは難しいので
Twitterの配布matcapモーメントにて取り上げられているものを有難く使わせていただきます。
twitter.com
メイン処理実装
MatCapのメイン処理を実装します。
追加する内容は下記になります。
vert関数
v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.viewNormal = mul((float3x3)UNITY_MATRIX_V, UnityObjectToWorldNormal(v.normal)); //① return o; }
frag関数
fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); float3 matCap = tex2D(_MatcapTex, i.viewNormal.xy * 0.5 + 0.5).rgb; //② -1 ~ 1 を 0 ~ 1へ col.rgb = matCap; //③ return col; }
追加した箇所の処理内容はそれぞれ以下のようになっています。
①ビュー座標での頂点法線を取得
②①のxy値をUVに入力してMatCapテクスチャをサンプリング
③MatCapのカラー値を出力
以上でMatCapの実装は完了です。
実装結果
描画結果はこのようになります。
最終的な全体のコードは下記のようになっております。
Shader "Unlit/MatCapShader" { Properties { _MainTex ("BaseTexture", 2D) = "white" {} _MatcapTex ("MatcapTexture",2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; float3 viewNormal : TEXCOORD1; }; sampler2D _MainTex; sampler2D _MatcapTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.viewNormal = mul((float3x3)UNITY_MATRIX_V, UnityObjectToWorldNormal(v.normal)); //① return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); float3 matCap = tex2D(_MatcapTex, i.viewNormal.xy * 0.5 + 0.5).rgb; //② -1 ~ 1 を 0 ~ 1へ col.rgb = matCap; //③ return col; } ENDCG } } }
今回は結果を分かりやすくするためMatCapのカラー値をそのまま出力しましたが、
ベーステクスチャにブレンド(Multiply,lerpなど)することでマスクとして利用するなど応用も効きます。
もしそちらの方に興味があればぜひ色々試してみてください。
仕組みが分かればUE4(またはUE5)でも実装ができるので
機会があればそちらの実装方法も紹介したいと思います。