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