Skip to content

Commit

Permalink
csm
Browse files Browse the repository at this point in the history
  • Loading branch information
Katzeee committed Aug 14, 2024
1 parent 99ddf29 commit bc9bb59
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 23 deletions.
91 changes: 68 additions & 23 deletions _posts/Cg/2024-08-06-advanced-shadow.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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[]

Expand All @@ -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[]
Expand All @@ -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]
++++
Expand Down Expand Up @@ -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边缘处出现狗啃阴影
+
--
Expand Down Expand Up @@ -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]
+
--
原因是每次摄像机平移时都需要重新计算世界空间到光空间的投影矩阵,由于小数精度问题,导致片元对应的深度贴图采样位置在像素之间跳跃。
Expand All @@ -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之间的映射发生变化,出现闪烁。
Expand Down
Binary file removed assets/images/2024-08-09-csm-large-object-blend.png
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/2024-08-14-csm-shadow-blend.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit bc9bb59

Please sign in to comment.