3D游戏渲染如何工作:顶点处理

图片

在这篇文章中,我们将考虑顶点的工作阶段。 也就是说,我们将不得不再次获得有关数学的教科书,并回顾线性代数,矩阵和三角学。 万岁!

我们将了解如何转换3D模型并考虑光源。 我们还将详细说明顶点着色器和几何着色器之间的区别,您将了解到在哪个阶段进行镶嵌。 为了便于理解,我们使用图表和代码示例来演示游戏如何执行计算和处理值。

帖子开头的屏幕截图以线框显示模式显示了GTA V游戏。 将其与不太复杂的Half-Life 2线框进行比较,图像由thalixte与ReShade共同创建。


有什么意义?


在数学世界中,点仅仅是几何空间中的一个位置。 没有什么比一个点还小,它没有大小,因此可以使用点来指定对象的起点和终点的精确位置,例如线段,平面和体积。

对于3D图形,此类信息至关重要,因为所有对象都显示为线段,平面等的集合,所以一切的外观都取决于它。 下图显示了Bethesda 2015 Fallout 4的屏幕截图:


您可能不容易看出这只是一大堆点和线,因此我们将向您展示在线框模式下相同场景的外观。 在此模式下,3D渲染引擎将跳过在像素阶段执行的纹理和效果,仅绘制连接点的多色线。


现在一切看起来都完全不同,但是我们看到了如何将所有线条组合在一起以形成各种对象,环境和背景。 有些仅由几十条线组成,例如前景中的石头,而另一些则包含许多线条,看起来很坚实。

每行开始和结束处的每个点都通过执行一堆计算来处理。 有些计算非常简单快捷,而另一些则更为复杂。 通过成组地处理点,尤其是三角形的点,可以显着提高生产率,因此让我们仔细研究一下它们。

三角形需要什么?


名称三角形清楚地表明该图形具有三个内部角; 为此,她需要三个角点和三个连接它们的线段。 将角点称为顶点(顶点)是正确的(复数形式为-顶点); 每个顶点由一个点定义。 由于我们处于三维几何世界中,因此将笛卡尔坐标系用于点。 通常,坐标以三个值的形式编写,例如(1,8,-3)或一般而言( x,y,z )。


接下来,我们可以再添加两个顶点以形成三角形:


请注意,显示的线是可选的-我们可以设置点并告诉系统这三个顶点形成一个三角形。 所有顶点数据存储在一个连续的内存块中,称为顶点缓冲区 ; 关于它们形成的图形的信息或者直接在渲染程序中编码,或者存储在另一个称为索引缓冲区的内存块中。

如果信息是在渲染程序中编码的,则可以由顶点形成的各种形状称为图元 。 Direct3D建议以点,线和三角形的形式为它们,条带和扇形使用列表。 如果使用正确,三角形的条纹会使用多个三角形的顶点,从而提高了生产率。 在下面的示例中,我们看到要创建连接在一起的两个三角形,只需要四个顶点-如果将它们分开,则需要六个顶点。


从左至右:点列表,线列表和三角形带

例如,如果我们需要处理更大的一组顶点,则在游戏NPC模型中,最好使用一个称为Mesh的对象,另一个内存块,但由多个缓冲区(顶点,索引等)和模型纹理资源组成。 Microsoft的在线文档简要说明了如何使用这些缓冲区。

现在,让我们集中讨论3D游戏中渲染每个新帧时这些顶点所发生的情况。 简而言之,它们执行以下两种操作之一:

  • 顶点移动到新位置。
  • 顶点颜色变化

准备好数学了吗? 优秀,因为我们需要它。

矢量出现在舞台上。


假设您在屏幕上有一个三角形,然后按一个键将其向左移动。 自然,我们希望每个顶点的数字( x,y,z )相应地发生变化; 就是这样,但这是实现更改的一种非常意外的方式 。 绝大多数3D图形渲染系统不是简单地更改坐标,而是使用一种特殊的数学工具:我们指的是vectors

向量可以表示为指向空间中特定点并具有所需长度的箭头。 通常使用基于笛卡尔坐标的向量来设置顶点:


请注意,蓝色箭头始于一个位置(在本例中为原点 ),并一直延伸到顶部。 为了设置向量,我们在列中使用了一条记录 ,但是在行中使用记录也是很有可能的。 您可能已经注意到,还有另一个第四值,通常称为w-component 。 它用于显示向量代表什么:点位置( 位置向量 )或大方向( 方向向量)。 在方向矢量的情况下,它将如下所示:


