--------------------------------------------------博主:mx
Shadow Volume(阴影体)
基本流程:
1.先画出Shadow Volume(阴影区域)
从交点V0,V1向外延申得到V2,V3,这样就能生成一个四边形面,其他几个面也是这样生成,并且只画轮廓,而不是每个区域内的三角形都画
2.z-Pass算法:
我们使用模板缓冲区,首先先清除模板缓冲区,然后将场景绘制入帧缓冲区中并得到深度图,然后关闭zbuffer和关闭颜色输出,然后打开模板测试,对于靠近相机的那个阴影区四边形面,如果z test pass则模板值增加1,否则不变,对于靠近相机的那个阴影区四边形面,如果z test pass 则模板值减少1,否则不变。最终如果模板缓冲区的值不为零就是在阴影区域。
这个应该可以用Depth Bounds Test来做
z-pass算法问题
但是这个算法存在一个问题,如果相机在阴影区域Volume里面,计数就会出现问题
3.z-fail算法:
基本上和z-pass一样,但是在计数上,做法是对于那个阴影区四边形面,如果z test fail则模板值减少1,否则不变,对于靠近相机的那个阴影区四边形面,如果z test fail 则模板值增加1,否则不变。最终如果模板缓冲区的值不为零就是在阴影区域。
但是这就要求Shadow volume是闭合的
Shadow Volume的缺陷:
极端的可变性。假如相机和光源在一个相同的位置,则Shadow Volume的像素占比大小是很小的,阴影计算消耗也小。但是当相机远离光源的时候,特别的当相机在Volume里面的时候,Shadow Volume的像素占比大小是很小的,阴影计算消耗是很可怕的,并且这个消耗难以预测
这个时候就应该了解,一种消耗更能够预测并且更加适合GPU的阴影生成方法----Shadow Mapping
Shadow Mapping(阴影映射)
基本流程:
先从相机方向,记录每个像素的z值(即深度),记下的这个图叫做阴影贴图,然后再从视线方向,将每个像素上看到位置通过变化矩阵转换到相机为圆心的坐标系,将该点的z值与阴影贴图中保存的值对比,如果渲染点离光源的距离比阴影贴图中相应的值要远,那么这个点就是在阴影中。这个算法的复杂度是相对可预测的,构建阴影贴图的成本和渲染的像素数是大致呈线性关系的,阴影贴图只需要生成一次。
Shadow caster:
表示可以投射阴影的物体,一般是光源视锥体内的物体
Shadow recevier:
表示可以接收阴影的物体,一般是视线视锥体内的物体
Shadow caster/Shadow recevier优化:
对这两个锥体的适当裁剪能够减少渲染压力。只渲染相关物体不仅可以节省渲染时间,还可以减少光源锥体所需的尺寸,从而增加阴影地图的有效分辨率,从而提高质量。此外,如果视锥体的近平面离光源越远越好,如果远平面离光源越近越好,就越有帮助。这样做可以增加z缓冲区的有效精度。
Shadow Mapping 存在的问题以及解决方案:
因为ShadowMap的深度图是存储在纹理中的,所以阴影的质量取决于阴影映射的分辨率(以像素为单位)和Z缓冲区的数值精度。
Shadow Acne(阴影自重叠):
原因:
这个问题有两个原因。其中之一就是处理器精度的数值限制。另一种来源是几何的,它是用一个点样本的值来表示一个区域的深度。也就是说,为光线生成的样本几乎永远不会和屏幕上的样本在同一个位置(例如,像素通常是在它们的中心取样的)。
解决方法:
可以添加一个偏移值来解决这个问题,但是一个恒定的偏移值对于斜射角度不同的光线有可能失败,正确方案是使用斜率标度偏移值,表面越远离光线/偏移角度越大,偏差就越大
Peter Panning (阴影悬浮/漏光):
--------------------------------------------------博主:mx
原因:
由于偏移量设置的太大,导致人物脚下的阴影发生了悬浮
解决方案:
第一种是只渲染物体的背面(即剔除物体的正面可以解决这个问题),但是有个问题,当物体的正面和背面几乎处于同一个位置的时候(例如纸张),这个方法就会出现问题。所以只渲染背面和添加偏移的使用最好视具体情况而定,例如,Sousa等人发现,将正面用于日光阴影,将背面用于室内灯光,可以达到最佳的应用效果。
第二种是渲染两次Shadow map,一次是正面的一次是背面的,然后深度取他们的平均值,但是这个的缺陷也很明显就算两个Shadow map的额外开销
抗锯齿:
锯齿出现的原因:
光线方向的Shadow map的一个元素屏幕空间的像素并不是一一对应的,如果我们有一个光源位于与眼睛相同的位置,那么阴影地图就会完美地与屏幕空间像素一一对应(游戏邦注:这里不存在可见的阴影,因为光线会照亮eve所看到的东西)。一旦光线的方向改变,这个像素比例也会改变,这会导致perspective aliasing。
解决方法:
简单粗暴的方法(不好):
增加Shadow map的分辨率,使得其中存储的一个像素更小,但是着明显要增加额外的开销。
另外更加有效的方法:
原理是改变光的采样方式使其更接近相机的模式。
透视阴影映射(PSM)[16911]、梯形阴影映射(TSM)[1132]和光空间透视阴影映射(LiSPSM)都是通过修改变换矩阵,使得靠近人眼位置的阴影贴图能获取更多的像素,从而达到缓解锯齿的目的,往往人眼与光源方向垂直时效果最好。Forsyth[2]比对了几个不同技术的优缺点: ● PSM,技术方案很不健壮,各种退化情况,需要考虑。引用原作者的一句话:Friends don’t let friends implement PSM。 ● TSM,类似于PSM的矩阵修改,但技术方案较稳定。问题在于复杂度较大,需要分割成多段,采用不同的变换矩阵,可以在片段着色器中用1D纹理来处理,如何找到阴影梯形(trapezoid)也是比较复杂的。引用原作者的一句话:Not very good at more “3D” worlds。 ● LiSPM,类似于PSM的矩阵修改,但技术方案较稳定。问题在于实现复杂度较大,需要凸包围体的交叉判断,但是可以进行估计。
这些技术在一些情况下会出问题,当遇到“deer in the headlights ”的情况的时候,即灯光在相机前并且指向相机,更多的shadow map采样点需要更加靠近眼睛,但是这些技术就会使情况更糟。并且像相机抖动移动而产生的不稳定的阴影质量使得这些方法不再受欢迎。
CSM:
2006年,Engel提出了CSM(Cascaded Shadow Maps)算法,将视锥进行切分成多份,如下图所示。在太阳方向上,对视锥进行分割,生成多级阴影贴图;在实时渲染时,再根据距离,选择合适的阴影贴图。 实际应用中,提高实时阴影的渲染效率有下面几种方法: ● 视锥内的模型未发生变化时,可以不用再渲染; ● 裁剪小物件; ● 使用低LOD的模型渲染阴影; ● CSM降低高级别的阴影贴图的更新频率; ● 使用静态阴影代替远处的级联阴影贴图 ● CSM可以和烘培光照贴图等技术混合使用
z-partitioning:
那每个区域怎么分割呢?CSM采用的是一种对数的分割方法。其公式如图:
(其中r是求得的距离对数,c是区域个数,f是远平面距离,n是近平面距离)
举例:如果场景中近平面距离是1米,原平面距离是1000米,我们有3个cascaded shadow map,那么r= 1000/1 = 10。近距离的平面距离是1和10,下一个间隔是10到100以保持这个比率,最后一个间隔是100到1000米。
这个划分方法有一个很大的问题:如果近深度只有0.1米,那么10000的立方根是21.54,一个相当高的比率,例如0.1到2.154到46.42到1000。这就意味着,生成的每个阴影地图都必须覆盖更大的区域,从而降低其精度。在实践中,这种划分为接近近平面的区域提供了相当大的分辨率,如果该区域中没有物体,这就浪费了。避免这种不匹配的一种方法是将分割距离设置为对数分布和等距分布的加权混合。 --------------------------------------------------博主:mx
SDSM:
Lauritzen等人提出了样本分布阴影图sample distribution shadow maps(SDSM),它使用前一帧的z深度值,通过两种方法中的一种来确定更好的分区: 第一个方法是查看z深度的最小值和最大值,并使用它们来设置近平面和远平面。这是在GPU上使用所谓的减少操作来执行的,其中一系列越来越小的缓冲区被计算或其他着色器分析,输出缓冲区反馈作为输入,直到一个1x1缓冲区被留下。 第二种方法还分析深度缓冲区的值,生成一个直方图,记录z深度沿范围的分布。除了找到紧密的近平面和远平面外,图中可能会有没有对象的缺口。通常添加到这样的一个区域,这个区域的任何分区平面都可以截取到对象实际存在的地方,从而为级联映射集提供更高的z深度精度。
当一个物体跨越两个阴影贴图的边界时,阴影的质量会发生突然的变化。一种解决方案是让划分区域稍微重叠。在这些重叠区域采集的样本收集了相邻阴影地图的结果,并将其混合。另一种方法是使用多重混合采样在这样的区域提取单个样品。
Percentage-Closer Soft Shadows(百分比渐进软阴影):
Percentage-Closer Filtering :
基本原理:
先确定采样点的表面的深度与它四周的四个纹素的的深度,然后确定每个阴影地图样本的点处于light或shadow中,例如处于阴影中为0,处于光亮中为1,然后用双线性插值来计算光线对表面位置的实际贡献,这样就产生了一个人为的软阴影。
但是这个没法根据距离产生不同程度的软阴影,比如根据远近,近处是硬阴影,远处是软阴影。这个时候就引出了我们的PCSS。
Percentage-Closer Soft Shadows:
基本原理:
根据光源宽度和遮挡物的距离、阴影接收点的距离来确定采样区域的大小,实际公式为
(其中dr是光源到达接受阴影物体的距离,do是光源到达遮挡物的距离)
下面是games202里的示意图
PCSS的计算主要分为三步:
- 计算着色点与平均遮挡物的距离 dr
- 利用平均遮挡物距离dr计算PCF用到的采样范围 Wsample
- 使用上一步得到的采样范围计算PCF
需要解决的问题以及解决方案:
问题:
找出dr需要对shadow map进行相当大小的采样
解决方案:
方法1:采用一个介于抖动和随机的函数来进行采样会得到更好的结果 方法2:采用contact hardening shadows(CHS,接触硬阴影),并且这个方法还解决了soft shadow 受shadow map分辨率的影响。首先生成shadow map 的mipmap,根据用户定义的世界空间大小选择合适的mip等级,求出Wsample后,在高分辨率的mip等级处查询硬阴影,在低分辨率mip等级查询软阴影。
CHS还能够加速需要多样本的单像素算法,这种应用常见的有hierarchical min/max shadow map(层次化的最小/最大阴影映射)。虽然阴影贴图深度通常不能求平均值,但是每个mipmap级别的最小值和最大值都是有用的。也就是说,可以形成两个mipmaps,一个保存每个区域中发现的最大z深度(有时称为HiZ),另一个保存最小的。给定一个texel的位置,深度和面积取样,mipmaps可以用来迅速确定完全明亮和完全阴影的条件。例如,如果texel的z-depth大于mipmap对应区域存储的最大z-depth,那么texel必须处于阴影中ー不需要进一步的样本。这种类型的阴影图使确定光能见度的任务更有效率。 --------------------------------------------------博主:mx
可以进一步改进的地方:
在PCSS的第一步和第三步中,都有对区域内进行比较并计算平均(滤波/卷积)的步骤,如果准确的范围采样,计算量其实有点大,如果稀疏的采样,则必然会有误差或噪声,此时需要执行图像空间降噪来处理。为了解决PCSS的这个问题,提出了VSSM(variance soft shadow map,VSSM)方法,针对性解决PCSS的第一步和第三步速度慢的问题。
Variance soft shadow map(方差软阴影):
基本原理:
该算法将深度存储在一张map中,深度平方存储在另一张map中。在生成map时,可以使用MSAA或其他反走样方案。这些地图可以模糊化、mipmap、放入求和区域表或任何其他方法。 首先,对于给定的receiver采样点,对深度图进行仅有一次的采样以返回遮挡物的平均深度,这个遮挡物深度为m1,称为第一时刻,大于阴影receiver t上的深度时,receiver完全处于光线中。当平均深度小于receiver的深度时,使用以下公式: 其中pmax是光照下样本的最大百分比,σ^2是方差,t是receiver深度,M1是阴影图中的平均预期深度。深度平方阴影图的样本M2,称为第二阶矩,用于计算方差:
pmax值是接收方可见性百分比的上限。实际照明百分比p不能大于该值。这个上界来自于切比雪夫不等式的单侧变量。该方程试图用概率论估计,在表面位置有多少遮挡物的分布超出了表面与光线的距离。Donnelly和Lauritzen表明,对于平面遮挡器和平面接收机在固定深度下,p=pmax, 公式可以用作许多真实阴影情况的一个很好的近似。
重要特征:
方差阴影映射的一个重要特征是,它可以以优雅的方式处理几何引起的表面偏差问题。Lauitzen[988]给出了如何使用表面的斜率来修改第二阶矩的推导。来自数值稳定性的偏差和其他问题可能是方差映射的一个问题。例如,从另一个相似的值中减去一个大的值。这种类型的计算倾向于放大底层数值表示准确性的不足。使用浮点纹理可以帮助避免这个问题。
PCF需要更多的样本,因此需要更多的时间,以避免产生噪声时,产生更柔和的阴影,而VSM可以只使用一个单一的,高质量的样本,以确定整个区域的效果,并产生一个平滑的半影。这种能力意味着在算法的限制下,阴影可以在没有额外成本的情况下被做成任意软的。
存在问题与解决方案:
问题:
当两个或更多的blocker覆盖一个receiver,并且一个blocker靠近receiver时,一个位置方差阴影映射沿着半影区域被打破。从概率论的切比雪夫不等式将产生一个与正确的光百分比无关的最大光值。最近的blocker,仅仅部分地遮挡光线,就打破了方程的近似。这导致光漏,完全闭塞的区域仍能接收光。
解决方案:
通过在更小的区域取更多的样本,这个问题可以得到解决,将方差阴影映射转化为一种PCF形式。与PCF一样,速度和性能是有权衡的,但对于阴影深度复杂度较低的场景,方差映射可以很好地工作。Lauritzen给出了一种由艺术家控制的方法来改善这个问题,即将低百分比视为完全阴影,并将其余百分比范围重新映射为0%至100%。这种方法使光渗变暗(缓解了问题),以缩小半影为代价。虽然轻微的光渗是一个严重的限制,但VSM很适合从地形产生阴影,因为这种阴影很少涉及多个blocker。
那又要怎么解决光渗问题呢?
convolution shadow map:
该思想将阴影深度编码为傅里叶展开。卷积阴影映射的一个缺点是需要计算和访问几个项,这大大增加了复杂度和存储成本。
exponential shadow map (ESM) or exponential variance shadow map (EVSM):
将深度的指数和它的二阶矩保存到两个缓冲区中。这可以显著减少出血的影响。它避免了 convolution shadow map的另一个问题,称为ringing,指轻微的光泄漏可能发生在刚刚超过原始遮挡器的深度的特定深度。并且存储数值的一个限制是,第二个矩值可能变得非常大,因此使用浮点数会超出范围。为了提高精度,并允许指数函数下降更陡,可以生成z-depth,使其为线性。由于它在VSM上的质量得到了改善,与卷积图相比,它的存储空间又更低,性能更好。
moment shadow map:
它提供了更好的质量,但以使用四个或更多矩为代价,增加了存储成本。这个开销可以通过使用16位整数来存储矩来降低。 --------------------------------------------------博主:mx
Volumetric Shadow Techniques(体积阴影技术):
自阴影对于头发和云彩等物体的真实感渲染至关重要,这些物体要么很小,要么半透明。单一深度的阴影贴图对这些情况不起作用。Lokovic和Veach首先提出了深层阴影贴图的概念,其中每个阴影贴图texel存储了光线随深度变化的函数。这个函数通常近似于一系列不同深度的样本,每个样本具有一个不透明度值。简单来说就是:对于粒子,烟雾,头发等体渲染实现软阴影效果,需要per-pixel linked lists,然后Filter。
其他有趣的阴影技术:
光线追踪:
基本思想是足够简单的,尤其是阴影。射线从texel的位置射向光源。如果发现有任何物体阻挡了射线,texel就处于阴影中。
GPU的光栅化硬件:
使用GPU的栅格化硬件来查看场景,但不只是z深度。额外的信息存储在光的每个网格单元的遮挡器边缘。例如,想象一下在每个阴影贴图texel中存储一个重叠网格单元的三角形列表。这样的列表可以通过保守的栅格化生成,其中,如果三角形的任何部分与像素重叠,而不仅仅是像素的中心,那么三角形就会生成一个片段。此类方案的一个问题是,每个texel的数据量通常需要受到限制。这反过来又会导致在确定每个接收器位置的状态时不准确。给出了gpu的现代链表原理[19431。每个像素当然可以存储更多的数据。但是,除了物理内存限制之外。在每个texel的列表中存储可变数量的数据的一个问题是,GPU处理会变得非常低效,因为单个变形可能会有几个片段。
学习资料:
核心:《Real-Time Rendering》 Chapter 7 Shadows 1.https://blog.csdn.net/u013299585/article/details/72723502 2.https://www.zhihu.com/question/412479769/answer/1390102376 3.https://wiki.jikexueyuan.com/project/modern-opengl-tutorial/tutorial40.html 4.https://www.cnblogs.com/TracePlus/p/4095873.html 5.https://blog.csdn.net/zjull/article/details/11819923?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-4.no_search_link&spm=1001.2101.3001.4242.3 6.https://zhuanlan.zhihu.com/p/369710758 7.games202 8.http://www.manongjc.com/article/53122.html
--------------------------------------------------博主:mx
|