本日は昨日に引き続きMRGTStandardShaderのトーン表現を拡張していきます。
〇shader_featureの定義
URPではマテリアルから使用する機能だけチェックボックスを有効化して有効にするウーバーシェーダーシステムを使用するためにパラメータごとにshader_feature_localを使用しています。
/// <summary> /// Features. /// </summary> ・・・ #pragma shader_feature_local _ _DIRECTIONAL_LIGHT _DISTANT_LIGHT #pragma shader_feature_local _NON_PHOTOREALISTIC #pragma shader_feature_local _SHADOW_MAP//追加 ・・・
これで#if defined(_SHADOW_MAP)で条件コンパイルを行うことができるようになりました。
〇テクスチャの定義
次にGraphicsToolsStandardInput.hlslに影ように使用するテクスチャを定義します。
GraphicsToolsStandardInput.hlslではシェーダー内で使用される構造体および変数の定義を行っています。
多くの変数はURP用とビルドインシェーダー用の2つのパターンの定義を行う必要があります。
#if defined(_URP) ・・・ #if defined(_EMISSION) TEXTURE2D(_EmissiveMap); SAMPLER(sampler_EmissiveMap); #endif #if defined(_SHAODWMAP) TEXTURE2D(_NPRShadowMap); SAMPLER(sampler_NPRShadowMap);//追加 ・・・ #else //ビルドインシェーダー用の記述 sampler2D _MainTex; ・・・ #if defined(_SHAODWMAP) sampler2D _NPRShadowMap; #endif #endif
またテクスチャのサンプリングを行うためにはUVを使用する必要がありますが、これもテクスチャを使用する場合のみ定義されるようになっています。
そのため_SHADOW_MAPを使用している場合にもUVを使用するように定義します。
#if !defined(_DISABLE_ALBEDO_MAP) || defined(_TRIPLANAR_MAPPING) || defined(_CHANNEL_MAP) || defined(_NORMAL_MAP) || defined(_DISTANCE_TO_EDGE) || defined(_GRADIENT) || defined(_EMISSION)||defined(_SHADOW_MAP) #define _UV #else #undef _UV #endif
〇テクスチャのサンプリング
サンプリングは次のように定義します。
#if defined(_SHADOW_MAP) #if defined(_URP) half3 shadowCol = SAMPLE_TEXTURE2D(_NPRShadowMap, sampler_NPRShadowMap, input.uv).xyz; #else half3 shadowCol = tex2D(_NPRShadowMap,input.uv); #endif #endif
〇影に反映
MRGTシェーダーでのライティングの処理はGTLightingNonPhotorealisticで行われています。
これはGraphicsToolsLighting.hlslで定義されています。
#if defined(_NON_PHOTOREALISTIC) output.rgb += GTLightingNonPhotorealistic(brdfData, light.color, light.direction, worldNormal, worldViewDir); #else output.rgb += GTLightingPhysicallyBased(brdfData, light.color, light.direction, worldNormal, worldViewDir); #endif
このため_SHADOW_MAPを使用している場合とそうではない場合で処理を分けます。
#if defined(_NON_PHOTOREALISTIC) #if defined(_SHADOW_MAP) output.rgb += GTLightingNonPhotorealistic(brdfData, light.color, light.direction, worldNormal, worldViewDir,shadowCol); #else output.rgb += GTLightingNonPhotorealistic(brdfData, light.color, light.direction, worldNormal, worldViewDir); #endif #else output.rgb += GTLightingPhysicallyBased(brdfData, light.color, light.direction, worldNormal, worldViewDir); #endif
_SHADOW_MAPを使用している場合はGTLightingNonPhotorealisticの引数として新たにサンプリング結果であるshadowColを渡すようにしています。
同時にGTLightingNonPhotorealisticも_SHADOW_MAPによって処理を分岐させます。
#if defined(_SHADOW_MAP) half3 GTLightingNonPhotorealistic(GTBRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half3 normalWS, half3 viewDirectionWS, half3 shadowCol) { #else half3 GTLightingNonPhotorealistic(GTBRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half3 normalWS, half3 viewDirectionWS) { #endif half NdotL = ceil(saturate(dot(normalWS, lightDirectionWS))); #if defined(_SHADOW_MAP) NdotL += saturate(1-NdotL)*shadowCol; #endif half3 radiance = lightColor * NdotL; half3 brdf = brdfData.diffuse; return brdf * radiance; }
今回は以下の処理を使用しています。
NdotL += saturate(1-NdotL)*shadowCol;
これはNdotLがもともとライトが当たっている部分を出力しているため、逆数を取り影が1、ライトが当たっている部分が0になるように設定し、影の部分だけにShadowColを掛け合わせて画像を使用するようにしています。
最終的にもともとのNdotLに足し合わせることで画像の暗い部分のみが影として残るという仕組みで実装を行いました。
画像を差し替えることで様々な表現ができそうです。
本日は以上です。