该向量指向相同的方向,并且与先前的位置向量具有相同的长度,即,值( x,y,z )相同; 但是, w分量不是1,而是零。 稍后我们将解释方向矢量的用法,但是现在,请记住一个事实,即将以这种方式描述3D场景中的所有顶点。 怎么了 因为以这种格式移动它们要容易得多。

数学,数学以及数学


回想一下,我们有一个简单的三角形,我们想将其向左移动。 每个顶点由一个位置矢量描述;因此,“运动数学”(称为“ 转换” )必须与这些矢量一起使用。 出现一个新工具: 矩阵矩阵为单数)。 这是一组以类似于Excel电子表格的格式编写的值数组,带有行和列。

对于每种类型的变换,都有一个对应的矩阵,对于一个变换,只需将变换矩阵与位置矢量相乘就足够了。 我们不会详细介绍这种情况的发生方式和原因,而只是看看它的外观。

在3D空间中移动顶点称为平移,它需要进行以下计算:


x 0 ,等等。 表示向量的原始坐标; delta - x值表示顶点需要移动的量。 矩阵和向量的相乘导致将它们简单地求和(请注意w分量保持不变,因此完成的答案仍然像以前一样是位置向量)。

除了移动之外,我们可能还需要旋转三角形或更改其比例-对于这些操作,还需要进行变换。


此变换使顶点绕XY平面中的z轴旋转


如果需要更改图形的比例,则使用此选项

我们可以使用实时渲染中基于WebGL的图形工具来可视化整个图形的这些计算。 让我们从处于标准位置的盒子开始:


在此在线工具中,模型点是位置矢量,世界矩阵是转换矩阵,世界空间点是转换顶点的位置矢量。

让我们将各种变换应用于框:


在上图中,该图沿每个轴移动了 5个单位。 这些值可以在中大型矩阵的最后一栏中看到。 原始位置矢量(4、5、3、1)保持应有的相同,但是现在将转换后的顶点移至(9、10、8、1)。


在此转换中,所有内容的缩放比例为2:现在,盒子的侧面已经变成两倍长。 最后,看一下轮换示例:



平行六面体旋转了45°的角度,但矩阵中使用该角度的正弦余弦 。 在使用科学计算器检查后,我们可以看到sin(45°) = 0.7071 ...,该值四舍五入为显示的值0.71。 对于余弦值,我们得到相同的答案。

矩阵和向量是可选的; 对于它们而言,一种流行的替代方法是使用复数和四元数 ,尤其是在处理复杂的转弯时。 这些计算与向量有很大不同,因此我们将不考虑它们,而是继续进行转换。

顶点着色器功率


在此阶段,我们需要了解所有这些工作都是由对渲染代码进行编程的人员完成的。 如果游戏开发人员使用第三方引擎(例如Unity或Unreal),则所有这些工作已经为他完成; 但是如果有人从头开始制作引擎,那么他将不得不使用顶点执行所有这些计算。

但是所有这些在代码方面如何看待?

为了理解这一点,我们将使用来自惊人的Braynzar Soft网站的示例。 如果您想自己开始进行3D编程,那么这里是学习基础知识以及更复杂内容的正确地方...


这是多合一转换的示例。 它根据键盘输入创建适当的变换矩阵,然后通过一次操作将其应用于原始位置矢量。 请注意,这总是按照给定的顺序(缩放-旋转-传输)完成,因为任何其他方式都会完全破坏结果。

此类代码块称为顶点着色器 ,其复杂性和大小可能相差很大。 上面的示例很简单,它只是一个顶点着色器,没有使用着色器的完全可编程特性。 更复杂的着色器序列可以在3D空间中变换对象,从场景摄影机的角度处理它们的外观,然后将数据传输到渲染过程的下一个阶段。 考虑到顶点处理的顺序,我们将研究其他示例。

当然,它们的用途还很多,因此在玩3D游戏时,请不要忘记您看到的所有动作都是由执行顶点着色器命令的GPU进行的。

但是,情况并非总是如此。 如果您回到1990年代中期,那么那个时代的图形卡就没有能力独立处理顶点和图元,仅中央处理器就可以完成所有这些工作。


