diff --git a/_posts/Cg/2024-08-06-advanced-shadow.adoc b/_posts/Cg/2024-08-06-advanced-shadow.adoc index 5a776a1..8f5dda6 100644 --- a/_posts/Cg/2024-08-06-advanced-shadow.adoc +++ b/_posts/Cg/2024-08-06-advanced-shadow.adoc @@ -11,27 +11,23 @@ === 泊松采样PCF -除了使用上述所说的3x3区域进行采样,还可以使用泊松圆盘分布采样,以得到更好的软阴影效果 +除了使用上述所说的3x3区域进行采样,还可以使用泊松圆盘分布采样,以得到更好的软阴影效果。 === 硬件PCF 硬件PCF是在2x2的区域上通过纹理采样器 compare func 并开启 linear sampling 实现双线性插值采样以做到直接计算出当前像素插值阴影权重。 -也可以通过4次硬件PCF再手动混合以得到一个3x3(四个采样中心偏离0.5texel)或4x4(四个采样中心偏离1.0texel)的硬件PCF。 +也可以通过4次硬件PCF再手动混合以得到一个3x3(四个采样中心偏离0.5texel)或4x4(四个采样中心偏离1.0texel)的硬件PCFfootnote:4[阴影的PCF采样优化算法 https://zhuanlan.zhihu.com/p/369761748]。 === 连续采样PCF -> https://zhuanlan.zhihu.com/p/369761748 + -> https://tajourney.games/5482/ + -> https://blog.csdn.net/wodownload2/article/details/134932880 - ==== 理论 -之前提到的PCF方法无论如何都会在半影区看到阴影分层现象。 +之前提到的PCF方法无论如何都会在半影区看到阴影分层现象footnote:4[]。 image::/assets/images/2024-08-09-pcf-shadow-layering.png[] -我们的一张阴影贴图实际上是这样离散的值。 +我们的一张阴影贴图实际上是这样离散的值footnote:3[Unity PCF 采样优化算法 https://tajourney.games/5482]。 image::/assets/images/2024-08-09-pcf-dicrete-texture.png[] @@ -51,16 +47,21 @@ image::/assets/images/2024-08-12-convolution-of-box-signal.gif[] ==== 实现 -实现上,考虑采样kernel是一个底为3,高为1.5的等腰三角形,在4x4的区域上进行采样混合。 +实现上,考虑采样kernel是一个底为3,高为1.5的等腰三角形,在4x4的区域上进行采样混合footnote:3[]footnote:5[解读unity内置的软阴影处理方式 https://blog.csdn.net/wodownload2/article/details/134932880]。 以实际渲染的点为中心,分别计算xy轴4个像素所占的权重,即如下图。 红点处为实际需要渲染的像素在shadow map中的uv坐标,在x,y两个方向分别计算四个像素的权重贡献。 -4x4的pcf是分为了4个2x2的Group进行计算的,因此红点左边的两个weight是Group1,3的权重贡献,同理再对y轴计算一遍便可以得到所有权重,用于计算实际采样点的坐标。 - image::/assets/images/2024-08-12-pcf-conv-calulate-weight.png[] +4x4的pcf是分为了4个2x2的Group进行计算的,因此红点左边的两个weight(橙色,黄色)是Group1,3的权重贡献,同理再对y轴计算一遍便可以得到所有权重,用于计算实际采样点的坐标。 + +[stem] +++++ +weight_橙 = \frac{S_橙}{S_黄 + S_橙} +++++ + NOTE: 红点不一定正好落在4x4区域的正中心 image::/assets/images/2024-08-12-pcf-conv-calulate-weight-not-center.png[] @@ -71,19 +72,13 @@ image::/assets/images/2024-08-12-pcf-conv-calulate-weight-not-center.png[] == Cascaded Shadow Map -Reference: - -> https://www.researchgate.net/publication/220805307_Parallel-split_shadow_maps_for_large-scale_virtual_environments + -> https://learn.microsoft.com/en-us/windows/win32/dxtecharts/common-techniques-to-improve-shadow-depth-maps -> https://zhuanlan.zhihu.com/p/45673049 + -> https://blog.csdn.net/qq_39300235/article/details/107796167 + -> https://www.cnblogs.com/X-Jun/p/16111750.html + +这篇文章大致讲解了CSM在Unity里的实现方法footnote:8[Unity实时阴影实现——Cascaded Shadow Mapping https://zhuanlan.zhihu.com/p/45673049]。这篇文章很完整的讲解了一个基于DX的CSM的示例footnote:1[DirectX11 With Windows SDK--38 级联阴影贴图(CSM) https://www.cnblogs.com/X-Jun/p/16111750.html],但是比较复杂。 === Split view frustum image::/assets/images/2024-08-06-z-split-frustum.png[] -考虑shadow map上一个像素大小stem:[\textit{d}s \times \textit{d}s],应该会根据面片的法线方向而对应到屏幕上stem:[\textit{d}p]长度的一片区域,而stem:[\textit{d}p/\textit{d}s]便可以被视为shadow map aliasing(因为屏幕上一像素的位置应该至少对应shadow map上一像素才是最完美的状态)当stem:[\textit{d}p]大于屏幕像素时,便会发生欠采样,也就是屏幕上多个像素对应到shadow map上一个像素。 +考虑shadow map上一个像素大小stem:[\textit{d}s \times \textit{d}s],应该会根据面片的法线方向而对应到屏幕上stem:[\textit{d}p]长度的一片区域,而stem:[\textit{d}p/\textit{d}s]便可以被视为shadow map aliasing(因为屏幕上一像素的位置应该至少对应shadow map上一像素才是最完美的状态)当stem:[\textit{d}p]大于屏幕像素时,便会发生欠采样,也就是屏幕上多个像素对应到shadow map上一个像素footnote:6[Parallel-split shadow maps for large-scale virtual environments https://www.researchgate.net/publication/220805307_Parallel-split_shadow_maps_for_large-scale_virtual_environments]。 [stem] ++++ @@ -152,8 +147,19 @@ NOTE: 这里应该需要开启Cull off,因为可能会有物体正好穿过近 image::/assets/images/2024-08-08-csm-cull-back-leak.png[] +=== Render shadow + +. 根据划分方法决定使用哪一张shadow mapfootnote:1[] + +* 根据near plne和far plane划分(Interval-Based Cascade Selection) +* 根据世界坐标变换到light clip space在0-1范围内(Map-Based Cascade Selection) + +. 使用阴影算法如PCF等计算该点阴影 + === Artifact +微软也提到了一些shdowmap的问题footnote:7[Common Techniques to Improve Shadow Depth Maps https://learn.microsoft.com/en-us/windows/win32/dxtecharts/common-techniques-to-improve-shadow-depth-maps]。 + . plane边缘处出现狗啃阴影 + -- @@ -187,12 +193,51 @@ image::/assets/images/2024-08-09-csm-shadow-seam.png[] . 当物体横跨两个分割时,出现阴影的断层 + -- -解决方案:blend +image::/assets/images/2024-08-09-csm-two-frustum-blend.gif[] + +解决方案:blendfootnote:1[] +-- + +* Interval-Based Blend ++ +-- +``` + pixelDepth + |<- ->| +/-+-------/----------+------/-------- +0 N F[0] F[i] + |<-blendInterval->| +blendBandLocation = 1 - depth/F[0] or +blendBandLocation = 1 - (depth-F[0]) / (F[i]-F[0]) +blendWeight = blendBandLocation / g_blendConstant; +``` +-- + +* Map-Based Blend ++ +-- +``` + _____________________ + | map[i+1] | + | | + | 0_______0 | + |______| map[i]|______| + | 0.5 | + |_______| + 0 0 +blendBandLocation = min(tx, ty, 1-tx, 1-ty); +blendWeight = blendBandLocation / g_blendConstant; +``` +-- + ++ +-- +计算出weight后对两级阴影进行lerp -image::/assets/images/2024-08-09-csm-large-object-blend.png[] +image::/assets/images/2024-08-14-csm-shadow-blend.gif[] -- -. 摄像机平移时,阴影抖动 +. 摄像机平移时,阴影抖动footnote:2[CSM中一些常见问题的解决方式 https://blog.csdn.net/qq_39300235/article/details/107796167] + -- 原因是每次摄像机平移时都需要重新计算世界空间到光空间的投影矩阵,由于小数精度问题,导致片元对应的深度贴图采样位置在像素之间跳跃。 @@ -202,7 +247,7 @@ image::/assets/images/2024-08-09-csm-large-object-blend.png[] image::/assets/images/2024-08-12-csm-camera-translate-shimmering.gif[] -- -. 摄像机旋转时,阴影抖动 +. 摄像机旋转时,阴影抖动footnote:2[] + -- 原因同上,旋转时light cam的包围盒发生变化,导致屏幕像素与shadow map之间的映射发生变化,出现闪烁。 diff --git a/assets/images/2024-08-09-csm-large-object-blend.png b/assets/images/2024-08-09-csm-large-object-blend.png deleted file mode 100644 index ad0daca..0000000 Binary files a/assets/images/2024-08-09-csm-large-object-blend.png and /dev/null differ diff --git a/assets/images/2024-08-09-csm-two-frustum-blend.gif b/assets/images/2024-08-09-csm-two-frustum-blend.gif new file mode 100644 index 0000000..9021b2d Binary files /dev/null and b/assets/images/2024-08-09-csm-two-frustum-blend.gif differ diff --git a/assets/images/2024-08-14-csm-shadow-blend.gif b/assets/images/2024-08-14-csm-shadow-blend.gif new file mode 100644 index 0000000..f6f2878 Binary files /dev/null and b/assets/images/2024-08-14-csm-shadow-blend.gif differ