前回はオブジェクトを半透明にするための方法を紹介しました。オブジェクトを半透明にするには次の3点をを設定する必要がありました。
- Queue
- alpha:fade
- SurfaceOutputStandard
今回は前回作った半透明のシェーダに、もうひと工夫加えることで氷のような見た目(ホログラフィック?)の絵にしてみましょう。今回の完成図は下のような感じになります。
シェーダでイケメンなドラゴンができた〜 pic.twitter.com/zw0X9FPOUH
— 北村愛実 (@tasonco_company) 2016年10月6日
氷シェーダのシェーダプログラム
次のプログラムが氷シェーダのプログラムになります。
Shader "Custom/sample" { SubShader { Tags { "Queue"="Transparent" } LOD 200 CGPROGRAM #pragma surface surf Standard alpha:fade #pragma target 3.0 struct Input { float3 worldNormal; float3 viewDir; }; void surf (Input IN, inout SurfaceOutputStandard o) { o.Albedo = fixed4(1, 1, 1, 1); float alpha = 1 - (abs(dot(IN.viewDir, IN.worldNormal))); o.Alpha = alpha*1.5f; } ENDCG } FallBack "Diffuse" }
前回の半透明のプログラムと比べると、次の2点を追加修正しています。それぞれ詳しく見ていきましょう。
- サーフェイスシェーダに、法線ベクトルと視線ベクトルを入力している
- 透明度の求めるために、ベクトル計算をしている
法線ベクトルと視線ベクトルについて
今回はサーフェイスシェーダにworldNormal(オブジェクトの法線ベクトル)とviewDir(視線ベクトル)を入力しています。法線ベクトルはオブジェクトの表面に対して垂直方向のベクトルです。また、視線ベクトルはカメラが向いている方向のベクトルです。
入力のパラメータの意味が分かったところで、どのようにしてworldNormalとviewDirを使って氷シェーダを作っているのかを考えてみましょう。完成図を見るとドラゴンの輪郭部分の透明度が低くなっているのに対して、中央部分の透明度は高くなっています。worldNormalとviewDIrを使って輪郭部分では1、中央部分では0になるような計算式を考えます。
氷シェーダの数式
では輪郭部分と中央部分のベクトルに注目してみましょう。輪郭部分では視線ベクトルと法線ベクトルが垂直に近い角度で交わるのに対して、中央部分ではほぼ平行に近い角度で交わっています。
この2つのベクトルが交わる角度を透明度に変換すればよさそうです。まずは2つのベクトルのなす角度を調べるために、内積(dot product)を使っています。worldNormalとviewDirの内積をとると下記の式ように、2つのベクトルが交わる角度に変換できます。(worldNormalとviewDirはどちらも正規化された長さ1のベクトルなので、|worldNormal=1、|viewDir|=1になります)
worldNormal * viewDir = |worldNormal| * |viewDir| * cosθ = cosθ
視線ベクトルと法線ベクトルの内積をとると、垂直に交わる場合は0、平行に交わる場合には1または-1になる計算ができました。今回は垂直に交わる場合には透明度を1、並行の場合は透明度を0にするために絶対値をとって1から引いています。これにより最終的な透明度の計算結果は、垂直に交わる場合には1、平行に交わる場合には0になります。
alpha = 1 - abs( cosθ );
最後に1.5を掛けているのは見た目を調整するためのパラメータです。ここの値を変化させることで見栄えが変わるので試してみて下さい。
シェーダの完成
完成したシェーダは次のとおりです。シェーダの出力結果がわかるようにドラゴンを回転させてみました。Unityでは見栄えを確認しながらシェーダを書き換えられるので調整が楽ですね〜。
今回作成したシェーダは、リムライティングと呼ばれる「背後からライトを当てて輪郭を浮かび上がらせる手法」とほぼ同じものです。リムライティングについても今後説明していく予定なのでお楽しみに!