个人感觉网络上关于 Shadow Acne 的教程中,原理图画的或晦涩,或错误,对人的理解比较有误导性。于是参考了 Reddit 上的一个问答,重新画了一张原理图,希望能够解释清楚。如有错误请不吝赐教。

Shadow Acne 产生原理

渲染阴影的一般做法:从光源视角进行对场景先进行一次渲染,将深度值存储到一张纹理中,称为深度贴图。在渲染真正场景时,将每个顶点的坐标变换为光源观察空间坐标,得到顶点所在位置的深度值,与深度贴图中对应位置深度值相比较,若比深度贴图中大,则在阴影中,否则被光照亮。

由于渲染深度贴图时,我们指定深度贴图的分辨率通常较小,这导致深度贴图中的一个像素会对应场景中的多个像素。

当光线与顶点所在平面成一个夹角射入时,会产生如下图所示的效果:

Shadow Acne

图中的 像素1 是深度贴图中的一个像素,它对应了场景中的 顶点范围 内的多个顶点。也就是说,图中 顶点范围 这个范围内的所有顶点在渲染时,都会将顶点坐标变换到光源观察空间,然后将深度值与 像素1 的深度值进行比较。

我们以 3 个顶点 A, B, C 为例。

  • 顶点 A 变换的得到的深度值小于 像素1 的深度值,因此被照亮。
  • 顶点 B 变换得到的深度值等于 像素1 的深度值,为临界点。(无所谓)
  • 顶点 C 变换的得到的深度值大于 像素1 的深度值,因此处在阴影中。

图中红色部分代表照亮,黑色部分代表处在阴影中。这样造成即使在一个平面上也会出现明暗相间大条纹,这种情况就称为 Shadow Acne,或者 erroneous self shadowing。

解决办法

最简单的解决办法是添加一个 bias:

Bias

我们将场景中的顶点标变换到光源观察空间后得到的深度值减去一个偏移,相当于把全部顶点的深度值都变小,这样就不会出现表面顶点深度值大于 像素1 深度值的情况了。

这个 bias 可以设为一个固定值,不过更精确的方法是根据光线与平面的夹角动态计算。