3D游戏渲染的工作原理:栅格化和光线跟踪

图片

第1部分:顶点处理

在本文中,我们将仔细研究3D世界中所有顶点处理完后发生的情况。 我们将再次不得不摆脱数学教科书上的尘埃,适应截断金字塔的几何形状,并解决透视图的奥秘。 我们还将简要地介绍光线追踪,照明和材料的物理原理。

本文的主题是一个重要的渲染阶段,在该阶段中,点,线段和三角形的三维世界变成了由彩色块组成的二维网格。 通常,此过程似乎是不可见的,因为从3D到2D的转换是不可见的,这与上一篇文章中描述的过程相反,在该文章中 ,我们可以立即看到顶点着色器和细分的影响。 如果您还没有准备好,可以从我们的3D游戏渲染101文章开始。

准备两次测量


绝大多数读者在完全平坦的显示器或智能手机屏幕上阅读本网站; 但即使您拥有现代技术-弯曲的监视器,它所显示的图像也将由多色像素的平面网格组成。 但是,当您播放新的《马里奥召唤:死亡义务战场》时,图像看起来是三维的。 对象在场景中移动,变大或变小,靠近并远离相机。


以2014年发布的Bethesda的Fallout 4为例,我们可以轻松地看到峰的处理方式,从而产生深度和距离感。 这在线框模式下尤其明显(请参见上文)。

如果您在过去的二十年中进行任何3D游戏,几乎每个人都会执行相同的动作序列,以将3D顶点世界转换为2D像素阵列。 这种转换通常称为栅格化 ,但这只是整个过程中许多步骤之一。

我们需要分析不同的阶段,并研究其中使用的技术和计算方法。 作为参考,我们将使用Direct3D中使用的序列。 下图显示了世界每个顶点发生的情况:


Direct3D转换管道

第一篇文章中,我们了解了世界空间(World space)中正在发生的事情:这里,使用各种矩阵计算,对顶点进行了变换和着色。 我们将跳过下一步,因为在摄影机空间中,仅顶点会在移动后进行转换和调整,因此摄影机成为参考点。

以下步骤太过复杂,无法跳过,因为它们对于从3D过渡到2D是绝对必要的-如果正确实施,我们的大脑将注视着纯平的屏幕,但“看到”具有深度和比例的场景。 如果一切都做错了,图片会很奇怪!

一切都与视角有关


此序列的第一步是从摄像机的角度设置范围。 为此,您首先需要设置水平和垂直视场的角度-首先是在游戏中经常更改,因为人们开发的水平周边视觉效果要好于垂直方向。

我们可以通过一个人的视野查看图像来解决这个问题:


视场的两个角(视场,fov)定义了平截头锥体的形状-带有从相机发出的正方形底的3D金字塔。 第一个角设置垂直视野,第二个角设置水平视野; 我们用符号αβ表示它们。 实际上,我们看到的世界并非如此,但是从计算的角度来看,使用截断金字塔比尝试生成逼真的可见性要容易得多。


您还需要指定另外两个参数-近(或前)和远(后) 裁剪平面(裁剪平面)的位置 。 第一个切掉金字塔的顶部,但本质上决定了绘制所有东西的距离相机的位置有多近。 后者的功能相同,但是确定了将渲染图元的距离相机多远。

截断面附近的大小和位置非常重要,因为它变成了所谓的视口 。 实际上,这就是我们在监视器上看到的,即 渲染帧,并且在大多数图形API中,视口是从左上角绘制的。 在下图中,点(a1,b2)将是平面的原点:相对于平面测量平面的宽度和高度。


视口的纵横比不仅对于显示渲染的世界很重要,而且对于匹配监视器的纵横比也很重要。 多年来,标准是4:3(或1.3333 ...以十进制表示)。 但是,今天大多数人以16:9或21:9的宽高比播放,称为宽屏和超宽屏。

相机空间中每个顶点的坐标必须进行转换,以使它们都适合于近乎截断的平面,如下所示:


修剪金字塔侧面和顶部

使用另一个称为透视投影矩阵的矩阵执行转换。 在下面的示例中,为了执行转换,我们使用了范围的角度和截断平面的位置。 但是,您可以改用视口大小。


顶点位置向量乘以该矩阵,这为我们提供了一组新的变换坐标。


瞧! 现在,所有顶点的编写方式都将源世界显示为3D透视图,并且靠近前截断平面的图元看起来比靠近远截平面的图元更大。

