@@ -9,7 +9,7 @@ counter: true
99先来简单回顾一下** 阴影映射** (shadow mapping)的原理。
1010
1111- 它是一种** 两趟** (2-pass)** 算法**
12- - 第一趟:从光源位置出发看向场景,生成** 阴影图 ** (SM),即来自光源的「深度纹理」
12+ - 第一趟:从光源位置出发看向场景,生成** 阴影贴图 ** (SM),即来自光源的「深度纹理」
1313
1414 <div style =" text-align : center " >
1515 <img src =" images/lec3/1.png " width =40% >
@@ -90,7 +90,7 @@ counter: true
9090 === "**自遮挡**"
9191
9292 - 左图中虽然人物的阴影是正常的,但是地面上却有一圈圈的纹路。这并非摩尔纹(即不是采样问题),而是由**数值精度**导致的问题。
93- - 来看右边的示意图,光源照射场景后会得到图中用红色线条标出的阴影图 ,它是离散的,且每个点的深度即为光源到该点的距离。
93+ - 来看右边的示意图,光源照射场景后会得到图中用红色线条标出的阴影贴图 ,它是离散的,且每个点的深度即为光源到该点的距离。
9494 - 现在从眼睛处看向某一点,然后取该点和光源的连线(蓝色虚线)。但由于数值精度问题,实际上该点的深度位于橙色线条标记的地方,也就是说光源对应的深度实际上会更浅。这就导致了橙色线条处的地板「挡住了」该点,误以为该点处在阴影中。
9595 - **掠射角**(grazing angle)越大,该问题越严重(所以垂直照射时问题最小)。
9696
@@ -120,16 +120,16 @@ counter: true
120120
121121 === "**反走样**"
122122
123- 阴影图的分辨率有限 ,如果分辨率不够大,很容易出现锯齿状的阴影。
123+ 阴影贴图的分辨率有限 ,如果分辨率不够大,很容易出现锯齿状的阴影。
124124
125125 <div style="text-align: center">
126126 <img src="images/lec3/13.png" width=60%>
127127 </div>
128128
129129 解决方案有:
130130
131- - **级联阴影映射**(cascaded shadow mapping):为阴影图的不同位置设置不同分辨率 ,在工业界中得到应用
132- - 动态分辨率的阴影图
131+ - **级联阴影映射**(cascaded shadow mapping):为阴影贴图的不同位置设置不同分辨率 ,在工业界中得到应用
132+ - 动态分辨率的阴影贴图
133133
134134
135135## The Math Behind Shadow Mapping
193193 - 而不是为了实现软阴影效果(也就是后面介绍的 PCSS)
194194 - 对** 阴影比较结果** (对于任意一点,在阴影中记为0,否则记为1)进行** 滤波** (filtering)(模糊)
195195
196- - ** 不直接对阴影图做滤波 ** 的原因:
196+ - ** 不直接对阴影贴图做滤波 ** 的原因:
197197 - (第一趟中)这样的纹理滤波仅对颜色分量计算平均值,这导致先会得到一个模糊的阴影贴图(比如把物体本身边缘给模糊掉了)
198198 - (第二趟中)而计算深度值平均值后再进行比较,则仍然得到的是** 二元** 可见性(硬阴影),并没有起到模糊阴影的效果
199199
303303
304304通过这个公式,我们就能理解之前为什么说
305305
306- - PCF != 先对阴影图滤波再比较的结果
306+ - PCF != 先对阴影贴图滤波再比较的结果
307307
308308 $$
309309 V(x) \ne \chi^+ \{[w * D_{\text{SM}}](q) - D_{\text{scene}}(x)\}
337337- 方差:
338338 - $\text{Var}(X) = E(X^2) - E^2(X)$
339339 - 所以只需额外计算深度平方的均值
340- - 也就是说只要生成另一张阴影图 ,记录深度的平方值(不会产生很多额外的开销)
340+ - 也就是说只要生成另一张阴影贴图 ,记录深度的平方值(不会产生很多额外的开销)
341341
342342回到前面有关「百分比接近」的讨论。在已经得到均值和方差的基础上,阴影区域的精确解(多少百分比的纹素的深度比着色点的更浅)便是着色点深度在对应正态分布的 ** CDF** (累计分布函数)值(即曲线下方的面积)。
343343
361361
362362总结一下 VSSM 在第三步中的改进:
363363
364- - 阴影图生成 :
364+ - 阴影贴图生成 :
365365 - 「平方深度图」:和普通的深度图并行计算,计算量取决于像素个数
366366 - 计算均值:MIPMAP 或 SAT(稍后介绍)
367367- 运行时:
@@ -453,7 +453,7 @@ VSSM 中还未讲到的一块是如何加速计算任意范围(深度和深度
453453 <img src="images/lec3/34.png" width=50%>
454454 </div>
455455
456- 矩阴影映射的做法和 VSSM 极其相似,但它要生成关于 $z, z^2, z^3, z^4$ 的阴影图 (4 张),并在执行遮挡物搜索和 PCF 期间恢复 CDF。
456+ 矩阴影映射的做法和 VSSM 极其相似,但它要生成关于 $z, z^2, z^3, z^4$ 的阴影贴图 (4 张),并在执行遮挡物搜索和 PCF 期间恢复 CDF。
457457
458458!!! success "优点:能产生非常好的结果。"
459459
@@ -467,4 +467,110 @@ VSSM 中还未讲到的一块是如何加速计算任意范围(深度和深度
467467 </div>
468468
469469
470- ## Distance Field Soft Shadows
470+ ## Distance Field Soft Shadows
471+
472+ ???+ example "例子(距离场软阴影的效果)"
473+
474+ <div style="text-align: center">
475+ <img src="images/lec3/36.png" width=80%>
476+ </div>
477+
478+
479+ ### Distance Function
480+
481+ 先来回顾一下** 距离函数** (distance functions)的概念。它是指在任何时刻给出到物体上最近位置的最小距离(可以是带符号的距离)。比如对于下图的字母 A,距离函数便是点到字母轮廓的最近距离,并且右图以可视化的方式呈现计算结果。
482+
483+ <div style =" text-align : center " >
484+ <img src="images/lec3/37.png" width=70%>
485+ </div >
486+
487+ ???+ example "例子(对移动边界进行混合(线性插值))"
488+
489+ <div style="text-align: center">
490+ <img src="images/lec3/38.png" width=80%>
491+ </div>
492+
493+ - SDF == 0 => 边界
494+ - 可以看到,如果只是简单的线性插值,只能得到中间的灰色区域,而不会产生新的黑白边界
495+ - 但如果是对两幅图的 SDF 进行线性插值,还原后的图像的中间就有明显的边界了
496+
497+ 实际上可以通过混合任意两个距离函数 d1, d2 来混合任意形状的物体:
498+
499+ <div style =" text-align : center " >
500+ <img src="images/lec3/39.png" width=90%>
501+ </div >
502+
503+ !!! info "注"
504+
505+ SDF 和**最优传输**(optimal transport)理论密切相关,建议课后学习一下。
506+
507+
508+ ### The Usages of Distance Fields
509+
510+ 距离场的用途有:
511+
512+ - 通过** 光线行进** (ray marching)(球体追踪(sphere tracing))来执行光线和 SDF 的求交计算(ray-SDF intersection)
513+ - 背后蕴含了一个非常聪明的思想:** SDF 的值 == 周围的「安全」距离**
514+ - 因此每次在点 p 处只需行进 SDF(p) 的距离,这样保证不会和任何物体相交
515+ - 当 SDF 小到一定程度后(也就是说离物体足够接近时),或者行进很长一段距离后还是没有碰到物体时停止行进
516+ - 可以处理运动的物体,但不适合用在有能产生形变的物体的场景中(需根据形变程度重新计算)
517+
518+ <div style =" text-align : center " >
519+ <img src="images/lec3/40.png" width=60%>
520+ </div >
521+
522+ - 使用 SDF 确定(近似的)** 遮挡百分比** (percentage of occlusion)
523+ - ** SDF 的值 == 眼睛看到的「安全」角度**
524+ - 观察:更小的安全角度 <-> 更小的可见性
525+
526+ <div style =" text-align : center " >
527+ <img src="images/lec3/41.png" width=50%>
528+ </div >
529+
530+
531+ ### Distance Field Soft Shadows
532+
533+ 现在正式来看如何计算距离场软阴影:在光线行进阶段的每一步中计算来自眼睛的「安全」角度,并保留最小值。
534+
535+ ![ ] ( images/lec3/43.png ) { align=right width=40% }
536+
537+ <div style =" text-align : center " >
538+ <img src="images/lec3/42.png" width=50%>
539+ </div >
540+
541+ 也许读者会想利用圆的切线来求解安全角度,这样的话计算公式便是 $\arcsin \dfrac{\text{SDF}(p)}{p - o}$。但在着色阶段中,人们通常希望能避免像反三角函数这样复杂的计算。因此实际上会用这个公式来算:$\min\left\{ \dfrac{k \cdot \text{SDF}(p)}{p - o}, 1.0 \right\} $。
542+
543+ - $\dfrac{k \cdot \text{SDF}(p)}{p - o}$ 用于近似表示反三角函数,加上 $\min$ 使该值不超过 1
544+ - $k$ 值越大,意味着安全角度很大,半影将更早被裁剪,因而让阴影变得更硬
545+
546+ ???+ example "距离场的可视化结果"
547+
548+ <div style="text-align: center">
549+ <img src="images/lec3/44.png" width=80%>
550+ </div>
551+
552+ !!! success "优点"
553+
554+ - 快速
555+ - 应该说生成 SDF 比生成阴影贴图更快,运行时查询的时候两者速度应该差不多
556+
557+ - 高质量
558+
559+ !!! bug "缺点"
560+
561+ - 需要预计算
562+ - 需要大量存储,不过也有一些缓解措施
563+ - 使用 KD 树、八叉树等对空间划分,只去计算场景中较近区域的 SDF
564+ - 有人尝试过用深度学习方法压缩,但这样做反而增加解压的时间,因此不太可能在实际中得到应用
565+
566+ - 接缝处可能存在瑕疵
567+
568+ ??? info "一个有趣的应用"
569+
570+ 在 RTR 中实现了抗锯齿/无限分辨率的字符。
571+
572+ <div style="text-align: center">
573+ <img src="images/lec3/45.png" width=60%>
574+ </div>
575+
576+ GitHub 链接:<https://github.com/protectwise/troika/tree/master/packages/troika-three-text>
0 commit comments