【UnityShader】トゥーンシェーダー #17
前回の成果
3種類のノイズを描画できるようになった。
今回やること
トゥーンシェーダー(Toon Shader)を自作してみようと思います。
ToonShaderとは
3次元コンピュータグラフィックスの一種で、2次元の手描きアニメーション、あるいは漫画やイラスト風の作画(いわゆるアニメ絵)で非写実的レンダリングさせる技術である
とあります。
こちらの画像では、右側がトゥーンシェーダーを使用して描画されています。
前準備
Scene上にオブジェクトを配置します。
こちらの画像を今回制作するMaterialの_RampTexにアタッチします。
ソースコード
Shader "Custom/toon" { Properties { _Color("Color", Color) = (1, 1, 1, 1) _MainTex("Albedo(RGB)", 2D) = "white" {} _RampTex("Ramp", 2D) = "white" {} } SubShader { Tags {"RenderType" = "Opaque"} LOD 200 CGPROGRAM #pragma surface surf ToonRamp #pragma target 3.0 sampler2D _MainTex; sampler2D _RampTex; struct Input { float2 uv_MainTex; }; fixed4 _Color; fixed4 LightingToonRamp(SurfaceOutput s, fixed3 lightDir, fixed atten) { half diff = dot(s.Normal, lightDir); fixed3 ramp = tex2D(_RampTex, fixed2(diff, diff)).rgb; fixed4 c; c.rgb = s.Albedo * _LightColor0.rgb * ramp * atten; c.a = s.Alpha; return c; } void surf(Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } Fallback "Diffuse" }
surf関数の中身はTextureの情報を取得しているだけのようですが、LightingToonRamp関数が呼び出しされていません。
#pragma surface surf ToonRamp
以前までは、
#pragma surface surf Standard
や
#pragma surface surf Standard fullforwardshadows
が多かったと思います。
これらは、 VertexとLightingをUnityに任せていたからです。
ですが、今回は自分でライティングを行うことによってトゥーンシェーダーを実装します。
ですので、
#pragma surface surf ToonRamp
と書きます。
#6で一度紹介したのですが、
// 元の文 #pragma surface surf ToonRamp // 説明 pragma宣言 サーフェスシェーダーを使うよ 関数名 ライティングモデル (オプションパラメータ)
となっています。
このライティングモデルにメソッド名を記述するとカスタムライティングとなります。
この時、注意して欲しいのは関数を定義する際にはLightingを頭につける必要があります
// カスタムライティングの定義 #pragma surface surf ToonRamp // 定義した名前(ToonRamp)の頭にLightingをつける fixed4 LightingToonRamp (SurfaceOutput s, fixed3 lightDir, fixed atten) // これだと動かない fixed4 ToonRamp (SurfaceOutput s, fixed3 lightDir, fixed atten)
また、カスタムライティングを使用する際にはSurfaceOutputStandardを使用することができません。
なので、SurfaceOutputに置き換えます。
カスタムライティングの引数
引数の種類は3種類あります。
Forward rendering
half4 LightingName(SurfaceOutput s、half3 lightDir、half atten){}
今回はこれを使用しています。
ViewDirectionあり
half4 LightingName(SurfaceOutput s、half3 lightDir、half3 viewDir、half atten){}
Deferred rendering
half4 LightingName_PrePass (SurfaceOutput s, half4 light){}
この関数は、プロジェクトで遅延レンダリングを使用している場合に使用されます。
ちなみに、attenはattenuationの略で、ライトの減衰率のことです。
_LightColor0
戻り値がfixed4型のライトの色です。
LightingToonRampのアルゴリズム
step1
half diff = dot(s.Normal, lightDir);
オブジェクトのピクセル毎の法線とライト方向の内積を出します。
dot(内積)は、-1~1の間で出力されます。
step2
fixed3 ramp = tex2D(_RampTex, fixed2(diff, diff)).rgb;
step1で出された内積の値で、_RampTexの色を取得します。
色 | UV値(内積の値) |
---|---|
黒 | 0~0.333... |
茶色 | 0.333...~0.666... |
白 | 0.666...~1 |
負の数になった場合は黒になります。
以上の2つの手順からトゥーンシェーダーができます。
結果
表
裏
表も裏もしっかり色を変更させることができています。
ソースコードにコメントを付与
Shader "Custom/toon" { // プロパティ Properties { // ベースとなる色 _Color("Color", Color) = (1, 1, 1, 1) // メインテクスチャ _MainTex("Albedo(RGB)", 2D) = "white" {} // rampテクスチャ _RampTex("Ramp", 2D) = "white" {} } // Shaderの中身を記述 SubShader { // 一般的なShaderを使用 Tags {"RenderType" = "Opaque"} // しきい値 LOD 200 // cg言語記述 CGPROGRAM // メソッド名がLightingToonRampのカスタムライティング宣言 #pragma surface surf ToonRamp // Shader Model #pragma target 3.0 // メインテクスチャ sampler2D _MainTex; // rampテクスチャ sampler2D _RampTex; // input構造体 struct Input { // uv座標 float2 uv_MainTex; }; // ベースとなる色 fixed4 _Color; // カスタムライティング fixed4 LightingToonRamp(SurfaceOutput s, fixed3 lightDir, fixed atten) { // 内積を取得 half diff = dot(s.Normal, lightDir); // rampテクスチャのuv値を取得 fixed3 ramp = tex2D(_RampTex, fixed2(diff, diff)).rgb; // rampテクスチャのuv値から色を取得 fixed4 c; c.rgb = s.Albedo * _LightColor0.rgb * ramp * atten; c.a = s.Alpha; return c; } // surf関数 void surf(Input IN, inout SurfaceOutput o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; o.Alpha = c.a; } // Shaderの記述終了 ENDCG } // SubShaderが失敗した時に呼ばれる Fallback "Diffuse" }
今回は以上となります。
次回はvertexShaderについて触れていこうと思います。