Day 19 - Toon shader
昨日は灰色になった球体に敗北して終了しました。 今日は勝ちます。 Shader script さて、まずはToon/Basic Outlineの中身がどんな事になっているのか見てみましょう。 Shader "Toon/Basic Outline" { Properties{ _Color("Main Color", Color) = (.5,.5,.5,1) _OutlineColor("Outline Color", Color) = (0,0,0,1) _Outline("Outline awidth", Range(.002, 0.03)) = ...
昨日は灰色になった球体に敗北して終了しました。
今日は勝ちます。
Shader script
さて、まずはToon/Basic Outlineの中身がどんな事になっているのか見てみましょう。
Shader "Toon/Basic Outline" {
Properties{
_Color("Main Color", Color) = (.5,.5,.5,1)
_OutlineColor("Outline Color", Color) = (0,0,0,1)
_Outline("Outline awidth", Range(.002, 0.03)) = .005
_MainTex("Base (RGB)", 2D) = "white" { }
_ToonShade("ToonShader Cubemap(RGB)", CUBE) = "" { }
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
UNITY_FOG_COORDS(0)
fixed4 color : COLOR;
};
uniform float _Outline;
uniform float4 _OutlineColor;
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
float3 norm = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
float2 offset = TransformViewToProjection(norm.xy);
#ifdef UNITY_Z_0_FAR_FROM_CLIPSPACE //to handle recent standard asset package on older version of unity (before 5.5)
o.pos.xy += offset * UNITY_Z_0_FAR_FROM_CLIPSPACE(o.pos.z) * _Outline;
#else
o.pos.xy += offset * o.pos.z * _Outline;
#endif
o.color = _OutlineColor;
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}
ENDCG
SubShader {
Tags { "RenderType"="Opaque" }
UsePass "Toon/Basic/BASE"
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
Cull Front
ZWrite On
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
fixed4 frag(v2f i) : SV_Target
{
UNITY_APPLY_FOG(i.fogCoord, i.color);
return i.color;
}
ENDCG
}
}
Fallback "Toon/Basic"
}
Shader "name" { [Properties] Subshaders [Fallback] } っていうのが文法らしいです。([ ]の中は省略可)
なんだか、ただの設定ファイルのように見える。
Color setting
この中で、色の設定っぽい部分は…
_Color("Main Color", Color) = (.5,.5,.5,1)
_OutlineColor("Outline Color", Color) = (0,0,0,1)
Properties内1行目にして早速出てきました。 Properties内に入っているものは、インスペクターに表示されます。 この右辺の括弧内は確か、RGBとAlphaの値だったかな?
OutlineColorは読んで字の如く、球体の周りの黒い縁のことですね。 そしてColor、コイツが今回の僕の敵ですかね。
http://j-press.info/16torgba/ こちらのサイトから、白い時のRGBの値を調べて(というか白と黒ぐらいは覚えておこう…)

3つとも255に設定してあげます。

あれ?
じゃあ、Propertiesの中の他のものを見てみます。
_Outline("Outline awidth", Range(.002, 0.03)) = .005
_MainTex("Texture", 2D) = "white" {}
_ToonShade("ToonShader Cubemap(RGB)", CUBE) = "" { }
Outlineのところはインスペクター上でいじれる長さの範囲の指定ですね。 これ、たぶんコードで書いてもただ初期値を指定しているだけだ。 じゃあ、カラーを変更しても意味はないわけだ。しかも原因はそこではないと。
MainTexはテクスチャを貼れるようにして、初期のテクスチャを白いものにするって感じ。 ToonShadeって所。ここ、実はトゥーン系の表現をする重要なポイントらしい。
Cubemap
Cubemapっていうのをインスペクター上でアタッチしてやると 指定した色でカメラの視点を基準にしてオブジェクトの色を調整して、擬似的に影がついたような状態になります。 そして、Effectsに入っていたCubemapをアタッチをしてやると…

