
>>matsuno.::さん
それ目指してます!
クローズドβ的なのはnemlog内で行う予定です~
(サーバーの関係で接続人数が少ないためw)
こんにちは、今回はUnityのシェーダーの解説記事です。
いつか誰かがこの記事を読んでくれた時に助けになれば幸いです。
UnityにはShaderGraphというものがあります。
これはプログラミングなしで、描画の仕方を決めれるところに利点があります。
また、Unityの仕様変更でシェーダーをいちいち書き直さなくていいメリットがある(かもしれません)
今回は、処理の超絶軽いUnlitマスターノードに、
ライトの情報を得るカスタムファンクションを作り、影の投影とシェーディングを行い、
さらに、強引に影を受けるところまで進めます。
影を受けるのが結構苦労しました。
ライトの情報を得るカスタムファンクションは公式にも紹介があります。
基本的にはこの情報だけで、ライトの情報を受け、シェーディングと影の投影はできるので、
割愛させていただきます。
https://github.com/Unity-Technologies/ShaderGraph-Custom-Lighting
ここのAssets/Includes/CustumLighting.HLSLを使うだけです。
このスクリプト内に影の強さ、ShadowAttenがあるのですが値は返ってきません。
本来、影を受けるためには、
multi_compile _MAIN_LIGHT_SHADOWS
shader_feature _RECEIVE_SHADOWS_OFF
このあたりをシェーダー内で記述すると、本来は影の強さ、
CustumLighting.HLSLのShadowAttenが返ってくる…はずなんです。
しかしShaderGraph内に、
multi_compile _MAIN_LIGHT_SHADOWS
これを記載すると、ShaderGraphのVerifyings.hlslの最後、
#ifdef _MAIN_LIGHT_SHADOWS
output.shadowCoord = GetShadowCoord(vertexInput);
#endif
return output;
ここでエラーが出てしまいます。
そこでmulti_compile _MAIN_LIGHT_SHADOWSを使用せずに、
影を受けるメソッドを探し、記載することにしました。
要は、_MAIN_LIGHT_SHADOWSの定義で分岐するところ分岐先を強引につなげて、影を得ていきます。
まずは、元のCustumLighting.HLSLの影の強さ
ShadowAtten = mainLight.shadowAttenuation;
これはLighting.hlsl内のこの部分のことですね。
Light GetMainLight(float4 shadowCoord)
{
Light light = GetMainLight();
light.shadowAttenuation = MainLightRealtimeShadow(shadowCoord);
return light;
}
次にMainLightRealtimeShadow(shadowCoord)を探します。
これはShadow.hlsl内にあります。
half MainLightRealtimeShadow(float4 shadowCoord)
{
#if !defined(_MAIN_LIGHT_SHADOWS) || defined(_RECEIVE_SHADOWS_OFF)
return 1.0h;
#endif
#if SHADOWS_SCREEN
return SampleScreenSpaceShadowmap(shadowCoord);
#else
ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
half4 shadowParams = GetMainLightShadowParams();
return SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, false);
#endif
}
はい、きました。
multi_compile _MAIN_LIGHT_SHADOWS
shader_feature _RECEIVE_SHADOWS_OFF の分岐。
Unlitマスターではこれらの定義がされていないので、返り値は1.0hの部分になります。
なので、いつまでたっても影の強さは1.0(1は白、0が影です)
これを分岐先を強引に取り出します。
下から3行があればいいですね。
SampleShadowmap(~~~)この中のshadowCoordだけは値を作らなければなりません。
shadowCoordの取得先もShadow.hlslの中にあります。
float4 GetShadowCoord(VertexPositionInputs vertexInput)
{
#if SHADOWS_SCREEN
return ComputeScreenPos(vertexInput.positionCS);
#else
return TransformWorldToShadowCoord(vertexInput.positionWS);
#endif
}
ここで出てきたVertexPositionInputsとは…先ほどお話ししたエラー先Verifyings.hlslにあるのですが、
要はpositionWS…ワールドスペースのポジションでOKです。
これらをまとめて、CustomLighting.hlslに記載してしまえばいいのです。
こちらが元のコード
void MainLight_float(float3 WorldPos, out float3 Direction, out float3 Color, out float DistanceAtten, out float ShadowAtten)
{
#if SHADERGRAPH_PREVIEW
Direction = float3(0.5, 0.5, 0);
Color = 1;
DistanceAtten = 1;
ShadowAtten = 1;
#else
#if SHADOWS_SCREEN
float4 clipPos = TransformWorldToHClip(WorldPos);
float4 shadowCoord = ComputeScreenPos(clipPos);
#else
float4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
#endif
Light mainLight = GetMainLight(shadowCoord);
Direction = mainLight.direction;
Color = mainLight.color;
DistanceAtten = mainLight.distanceAttenuation;
ShadowAtten = mainLight.shadowAttenuation;
#endif
}
これの下の部分のLight~ShadowAttenをこう書き換えます。
Light mainLight = GetMainLight();
Direction = mainLight.direction;
Color = mainLight.color;
DistanceAtten = mainLight.distanceAttenuation;
ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
half4 shadowParams = GetMainLightShadowParams();
ShadowAtten = SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), TransformWorldToShadowCoord(WorldPos), shadowSamplingData, shadowParams, false);
GetMainLight()で、DirevtionやColorは取得できます。
ShadowAtten周りだけ書き換えて、強引にシーン上の影を取得することができます。
右のスフィアが黒い箱の影を受けれるようになりました。
いやーネットを探してもなかなかこんなことしてる人いないみたい。
Unityの公式デモでは、PBRマスターのエミッションに描きこんで強引にトゥーン調を再現する方法があったかなぁ
本日はこれまで!
シェーダーを完成させたら販売モデルのシェーダー更新予定ですー