Unityシェーダー:ToonShader
Unityで開発したシンプルなToonShader.
Toon Shading
Toon Shadingでは,フォトリアリスティックなライティング結果に減色されたカラーマップを適用してアニメ風のレンダリング結果を得ます.初めてこの技術が発表されたのは,Stylized rendering techniques for scalable real-time 3D animationでその後X-toon,ハイライトのアニメ表現等色々な拡張が行われています.
Toon Shadingのプロセス
今回は,Lambertシェーディングの拡散反射項$k_d (\mathbf{L} \cdot \mathbf{N}),ドルPhongの鏡面反射項$k_s (\mathbf{L} \cdot \mathbf{RV})^{\alpha}$をライティングモデルとして使用し,単純な閾値処理で動的にカラーマップを作成してToon Shadingを行います.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
// Pixel処理でシェーディング計算を行うToonShaderShader "Custom/ToonShader" {// ToonShaderのパラメータを宣言Properties {// 一番暗い色の指定_Ambient ("Ambient Color", Color) = (0.1,0.1, 0.4,1)// 拡散反射で制御される陰影領域(陰影の明るい部分)// Border: 陰影領域の境界を制御// BorderBlur: 陰影領域の境界のぼけ具合の制御_Diffuse ("Diffuse Color", Color) = (0.3,0.3,1,1)_DiffuseBorder ("Diffuse border", Range (0.01, 1)) = 0.2_DiffuseBorderBlur ("Diffuse border blur", Range (0.01, 0.2)) = 0.01// 鏡面反射で制御される陰影領域(ハイライト部分)// Border: 陰影領域の境界を制御// BorderBlur: 陰影領域の境界のぼけ具合の制御_Specular ("Spec Color", Color) = (1,1,1,1)_SpecularBorder ("Specular border", Range (0.01, 1)) = 0.5_SpecularBorderBlur ("Specular border blur", Range (0.01, 0.2)) = 0.01_Shininess ("Shininess", Range (0.01, 1)) = 0.7}SubShader {pass{// Unityのライトオブジェクトを使ったLightModeTags { "LightMode" = "ForwardBase" }// Cgプログラムを使用する宣言// 頂点処理とピクセル処理を行うことを宣言CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"// Cgプログラムで使う変数// Propertiesブロックと対応付けるfloat4 _Ambient;float4 _Diffuse;float _DiffuseBorder;float _DiffuseBorderBlur;float4 _Specular;float _SpecularBorder;float _SpecularBorderBlur;float _Shininess;// 頂点からピクセルに転送されるデータstruct vertexOutput {float4 pos : SV_POSITION; // 座標変換後の位置float3 L : TEXCOORD0; // ライトベクトルfloat3 N : TEXCOORD1; // 法線ベクトルfloat3 RV : TEXCOORD2; // 視線の正反射ベクトル};// 頂点毎の処理// pos, L, N, RVのデータを計算する.vertexOutput vert(appdata_base v) : POSITION{vertexOutput output;// 座標変換後の位置output.pos = mul (UNITY_MATRIX_MVP, v.vertex);// 法線ベクトルfloat3 N = v.normal;output.N = N;// ライトベクトルoutput.L = ObjSpaceLightDir(v.vertex);// 視線の正反射ベクトルfloat3 V = ObjSpaceViewDir(v.vertex);output.RV = reflect(-V, N);return output;}// ピクセル毎の処理// 1. 通常の物理的なライティング計算を行う// 2. 照明結果を基に減色処理を行うfloat4 frag(vertexOutput input) : COLOR{// 頂点処理で計算したベクトルデータを正規化して取り出すfloat3 L = normalize( input.L );float3 N = normalize( input.N );float3 RV = normalize( input.RV );// 拡散反射の度合I_dを計算float LdN = clamp( dot(L, N), 0, 1 );float I_d = LdN;// 鏡面反射の度合I_sを計算float LdRV = clamp( dot(L, RV), 0, 1 );float shininess = pow(500.0, _Shininess);float I_s = pow( LdRV, shininess);// 得られたI_d, I_sを基に減色処理を行う// 一番暗い色からスタートするfloat4 c_a = _Ambient;// 拡散反射の度合I_dを基に閾値処理を行い,// I_d > _DiffuseBorderであれば,_Diffuseの色で塗る// _DiffuseBorderBlurにより,境界部分のぼけ具合を制御しているfloat t_d = smoothstep( _DiffuseBorder - _DiffuseBorderBlur, _DiffuseBorder + _DiffuseBorderBlur, I_d);float4 c_d = lerp(c_a, _Diffuse, t_d);// 拡散反射と同様に,鏡面反射についても閾値処理を行うfloat t_s = smoothstep(_SpecularBorder - _SpecularBorderBlur, _SpecularBorder + _SpecularBorderBlur, I_s);float4 c = lerp(c_d, _Specular, t_s);return c;}ENDCG}}FallBack "Diffuse"}
陰影の色は,暗い順に_Ambient,_Diffuse,_Specularで指定されます.
_DiffuseBorder,_SpecularBorderにより陰影色の境界を動かすことができます.
追加の機能として,_DiffuseBorderBlur,_SpecularBorderBlurでぼけ具合を調整して少し柔らかい質感を表現可能です.
参考文献
[1] Stylized rendering techniques for scalable real-time 3D animation: http://dl.acm.org/citation.cfm?id=340918
[2] X-toon: an extended toon shader: http://maverick.inria.fr/Publications/2006/BTM06a/
[3] アニメ表現のためのハイライトと陰影の直感的演出法 - OLM Digital R&D: http://www.olm.co.jp/rd/tweakable-light-and-shade-for-cartoon-animation/