おっしゃるとおりunity_LightShadowBias
が一体何なのか検索してみても、明確な情報はひっかかりませんね...
かろうじてヒントになりそうなのはUnityCG.cginc
に記述されている...
ShaderLab
1float4 UnityClipSpaceShadowCasterPos(float4 vertex, float3 normal)
2{
3 float4 wPos = mul(unity_ObjectToWorld, vertex);
4
5 if (unity_LightShadowBias.z != 0.0)
6 {
7 float3 wNormal = UnityObjectToWorldNormal(normal);
8 float3 wLight = normalize(UnityWorldSpaceLightDir(wPos.xyz));
9
10 // apply normal offset bias (inset position along the normal)
11 // bias needs to be scaled by sine between normal and light direction
12 // (http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/)
13 //
14 // unity_LightShadowBias.z contains user-specified normal offset amount
15 // scaled by world space texel size.
16
17 float shadowCos = dot(wNormal, wLight);
18 float shadowSine = sqrt(1-shadowCos*shadowCos);
19 float normalBias = unity_LightShadowBias.z * shadowSine;
20
21 wPos.xyz -= wNormal * normalBias;
22 }
23
24 return mul(UNITY_MATRIX_VP, wPos);
25}
のコメントぐらいでしょうか。ですがそれもz
についてだけであり、x
やy
については言及されていないようです。
多少なりともご参考になれば...と思いまして、「マテリアルのインスペクター上でBiasとNormal Biasを独自に設定して影を描く」というのを目標にフレームデバッガーで変数の値を観察しつつ試行錯誤して、下記のような計算式をひねり出してみました。
ShaderLab
1Shader "Unlit/CustomShadowBias"
2{
3 Properties
4 {
5 _MainTex ("Texture", 2D) = "white" {}
6 _ShadowIntensity ("Shadow Intensity", Range(0.0, 1.0)) = 0.5
7 [Toggle(CUSTOM_BIAS_ENABLED)] _UseCustomBias ("Use Custom Bias", Float) = 0
8 _ShadowMapResolution ("Shadow Map Resolution", Int) = 1024
9 _Bias ("Bias", Range(0.0, 2.0)) = 0.05
10 _NormalBias ("Normal Bias", Range(0.0, 3.0)) = 0.4
11 }
12 SubShader
13 {
14 Tags { "RenderType"="Opaque" "LightMode"="ForwardBase" }
15
16 Pass
17 {
18 CGPROGRAM
19 #pragma vertex vert
20 #pragma fragment frag
21 #pragma multi_compile_fwdbase
22
23 #include "UnityCG.cginc"
24 #include "AutoLight.cginc"
25
26 struct appdata
27 {
28 float4 vertex : POSITION;
29 float2 uv : TEXCOORD0;
30 };
31
32 struct v2f
33 {
34 float2 uv : TEXCOORD0;
35 float4 pos : SV_POSITION;
36 SHADOW_COORDS(1)
37 };
38
39 sampler2D _MainTex;
40 float4 _MainTex_ST;
41 float _ShadowIntensity;
42
43 v2f vert(appdata v)
44 {
45 v2f o;
46 o.pos = UnityObjectToClipPos(v.vertex);
47 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
48 TRANSFER_SHADOW(o)
49 return o;
50 }
51
52 fixed4 frag(v2f i) : SV_Target
53 {
54 fixed4 color = tex2D(_MainTex, i.uv);
55 color.rgb *= lerp(1.0, SHADOW_ATTENUATION(i), _ShadowIntensity);
56 return color;
57 }
58 ENDCG
59 }
60
61 Pass
62 {
63 Tags { "LightMode"="ShadowCaster" }
64
65 CGPROGRAM
66 #pragma vertex vert
67 #pragma fragment frag
68 #pragma multi_compile_shadowcaster
69 #pragma multi_compile _ CUSTOM_BIAS_ENABLED
70 #include "UnityCG.cginc"
71
72 struct v2f {
73 V2F_SHADOW_CASTER;
74 };
75
76 int _ShadowMapResolution;
77 float _Bias;
78 float _NormalBias;
79
80 float3 shadowBias()
81 {
82 float3 bias = unity_LightShadowBias.xyz;
83
84 #ifdef CUSTOM_BIAS_ENABLED
85 // yはDirectionalなら1、それ以外なら0になる?
86 float isDirectional = bias.y;
87
88 // xはBiasに比例して変化しているように見える
89 // Directionalの場合はBiasにクリップ空間の奥行きスケールを乗じたもの?
90 // Directional以外だと、xの絶対値はBiasがそのまま入ってくるように見える
91 // 符号はPointだと1、それ以外だと-1?
92 #ifdef SHADOWS_CUBE
93 float biasXSign = 1.0;
94 #else
95 float biasXSign = -1.0;
96 #endif
97 bias.x = lerp(1.0, UNITY_MATRIX_P._33, isDirectional) * biasXSign * _Bias;
98
99 // zはNormal Biasに比例して変化しているように見える
100 // Directionalの場合はNormal Biasを2倍し、クリップ空間の水平スケール×ビューポートサイズで割ったもの?
101 // Directional以外だと常に0?
102 // ビューポートサイズがやっかいで、おそらく https://docs.unity3d.com/Manual/shadow-mapping.html で
103 // 説明されているShadow map resolutionと同一だと思うが、それをシェーダー上で取得する方法がわからない...
104 // さしあたり_ShadowMapResolutionとしてインスペクター上で与えるようにしたが、これだとシャドウマップの
105 // 実際の解像度に応じて自動的に設定することができず、いまいち便利でない
106 // スクリプト上で計算してシェーダーに与えてやることになるのだろうか?
107 bias.z = lerp(0.0, 2.0 / (UNITY_MATRIX_P._11 * _ShadowMapResolution), isDirectional) * _NormalBias;
108
109 // ShadowCasterパスはカメラ視点でのデプスレンダリング(UpdateDepthTexture)と
110 // ライト視点でのシャドウマップレンダリング(Shadows.RenderShadowMap)の両方で使われるが
111 // どうやらUpdateDepthTextureではunity_LightShadowBiasは(0, 0, 0, 0)となり
112 // バイアスがかからないようになっているらしい?
113 // UpdateDepthTextureでバイアスをかけてしまうと、カメラ視点でのデプスが狂い
114 // スクリーンスペースでの影レンダリング(Shadows.CollectShadows)時に異常な影を
115 // 生じてしまいそうだった
116 // UpdateDepthTextureとShadows.RenderShadowMapのどちらであるかをシェーダー上で
117 // 区別する方法がわからず、しかたないのでunity_LightShadowBiasがゼロベクトルなら
118 // biasも全成分ゼロにすることにした
119 bias = lerp(0.0, bias, sign(dot(unity_LightShadowBias, unity_LightShadowBias)));
120 #endif
121
122 return bias;
123 }
124
125 // Normal Biasの適用
126 float4 clipSpaceShadowCasterPos(float4 vertex, float3 normal, float3 bias)
127 {
128 float4 wPos = mul(unity_ObjectToWorld, vertex);
129
130 if (bias.z != 0.0)
131 {
132 float3 wNormal = UnityObjectToWorldNormal(normal);
133 float3 wLight = normalize(UnityWorldSpaceLightDir(wPos.xyz));
134 float shadowCos = dot(wNormal, wLight);
135 float shadowSine = sqrt(1 - shadowCos * shadowCos);
136 float normalBias = bias.z * shadowSine;
137
138 wPos.xyz -= wNormal * normalBias;
139 }
140
141 return mul(UNITY_MATRIX_VP, wPos);
142 }
143
144 // Biasの適用
145 float4 applyLinearShadowBias(float4 clipPos, float3 bias)
146 {
147 #if !(defined(SHADOWS_CUBE) && defined(SHADOWS_CUBE_IN_DEPTH_TEX))
148 #if defined(UNITY_REVERSED_Z)
149 clipPos.z += max(-1, min(bias.x / clipPos.w, 0));
150 #else
151 clipPos.z += saturate(bias.x / clipPos.w);
152 #endif
153 #endif
154
155 #if defined(UNITY_REVERSED_Z)
156 float clamped = min(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE);
157 #else
158 float clamped = max(clipPos.z, clipPos.w * UNITY_NEAR_CLIP_VALUE);
159 #endif
160
161 clipPos.z = lerp(clipPos.z, clamped, bias.y);
162
163 return clipPos;
164 }
165
166 v2f vert(appdata_base v)
167 {
168 v2f o;
169 float3 bias = shadowBias();
170
171 #if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
172 o.vec = mul(unity_ObjectToWorld, v.vertex).xyz - _LightPositionRange.xyz;
173 o.pos = UnityObjectToClipPos(v.vertex);
174 #else
175 o.pos = clipSpaceShadowCasterPos(v.vertex, v.normal, bias);
176 o.pos = applyLinearShadowBias(o.pos, bias);
177 #endif
178
179 return o;
180 }
181
182 float4 frag(v2f i) : SV_Target
183 {
184 float3 bias = shadowBias();
185
186 #if defined(SHADOWS_CUBE) && !defined(SHADOWS_CUBE_IN_DEPTH_TEX)
187 return UnityEncodeCubeShadowDepth((length(i.vec) + bias.x) * _LightPositionRange.w);
188 #else
189 return 0;
190 #endif
191 }
192 ENDCG
193 }
194 }
195}
ですが、シャドウマップの解像度をインスペクターで指定してやらなければならず不便で、計算式もあれでいいのか自信がありません。信頼できる情報筋...できればUnity Technologiesの方からの意見をいただきたいところではあります。
「What's in unity_LightShadowBias? - Unity Answers」で類似した質問が出ているようですが(もしかしてご質問者さんご本人でしょうかね?)、あちらならUnityの開発者さんの目にとまりやすいかもしれません。時折チェックして新情報が投稿されていないかご確認いただくといいかと思います。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/05/16 07:46