1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef SHADERS__COMMON__3D_DM_SHADOWING_COMMON_H
17 #define SHADERS__COMMON__3D_DM_SHADOWING_COMMON_H
18
19 #include "render/shaders/common/render_compatibility_common.h"
20
21 /*
22 Basic shadowing functions
23 */
24 // NOTE: has currently some issues
25 #define CORE_SHADOW_ENABLE_RECEIVER_PLANE_BIAS 0
26
ComputeReceiverPlaneDepthBias(const vec3 uvDdx,const vec3 uvDdy)27 vec2 ComputeReceiverPlaneDepthBias(const vec3 uvDdx, const vec3 uvDdy)
28 {
29 vec2 uvBias;
30 uvBias.x = uvDdy.y * uvDdx.z - uvDdx.y * uvDdy.z;
31 uvBias.y = uvDdx.x * uvDdy.z - uvDdy.x * uvDdx.z;
32 uvBias *= 1.0f / ((uvDdx.x * uvDdy.y) - (uvDdx.y * uvDdy.x));
33 return uvBias;
34 }
35
GetPcfSample(sampler2DShadow shadow,const vec2 baseUv,const vec2 offset,const float compareDepth,const vec2 texelSize,const vec2 receiverPlaneDepthBias)36 float GetPcfSample(sampler2DShadow shadow, const vec2 baseUv, const vec2 offset, const float compareDepth,
37 const vec2 texelSize, const vec2 receiverPlaneDepthBias)
38 {
39 const vec2 uvOffset = offset * texelSize;
40 #if (CORE_SHADOW_ENABLE_RECEIVER_PLANE_BIAS == 1)
41 const float compZ = compareDepth + dot(uvOffset, receiverPlaneDepthBias);
42 #else
43 const float compZ = compareDepth;
44 #endif
45 return texture(shadow, vec3(baseUv + uvOffset, compZ)).x;
46 }
47
ValidShadowRange(const vec3 shadowCoord,float stepSize,float shadowIdx)48 bool ValidShadowRange(const vec3 shadowCoord, float stepSize, float shadowIdx)
49 {
50 const float xMin = stepSize * shadowIdx;
51 const float xMax = xMin + stepSize;
52 return ((shadowCoord.z > 0.0) && (shadowCoord.x > xMin) && (shadowCoord.x < xMax));
53 }
54
55 // http://www.ludicon.com/castano/blog/articles/shadow-mapping-summary-part-1/
56 // MIT license: https://github.com/TheRealMJP/Shadows/blob/master/Shadows/Mesh.hlsl
CalcPcfShadow(sampler2DShadow shadow,vec4 inShadowCoord,float NoL,vec4 shadowFactor,vec4 atlasSizeInvSize,uvec2 shadowFlags)57 float CalcPcfShadow(
58 sampler2DShadow shadow, vec4 inShadowCoord, float NoL, vec4 shadowFactor, vec4 atlasSizeInvSize, uvec2 shadowFlags)
59 {
60 // divide for perspective (just in case if used)
61 const vec3 shadowCoord = inShadowCoord.xyz / inShadowCoord.w;
62
63 CORE_RELAXEDP float light = 1.0;
64 if (ValidShadowRange(shadowCoord, shadowFactor.w, float(shadowFlags.x))) {
65 const vec2 textureSize = atlasSizeInvSize.xy;
66 const vec2 texelSize = atlasSizeInvSize.zw;
67 const float normalBias = shadowFactor.z;
68 const float depthBias = shadowFactor.y;
69 const float bias = max(normalBias * (1.0 - NoL), depthBias);
70
71 const vec2 offset = vec2(0.5);
72 const vec2 uv = (shadowCoord.xy * textureSize) + offset;
73 vec2 baseUv = (floor(uv) - offset) * texelSize;
74 const float fracS = fract(uv.x);
75 const float fracT = fract(uv.y);
76
77 #if (CORE_SHADOW_ENABLE_RECEIVER_PLANE_BIAS == 1)
78 const vec3 shadowDdx = dFdx(shadowCoord);
79 const vec3 shadowDdy = dFdy(shadowCoord);
80 const vec2 receiverPlaneDepthBias = ComputeReceiverPlaneDepthBias(shadowDdx, shadowDdy);
81
82 // static depth biasing for incorrect fractional sampling
83 const float fSamplingError = 2.0 * dot(vec2(1.0) * texelSize, abs(receiverPlaneDepthBias));
84
85 const float compareDepth = shadowCoord.z - min(fSamplingError, 0.01);
86 #else
87 // NOTE: not used for this purpose ATM
88 const vec2 receiverPlaneDepthBias = vec2(shadowFactor.y, shadowFactor.y);
89 const float compareDepth = shadowCoord.z - bias;
90 #endif
91
92 const float uw0 = 4.0 - 3.0 * fracS;
93 const float uw1 = 7.0;
94 const float uw2 = 1.0 + 3.0 * fracS;
95
96 const float u0 = (3.0 - 2.0 * fracS) / uw0 - 2.0;
97 const float u1 = (3.0 + fracS) / uw1;
98 const float u2 = fracS / uw2 + 2.0;
99
100 const float vw0 = 4.0 - 3.0 * fracT;
101 const float vw1 = 7.0;
102 const float vw2 = 1.0 + 3.0 * fracT;
103
104 const float v0 = (3.0 - 2.0 * fracT) / vw0 - 2.0;
105 const float v1 = (3.0 + fracT) / vw1;
106 const float v2 = fracT / vw2 + 2.0;
107
108 CORE_RELAXEDP float sum = 0;
109
110 sum += uw0 * vw0 * GetPcfSample(shadow, baseUv, vec2(u0, v0), compareDepth, texelSize, receiverPlaneDepthBias);
111 sum += uw1 * vw0 * GetPcfSample(shadow, baseUv, vec2(u1, v0), compareDepth, texelSize, receiverPlaneDepthBias);
112 sum += uw2 * vw0 * GetPcfSample(shadow, baseUv, vec2(u2, v0), compareDepth, texelSize, receiverPlaneDepthBias);
113
114 sum += uw0 * vw1 * GetPcfSample(shadow, baseUv, vec2(u0, v1), compareDepth, texelSize, receiverPlaneDepthBias);
115 sum += uw1 * vw1 * GetPcfSample(shadow, baseUv, vec2(u1, v1), compareDepth, texelSize, receiverPlaneDepthBias);
116 sum += uw2 * vw1 * GetPcfSample(shadow, baseUv, vec2(u2, v1), compareDepth, texelSize, receiverPlaneDepthBias);
117
118 sum += uw0 * vw2 * GetPcfSample(shadow, baseUv, vec2(u0, v2), compareDepth, texelSize, receiverPlaneDepthBias);
119 sum += uw1 * vw2 * GetPcfSample(shadow, baseUv, vec2(u1, v2), compareDepth, texelSize, receiverPlaneDepthBias);
120 sum += uw2 * vw2 * GetPcfSample(shadow, baseUv, vec2(u2, v2), compareDepth, texelSize, receiverPlaneDepthBias);
121
122 sum *= 1.0f / 144.0f;
123 // shadow strenght
124 light = 1.0 - (sum * shadowFactor.x);
125 }
126
127 return light;
128 }
CalcPcfShadowMed(sampler2DShadow shadow,vec4 inShadowCoord,float NoL,vec4 shadowFactor,vec4 atlasSizeInvSize,uvec2 shadowFlags)129 float CalcPcfShadowMed(
130 sampler2DShadow shadow, vec4 inShadowCoord, float NoL, vec4 shadowFactor, vec4 atlasSizeInvSize, uvec2 shadowFlags)
131 {
132 // divide for perspective (just in case if used)
133 const vec3 shadowCoord = inShadowCoord.xyz / inShadowCoord.w;
134
135 CORE_RELAXEDP float light = 1.0;
136 if (ValidShadowRange(shadowCoord, shadowFactor.w, float(shadowFlags.x))) {
137 const vec2 textureSize = vec2(textureSize(shadow, 0).xy);
138 const vec2 texelSize = 1.0 / textureSize;
139 const float normalBias = shadowFactor.z;
140 const float depthBias = shadowFactor.y;
141 const float bias = max(normalBias * (1.0 - NoL), depthBias);
142
143 const vec2 offset = vec2(0.5);
144 const vec2 uv = (shadowCoord.xy * textureSize) + offset;
145 vec2 baseUv = (floor(uv) - offset) * texelSize;
146 const float fracS = fract(uv.x);
147 const float fracT = fract(uv.y);
148
149 #if (CORE_SHADOW_ENABLE_RECEIVER_PLANE_BIAS == 1)
150 const vec3 shadowDdx = dFdx(shadowCoord);
151 const vec3 shadowDdy = dFdy(shadowCoord);
152 const vec2 receiverPlaneDepthBias = ComputeReceiverPlaneDepthBias(shadowDdx, shadowDdy);
153
154 // static depth biasing for incorrect fractional sampling
155 const float fSamplingError = 2.0 * dot(vec2(1.0) * texelSize, abs(receiverPlaneDepthBias));
156
157 const float compareDepth = shadowCoord.z - min(fSamplingError, 0.01);
158 #else
159 // NOTE: not used for this purpose ATM
160 const vec2 receiverPlaneDepthBias = vec2(shadowFactor.y, shadowFactor.y);
161 const float compareDepth = shadowCoord.z - bias;
162 #endif
163
164 const float uw0 = (3 - 2 * fracS);
165 const float uw1 = (1 + 2 * fracS);
166
167 const float u0 = (2 - fracS) / uw0 - 1;
168 const float u1 = fracS / uw1 + 1;
169
170 const float vw0 = (3 - 2 * fracT);
171 const float vw1 = (1 + 2 * fracT);
172
173 float v0 = (2 - fracT) / vw0 - 1;
174 float v1 = fracT / vw1 + 1;
175
176 CORE_RELAXEDP float sum = 0;
177
178 sum += uw0 * vw0 * GetPcfSample(shadow, baseUv, vec2(u0, v0), compareDepth, texelSize, receiverPlaneDepthBias);
179 sum += uw1 * vw0 * GetPcfSample(shadow, baseUv, vec2(u1, v0), compareDepth, texelSize, receiverPlaneDepthBias);
180
181 sum += uw0 * vw1 * GetPcfSample(shadow, baseUv, vec2(u0, v1), compareDepth, texelSize, receiverPlaneDepthBias);
182 sum += uw1 * vw1 * GetPcfSample(shadow, baseUv, vec2(u1, v1), compareDepth, texelSize, receiverPlaneDepthBias);
183
184 sum *= (1.0f / 16.0f);
185 // shadow strenght
186 light = 1.0 - (sum * shadowFactor.x);
187 }
188
189 return light;
190 }
191
CalcPcfShadowSimpleSample(sampler2DShadow shadow,vec4 inShadowCoord,vec4 shadowFactor,vec4 atlasSizeInvSize,uvec2 shadowFlags)192 float CalcPcfShadowSimpleSample(
193 sampler2DShadow shadow, vec4 inShadowCoord, vec4 shadowFactor, vec4 atlasSizeInvSize, uvec2 shadowFlags)
194 {
195 // divide for perspective (just in case if used)
196 const vec3 shadowCoord = inShadowCoord.xyz / inShadowCoord.w;
197
198 CORE_RELAXEDP float light = 1.0;
199 if (ValidShadowRange(shadowCoord, shadowFactor.w, float(shadowFlags.x))) {
200 const float bias = 0.002;
201 const vec2 baseUv = shadowCoord.xy;
202 const float compareDepth = shadowCoord.z - bias;
203 const CORE_RELAXEDP float sum = texture(shadow, vec3(baseUv, compareDepth)).x;
204
205 light = 1.0 - (sum * shadowFactor.x);
206 }
207
208 return light;
209 }
210
211 // https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch08.html
212 // reduce light bleeding
LinstepVsm(float minVal,float maxVal,float v)213 float LinstepVsm(float minVal, float maxVal, float v)
214 {
215 return clamp((v - minVal) / (maxVal - minVal), 0.0, 1.0);
216 }
ReduceLightBleedingVsm(float pMax,float amount)217 float ReduceLightBleedingVsm(float pMax, float amount)
218 {
219 // Remove the [0, amount] tail and linearly rescale (amount, 1].
220 return LinstepVsm(amount, 1.0, pMax);
221 }
222
CalcVsmShadow(sampler2D shadow,vec4 inShadowCoord,float NoL,vec4 shadowFactors,vec4 atlasSizeInvSize,uvec2 shadowFlags)223 float CalcVsmShadow(
224 sampler2D shadow, vec4 inShadowCoord, float NoL, vec4 shadowFactors, vec4 atlasSizeInvSize, uvec2 shadowFlags)
225 {
226 // divide for perspective (just in case if used)
227 const vec3 shadowCoord = inShadowCoord.xyz / inShadowCoord.w;
228
229 CORE_RELAXEDP float light = 1.0;
230 if (ValidShadowRange(shadowCoord, shadowFactors.w, float(shadowFlags.x))) {
231 const vec2 moments = texture(shadow, shadowCoord.xy).xy;
232 if (shadowCoord.z > moments.x) {
233 float variance = moments.y - (moments.x * moments.x);
234 const float evaluatedMinValue = 0.00001;
235 variance = max(variance, evaluatedMinValue);
236
237 const float d = shadowCoord.z - moments.x;
238 light = variance / (variance + d * d);
239 const float evaluatedLightBleedingVal = 0.2;
240 light = ReduceLightBleedingVsm(light, evaluatedLightBleedingVal);
241 light = 1.0 - (1.0 - light) * shadowFactors.x;
242 }
243 }
244
245 return light;
246 }
247
CalcVsmShadowSimpleSample(sampler2D shadow,vec4 inShadowCoord,vec4 shadowFactors,vec4 atlasSizeInvSize,uvec2 shadowFlags)248 float CalcVsmShadowSimpleSample(
249 sampler2D shadow, vec4 inShadowCoord, vec4 shadowFactors, vec4 atlasSizeInvSize, uvec2 shadowFlags)
250 {
251 // divide for perspective (just in case if used)
252 const vec3 shadowCoord = inShadowCoord.xyz / inShadowCoord.w;
253
254 CORE_RELAXEDP float light = 1.0;
255 if (ValidShadowRange(shadowCoord, shadowFactors.w, float(shadowFlags.x))) {
256 const vec2 moments = texture(shadow, shadowCoord.xy).xy;
257 if (shadowCoord.z > moments.x) {
258 float variance = moments.y - (moments.x * moments.x);
259 const float evaluatedMinValue = 0.00001;
260 variance = max(variance, evaluatedMinValue);
261
262 const float d = shadowCoord.z - moments.x;
263 light = variance / (variance + d * d);
264 const float evaluatedLightBleedingVal = 0.2;
265 light = ReduceLightBleedingVsm(light, evaluatedLightBleedingVal);
266 light = 1.0 - (1.0 - light) * shadowFactors.x;
267 }
268 }
269
270 return light;
271 }
272
273 #endif // SHADERS__COMMON__3D_DM_SHADOWING_COMMON_H
274