本日はShader&MRGT学習枠です。
前回MixedRealityGraphicsTools(MRGT)のStandardShaderにNPR機能を実装しました。
NPRとはNon Photorialisitic Renderingの略称で、一言で言うと非現実的な影の付き方のレンダリングを指します。
NPRの中には絵画調やコミック調など様々な種類がありますが、今回は最終的にアニメなどに使用されるセルルックな見た目を目指し実装していきます。
〇NPRのライティング
ここでは前回のおさらいをします。
NPRか物理ベースのライティングかというのは様々な手法があるもののライトの処理の仕方によって大きく変わります。
通常のMRGT StandardShaderの処理ではDirectionalLightとピクセルごとのメッシュの法線のDot積で算出しています。
half diffuse = max(0.0, dot(worldNormal, directionalLightDirection.xyz));
つまり、ライトの方向に対して面の向きがどうなっているか?でライティングをしていることになります。
今回NPRのライティングを行うためにceil関数を使用して0or1に強制的に光情報をデジタル化して、滑らかな変化をなくしました。
half diffuse = ceil(saturate(dot(worldNormal,directionalLightDirection.xyz)+_ToonAmount));
half shadow = (1-diffuse)*0.5;
diffuse = diffuse + shadow;
今回は別の手法で試していきます。
〇RampTexture
RampTextureはToonShaderで実装されている手法の一つで、Toon調の影の付き方をTextureによって制御する方法になります。
ライティングでは結局のところdiffuseは0~1の一次元の値として影情報を格納していました。
今回はこの情報をRampTextureのサンプリングに使用する方法でRampTextureによる影の制御を実装します。
まずはRampTextureを使用するためのプロパティ設定を行います。
①Propertiesに[Toggle(ToonMap)]とRampMapを加えます。
Properties
{
//Aditional Parametors(Toon)
[Toggle(_Toon)] _ToonShading("ToonShading", Float) = 1.0
_ToonAmount("ToonAmount",Range(0,1)) = 0.0
[Toggle(_ToonMap)] _ToonMap("RampMap",Float) = 0
_RampMap("RampMap", 2D) = "white"{}
②StandardProgram.hlslのShaderFeatureに_ToonMapのシェーダーキーワードを追加します。
#pragma shader_feature_local _ToonMap
③またシェーダーキーワードによって処理を分岐させるための処理を加えます。
#if defined(_ToonMap) #define _ToonMap #else #undef _ToonMap #endif
これで_ToonMapを有効にしている場合とそうではない場合で処理を分岐できます。
④GraphicsToolsInput.hlslにテクスチャサンプリングの定義を加えます。
#if defined(_ToonMap) TEXTURE2D(_RampMap); SAMPLER(sampler_RampMap); #endif
⑤ライティング部分の処理(GraphicsToolsStandardProgram.hlsl)を次のように書き換えます。
#if defined(_Toon) && !defined(_ToonMap)
half diffuse = ceil(saturate(dot(worldNormal,directionalLightDirection.xyz)+_ToonAmount));
half shadow = (1-diffuse)*0.5;
diffuse = diffuse + shadow;
#elif defined(_ToonMap)
half diffuse = saturate(dot(worldNormal,directionalLightDirection.xyz)+_ToonAmount);
diffuse = SAMPLE_TEXTURE2D(_RampMap,sampler_RampMap,diffuse+0.01f).rgb;
#else
half diffuse = max(0.0, dot(worldNormal, directionalLightDirection.xyz));
#endif
今回重要な部分は次の部分です。
half diffuse = saturate(dot(worldNormal,directionalLightDirection.xyz)+_ToonAmount);
diffuse = SAMPLE_TEXTURE2D(_RampMap,sampler_RampMap,diffuse+0.01f).rgb;
これによって次のようにRampMapを使用した影をつけることができます。

行っていることは_RampMapのサンプリングを光の当たり方の強度で行っている点です。
SAMPLE_TEXTURE2D(_RampMap,sampler_RampMap,diffuse+0.01f)
通常SAMPLE_TEXTURE2D(_RampMap,sampler_RampMap,input.uv)などのようにuvの座標に基づいてサンプリングを行いますが、ここをdiffuse(0~1)の値で行うようにして、ライトがよりあたっている部分がRampMapの右側の色が使用されるようにサンプリングされるようにしています。

ここで0.01fを足し合わせているのはUVが0の場合想定されるサンプリングが行われなかったためです。

このあたりは詳しく調査することができなかったのですが、RampMapは複雑なテクスチャではなく、一般的にきれいなアナログなグラデーションではなく、とびとびのデジタルなグラデージョンになっています。
このため今回は影響がなく問題ないだろうと判断しました。
本日は以上です。