首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

级联阴影贴图不够了:在游戏引擎中应用快速的光线跟踪软阴影

级联阴影贴图不够了:在游戏引擎中应用快速的光线跟踪软阴影

关键词: 光线跟踪 , 阴影 , 级联阴影贴图
大约1年前,笔者在Gamasutra上发表了《游戏领域的光线跟踪实用技巧》一文,阐释了开发人员如何在PowerVR GR6500(即我们的光线可跟踪GPU)上应用一系列的混合渲染技术来实现一些令人印象深刻的效果。尽管光线跟踪的目标应用程序极为不同,但本文主要关注点在于阴影。光线跟踪不仅仅可以创造更多精确的且可摆脱阴影贴图伪影的阴影,其跟踪阴影的效率还成倍地提高了。它们只需GPU一半的周期或更多的是只需一半的内存传输便可生成更具可比性或质量更好的图像。
接下来,本文将带你走进一个高效实现软阴影技术的全过程。

传统栅格图像中的级联阴影贴图

首先,让我们回顾一下级联阴影贴图——即当下使用的为栅格图形生成阴影的最先进的技术。级联阴影贴图旨在基于视点的距离将视景体分为许多区域,并在每个区域呈现一个阴影。这为阴影贴图提供了可变的分辨率:距离相机较近的物体则可得到高分辨率,而距离较远的物体每单位面积分辨率则更低。

下述图中,我们可以看到场景中一些物体的示例。阴影贴图将逐一呈现,且每个贴图覆盖的场景将逐渐变大。由于所有的阴影贴图都具有相同的分辨率,因此阴影贴图像素的密度将随着我们逐渐远离视点而变得越来越低。

最后,在最终场景中使用相机的视角再度呈现物体时,我们将基于每个物体与视点的距离来筛选恰当的阴影贴图,并在阴影贴图之间进行插补,以确定最终的像素是亮或暗。



这样的复杂性只为一个目的:减少由于阴影贴图过粗导致分辨率低这类情况的发生。这是由于物体离视点太远,在场景中占有的空间小,因此所需的阴影细节少。尽管GPU周期和内存传输的成本很高,但这样处理却非常好。

光线跟踪阴影介绍

从根本上说,光线跟踪阴影是在屏幕空间上运作。因此,没必要将阴影贴图的分辨率与屏幕分辨率进行匹配,分辨率问题由此也不存在。

基本的光线跟踪阴影算法如下:对于可见表面的每一个点,我们将直接朝光源的方向发出射线。如果光线到达了光源,其表面便是明亮的,我们使用的便是传统的常规照明程序。如果光线在到达光源之前遇到了其它障碍物,那么,由于表面处在阴影中,光线将被忽略。这项技术生成的是硬阴影,正如我们在万里无云的晴天所见的影子一样。

然而,现实世界中的很多阴影都在更亮和更暗的区域之间渐进过渡——这种软边缘被称为半影。半影是由多个光物理相关的因素引起。尽管很多游戏将光源模拟为无量纲点源,而实际上,每个光源都有一个表面。表面区域可使阴影柔化。在半影区域内,部分光被遮挡物阻塞,但剩下的光则有一条清晰的线路。这就是为何看到的区域并不是完全明亮也不是完全处在阴影中。



下图展示了如何基于以下三个变量来计算半影的大小:光源的大小(R)、光源的距离(L)、遮挡物和阴影投射表面之间的距离(O)。将遮挡物逐渐靠近表面时,半影也在缩小。



基于这三种变量,我们可以得出一个简单的等式来计算半影的尺寸。



使用这个简单的关系,我们可以制定一个算法。在这个算法中,每像素仅使用一条光线来呈现精确的软阴影。可以从上述的硬阴影算法入手,当射线与物体相交时,在屏幕缓冲区记录下表面到物体的距离。



该算法可以扩展到半透明的表面。例如:穿过一个表面时,可以记录是否该表明是透明的。如果表面是透明的,可以选择继续让光线穿过表面(注意它在独立密度缓冲区的alpha值)。



光线跟踪阴影优势

相比级联阴影贴图或其它通用的技巧,该方法的优势在于:

·由于它是基于屏幕空间,因此不存在阴影贴图分辨率的问题
·由于抽样误差,因为没有条带、噪音或嗡嗡声效果
·由于直接几何投射光线,因此不存在偏置问题(有时称为Peter-Panning),阴影和投射物体之间也没有任何接触
以下展示了光线跟踪通过的缓冲区(我们上传的是原始的框架)。

第一、我们有光线跟踪密度缓冲区。这个场景中的很多物体都是不透明的,因此阴影密度值为1。然而,围栏区域包含了多个密度值在0和1之间的像素。




接下来便是到遮挡物缓冲区的距离。由于我们远离遮挡物,红色分量的值便增加了,这表明阴影像素和遮挡物之间的距离变大了。



最后,我们使用了一个滤波器来计算,使用这两个缓冲区的每个像素其阴影值是多少。

首先,我们计算出对像素产生影响的半影的大小,使用这个半影来选择一个模糊的内核半径,然后相应地将屏幕空间阴影密度缓冲区便模糊。对于在远处遮挡物缓冲区具有密度值的像素,计算半影非常简单。由于我们知道到遮挡物的距离,我们只需要将这个值从世界空间投射到屏幕空间,并使用投射的半影来选择模糊的内核半径。



若像素是亮的,则需要进一步加强工作。我们采用了十字搜索算法来寻找另一个可形成半影的光线像素。如果在X轴或Y轴上发现任何处在阴影中的像素(即,具有有效的距离值),则选择距离遮挡物最大的值,且使用其计算像素的半影区域。随后,调整已找到的像素的梳理,计算出半影侧边。



从这里开始,算法都是相同的:将世界空间的半影大小投射到屏幕空间,然后算出半影所覆盖的像素区域,最后进行模糊处理。如果没有发现阴影像素,这种情况则假设像素完全是亮的。

下图表示我们最后的滤波器内核。



用方框滤波器盒覆盖半影区域,以此取样并同时注意不连续表面。这样有助于得到深度抑制值,为计算深度抑制值,我们将使用本地差分来计算X轴和Y轴上当前像素和定值之间的delta关系。

有了计算结果,我们便知道在穿过屏幕空间时还需要后退多远。对内核抽样时,我们将基于离中心像素的距离,扩大深度阀值。

结论:级联阴影贴图vs.光线跟踪阴影

在上述示例中,我们已拒绝了所有标记为红色的样本,因为其相应的区域属于栅栏,而我们则专注对地上的一点进行采样。进行模糊处理后,由此生成的缓冲区则表示对贯穿屏幕阴影密度的准确估计。

这是上文发布的原始框架进行对比的最终截图。注意,图像质量的改善是由光线跟踪阴影在级联阴影贴图的基础上完成的。



返回列表