おお、白い! なるほど、トゥーン表現に必要なカラーマップが無かったから暗くなってしまっていたみたい。
そういえば、影について擬似的についているといったのは、これは光に当って出来る実際の影とは全く別物なんです。

なので上のURLのように、太陽が消えた光の無い世界でも疑似影は着いた状態になります。
影の付き方をもっとリアルにしたい場合はBasic OutlineではなくLit Outlineを使います。 しかし僕は「それっぽい」感じぐらいが良いと思っていて、別にそんなにリアルを求めなくていいのではと思ってます。 ここは個人の考え方なので、求めたい人は求めましょう。
Outline of cube
さて、球体にアウトラインを付ける事が出来ました。 調子に乗って色々な物体にアウトラインをつけたのですが、なんとそこでまた躓くポイントが。
何に躓いたのか、画像を見てもらえばわかると思います。

これが、立方体にアウトラインを太くして適用させた時の画像です。 なんだか面が浮いているような状態になってしまいました。
これの原因っていうのは、このシェーダーでアウトラインを付ける時にやっていることが 物体の表面から法線方向に線を伸ばしているからだそうです。
画像で説明しましょう

まず、法線というのはある一点に触れる接線に垂直な線のことです。 その法線の方向にアウトラインをつけるために線を伸ばすと

アウトラインのごく一部ができます。 これをいろんな場所でやっていくと、途中はこんな感じになり

全部やると

完成です。こういう処理をしてアウトラインをつけているんですね。
しかし、これを曲線上ではなく平面上でやった時の事を考えてみましょう。 まず、四角の図形があるとします。

それの一辺から線を伸ばしてアウトラインをつけようとします。

こうなりますね。しかし、他の辺にも適用して全体を見てみると…

このように、角に対して黒い縁が出来ていないことがわかります。 なので、立体にしたときも角にアウトラインを付けることが出来ずに変な表示になってしまっているわけですね。
So then,what should i do?
では、何をすれば良いのか。 シェーダーを変えちゃうとか、色々有ると思いますが…
ちょっと思いついたのが、角の部分で問題が起きているなら 角をなくせばいい!という単純な発想。
じゃあ、面取りをしちゃいましょう。
しかし、Unity上で面取りをするにはどうすればいいのか? わからなかったのですが、どうやらMecanimを触れた時にダウンロードしておいたBlenderソフトで出来る様子。
Blender
早速いじってみます。そして、早速つまづきます。 Unityで作ったオブジェクトをBlender側で読み込めるファイルにするにはどうするんだ。
Blenderで作ったオブジェクトをUnityで読み込むやり方はいくらでもネットに載っていますね 逆が知りたいのに…
まあ、Blenderで同じような箱を作って面取りをしてみましょう。 Blenderを起動したら最初からある箱を、画面下のEdit Modeにした後にCtrl+Bを押し、マウス移動+スクロールで面取りができます。

画像だとちょっと分かりづらいですが、ちゃんと面取りがされました。
さて、これをFbxファイル形式でエクスポートしてUnity側にぶちこみます。

読み込むと、こんな感じで表示されます。シーン上に配置したあと、fbxファイルの中にあるCubeにToonShaderのマテリアルを適用。 これでいけたのでは・・・

あれ?縁取りされてない…?
いやこれ

見えますかね? 縁取りされてないんじゃなくて、面取りがまだまだカクカクしていて法線方向に伸ばした線が連続せずに ものすごく薄く縁取りされている状態になっていますね。
これを解消するために、スムース処理というのをしてやります。 Blender上でもう一度面取りしたファイルを開き、オブジェクトモードにします。
その際、画面上はこんな感じ

そして、左側のウィンドウを見ると

Smooth、それっぽいのがありますね。コイツを押します。

スムースになった!(って言えばいいのか?) もう一度Unityに取り込んでマテリアルを適用します。

やりました。
よし、なんとか出来たので今日はここまで。 明日はユニティちゃんのモデルをトゥーン化する作業をしてみます。 その次の日はちょっとスクリプトを書くような事をやっていきましょう。