2000年发布的Nvidia GeForce是最早具有此过程自身硬件加速功能的处理器之一 ,该功能称为硬件变换和照明 (简称为Hardware TnL)。 就团队而言,该设备可以处理的过程非常有限,但是随着新芯片的发布,情况迅速改变。 如今,没有单独的设备可以处理顶点,而一个设备可以一次完成所有操作:点,图元,像素,纹理等。

顺便说一下,关于照明 :值得一提的是,借助光,我们可以看到一切,因此,让我们看看如何在顶点阶段对其进行处理。 为此,我们需要利用我们先前讨论的内容。

灯光,相机,马达!


想象一下这张图片:玩家正站在一个黑暗的房间里,被一个右侧的光源照亮。 在房间中间是一个巨大的水壶。 您可能需要与此相关的帮助,因此让我们使用实时渲染网站,看看它的外观:


不要忘了这个对象是一组连接在一起的平面三角形。 也就是说,每个三角形的平面将指向某个方向。 它们中的一些对准照相机,另一些-对准另一些,会失真。 来自光源的光落在每个平面上,并以一定角度从该平面反射。

根据反射光的位置,平面的颜色和亮度可能会发生变化,并且为了使对象的颜色看起来正确,所有这些都需要计算并考虑在内。

首先,我们需要找出每个平面的方向,为此,我们需要平面的法向矢量 。 这是另一个箭头,但是与位置向量不同,它的大小并不重要(实际上,在计算法向向量的比例后,总会减小以使其长度为1),并且它始终垂直于平面(成直角)。


通过确定形成三角形边的两个方向向量(在pq上方显示)的向量积,可以计算出每个三角形平面的法线。 实际上,最好是为每个顶点而不是三角形计算它们,但是由于前者总是比后者更多,因此计算三角形的法线会更快。

接收到表面的法线后,就可以开始考虑光源和摄像机了。 在3D渲染中,光源可以是不同的类型,但是在本文中,我们将仅考虑定向光源,例如聚光灯。 像三角形的平面一样,聚光灯和照相机将指向某个方向,如下所示:


光源矢量和法线矢量可用于计算光入射到表面的角度(使用矢量的标量乘积与其大小的乘积之间的关系)。 三角形的顶点将包含有关其颜色和材质的其他信息。 材料描述了当光击中表面时会发生什么。

光滑的金属表面将以其落入的角度反射几乎所有入射光,几乎不会改变物体的颜色。 粗糙的哑光材料以较难预测的方式散射光,并且颜色略有变化。 要考虑到这一点,您需要向顶点添加其他值:

  • 原始底色
  • 环境材料属性-一个值,该值确定“背景”照明可以吸收和反射顶点的数量
  • 漫反射材质的属性是另一个值,但这一次确定顶点的“粗糙度”,进而影响散射光的吸收和反射量
  • 高光材质属性-指定顶点光泽的两个值

不同的照明模型使用不同的数学公式对所有这些属性进行分组,并且计算结果是输出的照明矢量。 结合相机矢量,可以确定三角形的整体外观。


一个定向光源照亮了许多不同的Nvidia演示

我们省略了许多详细信息,这是有充分理由的:打开任何3D渲染教程,您将看到整章都专门介绍了此过程。 但是,在现代游戏中,所有照明和材质效果的计算大部分都是在像素处理阶段执行的,因此我们将在下一篇文章中再次进行介绍。


示例代码 B. Anguelov显示了如何在顶点着色器中处理Phong光反射模型

上面我们检查的所有事情都是由顶点着色器完成的,似乎它们对它来说是不可能的。 不幸的是事实并非如此。 顶点着色器无法创建新的顶点,并且每个着色器必须处理每个单独的顶点。 如果您可以使用该代码在我们已经拥有的三角形之间创建新的三角形(以提高视觉质量),并使用可以处理整个图元的着色器(以加快处理速度),将非常方便。 好吧,在现代GPU中,我们可以做到!

先生,我想要更多(三角形)


现代图形芯片功能非常强大,能够每秒执行数百万个矩阵矢量计算。 他们轻松地一次应对一大堆山峰。 另一方面,创建用于渲染的高度详细的模型是一个很长的过程,如果模型与场景相距一定距离,那么所有这些细节都将被浪费。

也就是说,我们需要以某种方式命令处理器将一个较大的图元(例如,一个平面三角形)拆分为位于原始三角形内部的一组较小的三角形。 这样的过程称为镶嵌效果,图形芯片已经学会了很好地执行它。 在多年的发展中,程序员对该过程的控制程度有所提高。