尽管视口大小和视角相关,但它们可以单独处理。 换句话说,您可以设置截断金字塔,以便获得与视口不同大小和长宽比的近截断平面。 为此,在操作链中需要执行一个附加步骤,在该步骤中,必须再次转换近乎截断平面中的顶点以解决此差异。

但是,这可能导致可见透视的失真。 以Bethesda Skyrim 2011游戏为例我们可以看到在保持视口的长宽比不变的情况下更改可见性区域β的水平角度如何对场景产生巨大影响:


在第一张图像中,我们将β设置为75°,场景看起来完全正常。 现在尝试设置β = 120°:


立刻可以注意到两个差异-首先,现在我们在“视野”的侧面看到了更多; 其次,物体现在看起来更远(尤其是树木)。 但是,现在在水表面的视觉效果看起来是错误的,因为该过程不是为这样的可见区域设计的。

现在,让我们假设我们的角色有外星人的眼睛,并且将β = 180°!


这样的可见区域可创建几乎全景的场景,但是您必须为此付出代价,因为在边缘渲染的对象会发生严重的变形。 再次发生这种情况是因为游戏设计师没有预见到这种情况,也没有为这样的视角(标准值约为70°)创造游戏的资源和视觉效果。

在上面的图像中,照相机似乎已经移动了,但事实并非如此-唯一的更改是修改了截断金字塔,这又改变了近截断平面的尺寸。 在每个图像上,视口的宽高比保持不变,因此将缩放矩阵应用于顶点,以便所有内容都适合该顶点。

那你留下还是离开?


在投影阶段执行了转换之后,我们继续进行剪辑空间 。 尽管此操作是投影之后完成的,但如果我们提前执行操作,会更容易显示出结果:


在上图中,我们看到在橡皮鸭,蝙蝠之一和部分树木中,三角形位于截头金字塔的内部; 但是,另一只蝙蝠和最远的树在截断金字塔的范围之外。 尽管组成这些对象的顶点已经过处理,但我们不会在视口中看到它们。 这意味着它们被剪切了

沿金字塔截断(视锥修剪)时,截断金字塔之外的所有图元都将被完全删除,而位于边界处的图元将转换为新的图元。 截断不会大大提高性能,因为在此阶段之前,已经在顶点着色器等中处理了所有这些不可见的顶点。 如有必要,甚至可以完全跳过整个截断步骤,但是并非所有API都支持此功能(例如,标准OpenGL不允许其被跳过,但这可以使用API​​扩展来完成)。


值得注意的是,游戏中远方截断平面的位置并不总是等于绘制距离 ,因为绘制距离是由游戏引擎本身控制的。 引擎还对金字塔进行截断(视锥剔除) -运行代码确定对象是否将在截断金字塔内绘制,以及是否会影响可见对象; 如果答案为 ,则该对象不会传输到渲染。 这与截锥体修剪不同,因为它还会丢弃金字塔外部的图元,但它们已经通过了顶点处理阶段。 剔除时,根本不会对其进行处理,从而节省了大量资源。

我们已经完成了所有的转换和截断,似乎顶点终于准备好了渲染顺序中的下一步。 但是实际上并非如此,因为在顶点处理阶段以及从世界空间到截断空间的转换操作中执行的所有计算都必须在统一的坐标系中执行(即每个顶点具有4个组成部分,而不是3个组成部分) 。 但是,视口是完全二维的,也就是说,API希望顶点信息仅包含x,y的值(尽管保存了深度z的值)。

为了摆脱第四分量,执行透视除法 ,其中每个分量除以w的值。 此操作将xy限制为可能值[-1.1]的间隔,将z限制为[0.1]的间隔。 这些称为标准化设备坐标 (NDC)。

如果您想了解更多关于我们刚刚解释的内容的信息,并且喜欢数学,请阅读关于此主题Song Ho An的出色教程 。 现在,将这些顶点转换为像素!

我们掌握光栅化


与转换一样,我们将以Direct3D为例查看将视口变成像素网格的规则和过程。 该表类似于具有行和列的Excel电子表格,其中每个单元格包含不同的数据值(例如颜色,深度值,纹理坐标等)。 通常将此栅格称为栅格图像 ,其生成过程称为栅格化 。 在3D渲染文章101中,我们简化了此过程:


上面的图像给人的印象是,将原语简单地切成小块,但实际上有更多的操作。 第一步是确定图元是否面向相机-例如,在上面的带有截锥的图像中,组成灰兔子背面的图元将不可见。 因此,尽管它们存在于视口中,但不需要渲染它们。

