games202实时渲染笔记

669 阅读12分钟

lecture 2

实时渲染的特点: 不做与真实或者离线完全一致的渲染,而是选择相似度比较高的方式来进行,以此来节约时间和达到实时的目的。

全局光照: 光照分直接光照,就是光线通过光源打到点上,然后再反射到人眼或者相机,这种叫直接光照;间接光照,就是光打到A点,B点接受来自A点的光,B点反射再打到相机或者人眼。

实时渲染里,实时全局光照中一般只做一次间接光照,而不是按照真实的光线传播那样一直递归和损耗,因为两者效果区别并不大,但是不做间接光照和做的区别很大。

这是不做间接光照的照片: image.png

这是只做一次间接光照的照片:

image.png

这是做两次的bounce的实时光照:

image.png

可以看到区别不大。

Lecture 3 shadow mapping

为什么做阴影?

因为阴影让物体更真实,而非有一种贴图感。

image.png

在渲染里,做阴影的思路是:先从光源出发,做一张深度图,记录它所能到达空间内的每个位置的最浅深度。再从摄像机出发,从摄像机所看到的点,计算这个点到光源的深度,与光源第一次记录这个位置的深度相对比后,如果发现其大于光源记录的深度,则说明,摄像机所照进去的这个点在阴影里,不然则说明这个点可以被光源看到。

image.png

shadow mapping 的问题有两个,一个是自遮挡,另一个是走样。

3.1shadow mapping 的自遮挡问题

当光源与所照射的物体倾斜角度越大的时候,一个像素更容易遮挡住另一个像素,用它的深度覆盖另一个像素的深度。

//这部分转载自知乎 zilch,因为他讲的这部分我个人感觉更好懂。

/* 作者:zilch
链接:zhuanlan.zhihu.com/p/370951892… 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 */

3.1.1 引起shadow acne的原因

  • ShadowMap的分辨率有限

  • 因此ShadowMap中的一个像素,对应到场景上是一片区域。

  • 在ShadowMap生成阶段(Shadow Caster),该区域内的点(假设都暴露在光照下)最终会汇集到ShadowMap上的同一个像素,对应一个深度值D,该深度值在光栅化阶段由插值得到。对于平面来说,深度值应当恰好等于区域的中心。

  • 在阴影计算阶段(Shadow Receiver),当我们将该区域内的点投影到ShadowMap中进行深度对比时,会发现其中一些点的深度大于D,而其中一些小于D,因此形成了亮暗条纹。

用一张图来解释:

图片来自learnOpenGL

箭头为入射的平行光,每一条斜黄线断代表ShadowMap贴图中的一个像素,对应到水平地面上可能覆盖多个像素。多个像素里有些深度较大,有些深度较小,因而产生了条状瑕疵。

我们可以使用一个Shadow Debug Pass,将ShadowMap的分辨率以红黑格子的形式,投影到场景中绘制出来如下:

ShadowMap分辨率投影效果

可以看得出来,自遮挡引起的阴影瑕疵条纹与shadowmap的分辨率格子大小是一致的。

凑近了观察一下

ShadowMap分辨率投影效果

红黑格子为shadowmap投下的分辨率(平行光以45度照射,因此格子呈现1:2的长方形)。黑白条纹会自遮挡引起的阴影瑕疵。我们可以看出,从左至右,一个分辨率格子内包含了黑白条纹各一条。

接下来我们会将以上的阴影自遮挡问题,转化为一个纯粹的几何问题,来用数学进行精确描述。这将解释:

  • 为什么瑕疵条纹与ShadowMap的分辨率会呈现以上的关系
  • 如何精确的计算Bias的数值来修正自遮挡问题。

几何模型

首先看下图:

此图为Shadow Map数学几何模型

  • 橙色线条为平行光视角的近平面,记为L,这个近平面最终将映射为一张ShadowMap深度图。
  • 蓝色线条为接受光照的平面。

我们暂且假设Light对应的正交矩阵其宽高相同(实际上URP中就是这样的),记为frustumSize。ShadowMap贴图的尺寸记为shadowMapSize。

设图中AB线段代表ShadowMap贴图上单位像素在Light近平面上对应的尺寸。那么我们有以下公式:

|AB| = frustumSize / shadowMapSize