为了实际观察这一点,我们将使用Unigine引擎Heaven基准测试工具 ,因为它允许我们将不同的细分值应用于测试中使用的模型。


首先,让我们在基准测试中占有一席之地,并在不使用细分的情况下对其进行研究。请注意,地面上的鹅卵石看起来非常不自然-所使用的纹理有效,但似乎错了。让我们将细分应用于场景:Unigine引擎仅将细分应用于各个部分,但差异会很大。


土地,建筑物的边缘和门看起来更加真实。我们可以看到通过再次启动该过程是如何实现的,但是这次选择了所有原语(即在线框模式下):


可以清楚地看到为什么地球看起来如此奇怪-它是完全平坦的!门与墙合并,建筑物的边缘是简单的平行六面体。

在Direct3D中,可以通过执行三步过程将基元分为一组较小的部分(此过程称为细分)。首先,程序员编写一个表面着色器(hull shader) -实际上,此代码创建了一个称为geometry patch的结构。您可以将其视为告诉处理器新点和线将出现在初始图元中的地图。

然后,GPU内的镶嵌细分将此补丁应用于图元。最后,执行域着色器计算所有新顶点的位置。如有必要,可以将这些数据传输回顶点缓冲区,以便可以重新执行照明计算,但是这次结果更好。

看起来像什么?让我们启动镶嵌场景的线框版本:


坦率地说,我们设置了较高的细分水平,以使对过程的解释更加明显。不管现代图形芯片的质量如何,都不应在每个场景中都做到这一点-例如,看门旁的灯。

在禁用线框的图像中,您很难在此距离找到差异,并且我们看到这种细分水平添加了太多三角形,以至于很难将它们彼此分开。但是,正确使用此顶点处理功能可以创建出色的视觉效果,尤其是在模拟软体碰撞时。

让我们看看它在Direct3D代码方面的外观;为此,我们使用另一个很棒的网站RasterTek的示例

这是一个细分为许多小三角形的简单绿色三角形...


顶点处理由三个单独的着色器执行(请参见代码示例):准备用于镶嵌的三角形的顶点着色器,生成面片的表面着色器和处理新顶点的域着色器。这个结果是可以理解的,但是Unigine的例子证明了细分方法广泛使用的潜在好处和危险。

“铁”受不了了!


还记得我们说过的顶点着色器总是处理场景中的每个顶点吗?不难理解,镶嵌细分在这里可能是一个严重的问题。在许多视觉效果中,您需要处理一个原语的不同版本,但无需一开始就创建它们,例如,头发,毛皮,草和爆炸粒子。

幸运的是,特别是对于此类事物,还有另一个着色器- 几何着色器。这是顶点着色器的一个更受限的版本,但是它可以应用于整个图元。与曲面细分结合使用时,它使程序员可以更好地控制大组顶点。




与所有现代图形API一样,UL Benchmark的3DMark Vantage-几何着色器可处理粒子和 Direct3D 标志,它使您可以对顶点执行大量计算。可以将完成的数据传输到渲染过程的下一个阶段(光栅化),也可以将其返回到内存池以进行重新处理或由中央处理器读取以用于其他目的。正如Microsoft关于Direct3D的文档所述,可以将其实现为数据流:


步骤输出流(流输出)可选的,特别是因为它可以返回到渲染图元整个周期只(而不是单独的顶点),但它是对于需要大量粒子的影响是有用的。使用可变或动态顶点缓冲区可以完成相同的操作,但是最好保持输入缓冲区不变,因为打开它们进行编辑时,性能会降低。

顶点处理是渲染的关键部分,因为它可以从相机的角度确定场景的外观。在现代游戏中,数百万个三角形可用于构建世界,并且每个顶点都以某种方式进行了变形和照明。


三角形。有数百万。

处理所有这些计算和数学似乎是后勤上的噩梦,但是在设计图形处理器(GPU)和API时要牢记所有这些-假设一个运作良好的工厂一次将一个产品通过生产管道。

经验丰富的3D游戏渲染程序员具有数学和物理方面的基础知识;他们使用所有可能的技巧和工具来优化操作,将顶点处理阶段压缩到仅几毫秒。但这仅仅是构建3D帧的开始-下一步是光栅化,然后是极其复杂的像素和纹理处理,然后图像才能进入监视器。

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


All Articles