通过查看下图,我们可以大致想象它的外观。 多维数据集进行了各种转换,以将3D模型放置在屏幕的2D空间中,并且从摄影机的角度来看,部分多维数据集面是不可见的。 如果我们假设所有表面都是不透明的,则可以忽略其中的某些图元。


从左到右:世界空间>相机空间>投影空间>屏幕空间

在Direct3D中,可以通过告诉系统呈现状态是什么来实现这一点,并且该指令将告知它必须移除( 切除 )向前或向后看的每个图元的侧面(或例如在线框模式下不要完全切除)。 。 但是她怎么知道哪一边向前还是向后? 当我们检查顶点处理数学模型时 ,我们发现三角形(或更确切地说,顶点)具有法线向量,告诉系统它在看哪个方向。 由于有了这些信息,您可以执行简单的检查,如果原语失败,则将其从渲染链中删除。

现在是时候应用像素网格了。 这又是一个意想不到的复杂过程,因为系统必须了解像素是否在基元内部-完全,部分或根本不存在。 为此,执行覆盖率测试过程。 下图显示了如何在Direct3D 11中对三角形进行栅格化:


该规则非常简单:如果像素的中心通过了检查,则该像素被视为位于三角形的内部,Microsoft将其称为“左上”规则 。 “顶部”是指检查水平线; 像素的中心应这条线上。 “左”是指非水平线,像素的中心应在该线的左侧。 还有其他与非本原语相关的规则,例如,简单的线段和点,并且在使用多重采样时 ,如果条件出现在规则中,则还会有其他规则。

如果仔细查看Microsoft文档,您会发现由像素创建的形状与原始图元不太相似。 这是因为像素太大,无法创建逼真的三角形-位图图像包含的原始对象的数据不足,这会导致称为锯齿的现象。

让我们看一下UL Benchmark 3DMark03的别名:


720 x 480像素光栅化

在第一个图像中,光栅图像的分辨率非常低-720 x 480像素。 在上级士兵的武器所投射的栏杆和阴影上可以清楚地看到锯齿。 将此与光栅化期间像素数量增加24倍的结果进行比较:


栅格化3840 x 2160像素

在这里,我们看到栏杆上的锯齿和阴影已完全消失。 似乎您应该始终使用较大的位图,但显示帧的监视器应支持网格大小。 考虑到所有这些像素都需要处理的事实,很明显性能会下降。

多重采样可以为您提供帮助。 这是在Direct3D中的工作方式:


而不是检查像素的中心是否符合栅格化规则,而是检查每个像素内的几个点(称为子像素样本或子样本),如果其中一些满足要求,则它们构成图形的一部分。 似乎没有任何好处,甚至混叠得到了增强,但是当使用多重采样时,有关哪些子采样被原语覆盖以及处理像素结果的信息存储在内存中的缓冲区中。

然后使用该缓冲区混合这些子样本和像素,以使图元的边缘较少被撕裂。 我们将在另一篇文章中更详细地介绍锯齿,但是到目前为止,这些信息足以让我们了解在使用栅格化过少的像素时多重采样可以做什么:


如您所见,不同形状的边缘处的混叠量已大大减少。 分辨率更高的栅格化无疑会更好,但是性能下降可能会提示您使用多重采样。

同样在栅格化期间,执行遮挡测试 。 这是必需的,因为视口将被彼此叠加的图元填充-例如,在上图中,在前景中构成士兵的前瞻性三角形与另一名士兵的相同三角形重叠。 除了检查图元是否覆盖像素外,还可以比较相对深度,如果一个表面在另一个表面之后,则必须将其从其余渲染过程中删除。

, , . 3D- GPU z- , . , GPU . , .


, . z-, , , z-. , .

— , , , . , , , . , , .

, 2D, 3D-. , , . - 3D- ; , .

, — 3D- 2D- . .

( )


, . , , , ; . , ( , , ) .

, , , ( (occlusion culling)) ( ). " ", .

, , . , .


: , ,

? — , , . , , , , .

, — , , . , , , . , . .

- …

: ray tracing!


, . ( , ..) . , . ray casting .

, , , , . , ; , , . (ray tracing) ( , , , ) .


从上图可以了解Whited算法的工作原理。对于帧中的每个像素,一束光束从相机发出并移动直到到达表面。在此示例中,表面是半透明的,因此光可以通过它反射和折射。在这两种情况下,都会产生传播的辅助射线,直到它们与表面碰撞为止。还会产生新的辅助光线,以说明光源的颜色及其所产生的阴影。