从AB作橙线的垂线,相交场景蓝色平面于CD两点。易知,CD范围内的所有像素均投影到平面L上的AB区域。

由于AB代表了ShadowMap上的单个像素,因此AB像素中存储的深度值应当是CD中点F到平面L的距离,即|FE|(实际上是归一的)。

我们记:

Distance(X,L) - 表示任意点X到光源近平面L的距离。

那么显然:

  • 对任意点X属于CF,有Distance(X,L) < |FE|,因此判定为不在阴影中
  • 对任意点X属于DF,有Distance(X,L) > |FE|,因此判定为在阴影。

于是在CD区域中会呈现出一半白,一半黑的阴影瑕疵,这就是Shadow Acne,其以CD长度为周期在平面上循环交替,而|CD|正是ShadowMap中单个像素投射在场景上覆盖的区域。

作者:zilch
链接:zhuanlan.zhihu.com/p/370951892… 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
\

3.1.2 如何解决shadow ance

使用DepthBias

过F点做L的平行线p,如下图:

ShadowMap数学几何模型示意图

我们只要将CD中的点,往光照方向平移一定距离到GH上即可修正误差。这个移动的距离即称为Depth Bias。不妨先考察D点。 |DG|即是要修正的Depth Bias,于是我们有:

[公式]

那么对于CD上的任意点X,我们可以使用相似三角形的原理,从DepthBias(D)乘以对应的比例就可以了。

伪代码如下:

shadowUV = shadowProj(X);
percent = (frac(shadowUV) - 0.5) / 0.5;
DepthBias_X = DepthBias_D * percent;

实际上在大多数引擎的实现中,并不会这么精确的去计算每个点的bias数值,而是对所有的点执行一个固定的Depth Bias。很明显,D点的误差是最大的,因此只要对所有的点都使用D点的bias数值,就可以修正自遮挡的问题。

但是固定的depth bias也会引起其他的问题。

3.1.3 漏光问题

考虑点C前面有个遮挡物。

ShadowMap Bias 遮挡物情形示意图

本来C点应该是处于阴影中的。但是我们通过depth bias将C点往光源方向进行了长度为|DG|的偏移,那么C点就变到了遮挡物前面去了。这就是固定depth bias引起的漏光现象

3.1.4 bias趋向无穷大的问题

当入射光线与平面夹角趋于0,即 [公式] 趋于90度时,线段DG长度会趋于无穷大。从|DG|的公式也可以看出:

[公式]

[公式] 在角度趋于90度时,会变得无穷大。我们不能对像素应用过大的偏移,否则漏光现象会变得非常严重。因此固定尺度的depth bias在 [公式] 趋于90度时,将会趋于失效。

过大depth bias导致漏光现象

3.1.5 Normal Bias

为了解决Depth Bias的缺陷,于是人们提出了Normal Bias。顾名思义,既然往光源方向偏移有问题,那么我就往法线方向偏移呗。

Normal Bias几何示意图

将CD平面按照法线方向,平移到C'D',平移距离为G到CD的距离即|GM|。从图中可以看出,经过法线方向平移后:

  • C'G段的点,深度均小于|EF|,因此光亮
  • D'G段的点,移动到了隔壁的像素区间,易知也是小于隔壁像素深度的,同样呈现光亮。

这样同样可以解决shadow acne。通过简单的几何知识,我们可以写出关于Normal Bias的公式,即对任意点X属于CD有:

[公式]

于Depth Bias相比,其优点在于当 [公式] 趋于90度时,bias趋于{frustumSize/(2 * shadowMapSize)},而不是无穷大。也即,normal bias的最大值只与shadowMap的分辨率有关。

3.1.6 Normal Bias的漏光问题

Normal Bias同样会存在漏光问题,并且是两头漏光。考虑有个遮挡物如下:

Normal Bias遮挡物示意图

该遮挡本应该在CD区域投下阴影。但由于normal bias的存在,我们在进行深度判定的时候将CD移到了C'D',因此CD平面左侧会有少部分位于遮挡物上方,从而漏光。而右侧的GD'部分,也因为进入了隔壁的像素地盘,形成了漏光。

但在实际应用中,由于Normal Bias的最大值相对可控(只要提升ShadowMap分辨率即可),因此漏光问题并不太严重。

3.2 阴影的走样

image.png

解决办法有:

利用PCF(Percentage Closer Filtering)直接对阴影信号[公式] 进行滤波,就可以实现平滑阴影边缘。

工业界采用动态分辨率的方法。

3.3 硬阴影与软阴影

硬阴影是左上,阴影非常棱角分明,而软阴影如左下,更为贴近真实。软阴影有过度,一般是面光源,更自然,有一个半阴影的区域,从此看光源发现光源被部分遮挡。

image.png

PCF是做抗锯齿的,而PCSS是用来做软阴影的。

最早开始研究的是pcf,他们两者的原理都是,做滤波,也就是卷积。

image.png

此处转载知乎insulation的部分笔记,用来解释硬阴影与软阴影 作者:insulation
链接:zhuanlan.zhihu.com/p/359377010… 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

为了实现软阴影的效果,我们可以修改一下采样比较shadow map时的方式,不是直接地采样得到shadow map中对应点的深度与实际到光源深度进行比较,而是采用所谓的filter的方式,在shadow map中对对应点的周围的数个像素采样,将采样得到的深度与对应点实际到光源深度进行比较,从而得到或0或1的值,然后对这些值取平均,作为阴影绘制的依据。如图中我们绘制P点阴影时,可以假设在shadow map中以P点为中心取到3x3的区域。

image.png

以P点为中心在shadow map中采样

假设这是我们在shadow map中获取到的深度值,而P点得到的实际到光源深度为0.5,这时所有的在shadow map中获取到的深度值要与P点得到的实际到光源深度,即0.5进行比较,所有大于0.5的像素我们输出0,反之则输出1。

采样得到的结果

可以得到:

比较后的结果

接下来,将得到的值求平均,得到4/9=0.44,作为阴影输出,这样我们就得到了一个软阴影,而这就是PCF的核心。 这样的计算可以得到软阴影效果,而且锯齿也不再那么明显了,但是要得到一个看上去不错的软阴影效果,需要采样的区域很可能是16x16甚至于64x64的,这样对每个点进行计算,将是非常大的性能消耗,而且对于每个点都进行PCF,最后将得到一个“糊掉”的阴影,这都取决于我们进行filter的范围,在我们仔细观察下,一个阴影可以分为软+硬的两部分。

闫老师自称很喜欢的一张图

在这张图中,我们可以看到,这支笔的阴影在笔尖的部分比较符合“硬阴影”,而在较远处则更符合“软阴影”,注意观察钢笔到纸面的距离,不难发现阴影的接收物到阴影的投射物越远(也就是笔和笔的阴影),阴影就越“软”,反之则越“硬”。以此为原理,我们可以计算出我们要进行filter的范围,这就是PCSS。、

我们可以用下面这张图来说明,Wlight为我们模拟出的光源的“大小”(我们使用的点光源其实并不存在大小一说,这里是将其模拟为面光源来处理 ),Blocker为遮挡物,Receiver为我们接收光源的面,下面的W就是软阴影,也就是filter的范围。

我们很容易就可以发现一对相似三角形,根据这个相似三角形,可以得到如下图的等式

相似三角形在图中标出

由相似三角形推出的等式。计算得到的Wp 与filter的大小有关,如果越大,则filter也越大,卷积变大,阴影边缘变的模糊;越小,说明遮挡物与阴影越近,阴影越清晰。

在等式右侧这些值中,除了Blocker到光源的竖直距离,其他我们都可以轻松得到,这个距离如何界定呢?首先不能直接使用shadow map中对应单个点的深度,因为这样如果该点的深度与周围点的深度差距较大(遮挡物的表面陡峭或者对应点正好有一个孔洞),将会产生一个错误的效果。我们选择使用平均遮挡距离来代替,具体方法是在shadow map上采样该点周围取许多点来计算各自的遮挡距离后求平均。

                 立体示意图

这样就又涉及到了一个问题,采样的范围该如何界定?一种方法是采用固定的范围,例如4x4、16x16。另一种更好的方法是动态计算遮挡范围,我们计算shadow map的时候在光源处设置过相机,如图所示,我们把shadow map放在相机的近截面,然后将光源和要渲染的点相连,在shadow map上截出来的面就是要查询计算平均遮挡距离的部分,这部分的深度求一个均值,就是Blocker到光源的平均遮挡距离。