, . - , . . , , , , .

. , , — -, , , , ( ). — , , — , :


: Nvidia RTX

, (bounding volumes, BV) — , . .

, BV . , , , ..; , .., . , ( BV hierarchy BVH); BV:


BVH, , , , 3D-.

, Blender POV-ray ( photon tracing radiosity) :


: , ? : -, , . , 800 x 600 480 000 , . PC. , .

3D- . 3D rendering 101 , .

Wolfenstein 3D ray casting 1992 Battlefield V Metro Exodus , 2019 , ? ? , .


2018 Microsoft API Direct3D 12 DXR (DirectX Raytracing). , . , , , , Direct3D 12.


Microsoft谈论DXR的同一游戏开发者大会上,Electronic Arts谈论了其Pica Pica Project (使用DXR的3D引擎的实验)。 该公司表明可以使用光线跟踪,但不能渲染整个帧。 大部分工作使用传统的栅格化技术和计算着色器,而DXR用于特定区域。 也就是说,产生的光线数量远少于整个场景的光线数量。


过去已经使用了这种混合方法,尽管程度较小。 例如,Wolfenstein 3D 使用射线投射来渲染帧,但是它是通过每个像素列而不是像素一个光束执行的。 除非您记得游戏的分辨率为640 x 480 [ 转换:实际上是320 x 200],也就是说,同时发出的光线不超过640条。

2018年初的显卡如AMD Radeon RX 580或Nvidia GeForce 1080 Ti满足了DXR要求,但即使具有计算能力,也有人担心它们的功能不足以使DXR有意义。

Nvidia在2018年8月发布了代号为Turing的最新GPU架构后情况发生了变化。 该芯片的最重要特征是所谓的RT内核的出现:独立的逻辑块可加速射线三角相交的计算以及边界体积层次(BVH)的通过。 这两个过程是耗时的过程,用于确定光线与构成场景对象的三角形的交互点。 鉴于RT内核是唯一的Turing处理器单元,因此只能通过Nvidia专有API进行访问。

支持此功能的第一款游戏是EA的《战地风云5》。 当我们在其中测试DXR时 ,我们对水,草和金属上反射的改善以及相应的性能下降印象深刻:


老实说,随后的补丁改善了这种情况,但是渲染帧的速度仍然有所下降(并且仍然是)。 到2019年,还会有其他一些游戏支持此API,并对框架的各个部分执行光线追踪。 面对相同的情况,我们测试了Metro ExodusTomb Raider的影子 -积极使用DXR会大大降低帧速率。

大约在同一时间,UL Benchmarks 宣布3DMark创建DXR功能测试:


DXR用于Nvidia Titan X(帕斯卡)图形卡-是的,结果是8 fps

然而,一项对具有DXR支持和3DMark测试的游戏的研究表明,即使在2019年,即使价格超过1000美元,光线追踪对于GPU仍然是一项非常困难的任务。 这是否意味着我们没有光栅化的真正替代方案?

消费者3D图形技术中的渐进功能通常非常昂贵,并且它们对新API功能的初始支持可能非常分散或缓慢(正如我们在2012年在不同版本的Direct3D上测试Max Payne 3时发现的那样)。 后一个问题通常是由于游戏开发人员试图将尽可能多的现代功能集成到他们的产品中而有时没有足够的经验而出现的。

但是,顶点和像素着色器,细分,HDR渲染和屏幕空间环境光遮蔽也曾经是昂贵的技术,仅适用于功能强大的GPU,现在它们已成为游戏的标准,并且支持许多图形卡。 光线追踪也会发生同样的事情。 随着时间的流逝,它将简单地变成另一个细节参数,大多数玩家默认启用该参数。

总结


因此,我们进入了分析的第二部分的结尾,在其中我们对3D图形世界进行了更深入的研究。 我们了解了世界和模型的顶部是如何从三个维度转移到平面2D图像的。 我们看到,我们需要考虑范围并意识到其影响。 我们检查了将这些Verine转换为像素的过程,最后简要介绍了传统栅格化过程的替代方法。

与上一篇文章一样,我们不太可能透露所有主题,而错过了一些细节-最后,这不是教科书! 但是我们希望您学到了一些新知识,并且现在尊重使用计算机和科学在您喜欢的3D游戏中实现所有这些功能的程序员和工程师的工作。

Source: https://habr.com/ru/post/zh-CN480338/


All Articles