PISCOnoob

导航

图形渲染管线浅述

图形渲染管线(graphics rendering pipeline)是实时渲染的核心部分,它代表了一整个流程而不是某单一部分,所以有时又会称之为“渲染流水线”。它的主要功能是,在给定的一些条件下(虚拟相机,光源,三维物体等)将场景渲染为一幅幅的二维图像并呈现在我们的显示器中。GPU(graphics processing unit)由数量众多的计算单元组成,每个计算单元只负责简单的运算和逻辑控制,如果和CPU比较的话,CPU就像是一个知识渊博的老师,而GPU则是由一群小学生组成,但在解决一百到一千道简单的数学题的情况下,明显是后者更适合更有优势,而我们后文将要讲述的渲染图像就属于这么一种情况。不同的图形API,书籍可能会依据不同的实现和理解将这一条渲染流水线划分为不同的阶段,但普遍来说,都会划分为应用、几何、光栅化这三个阶段。

应用阶段(The Application Stage)

我们硬盘中的模型是怎么一步步经过渲染流水线从3D场景变为2D图像的呢?我们从这里开始讲起。我们准备好场景数据(模型顶点坐标、法向量、纹理坐标等),设置好渲染状态(纹理、材质等),从CPU传送给GPU调用draw call命令。这个阶段主要由CPU负责,开发人员对此阶段有完全的控制权。因此,开发人员可以决定这个阶段的实现,对场景数据进行进一步处理,为后续阶段的优化提供帮助(加速算法,剔除算法等)。比如,我们可以设计一些算法来减少后续需要渲染的三角形面片数量。又比如,只传递给GPU可以被摄像机看得见的物体,而其他不可见的物体则被剔除(culling)。此外,我们通常还会在这个阶段进行物理碰撞检测,并把物体碰撞后的结果反馈回物体,还会在这个阶段接收键盘,鼠标等的输入。

几何阶段(Geometry Processing)

渲染流水线的一大特征就是上一阶段的输出会作为这一阶段的输入,而这一阶段的输出则作为下一阶段的输入,应用阶段的输出作为几何阶段的输入。这一阶段主要负责逐顶点和逐三角形的操作,也可以进一步划分为顶点着色,投影,裁剪和屏幕映射几个阶段。

 

 顶点着色阶段(vertex shading)的基本工作是计算不同空间坐标系中顶点的位置,还有计算如法线,纹理坐标等数据作为顶点数据输出。我们在DCC工具(3DX,MAYA等)中制作的模型会位于自己的模型空间(object space)中,每个模型空间都是相互独立,我们需要把它们都转到统一的空间坐标系中方便后续的运算,比如,光照计算一般就会在世界空间中统一计算。首先我们把模型从object space转到world space中(世界空间),由此一来所有的模型都以世界空间中的原点作为参照。

 

 在游戏中实际上我们是通过虚拟相机来观察游戏世界,就跟现实生活中一样,通过我们的眼睛来观察现实世界。我们可以说某辆车在世界上某经度某纬度的地方,但也同样可以说,它就在我眼前多少米的地方,后者这种描述方式在某些情况下是更为方便的。在游戏场景中我们会从虚拟相机出发来渲染所能看到的场景,屏幕的内容随着虚拟相机位置和角度的变化而变化,这时候,其实是把物体顶点坐标从世界空间转换到了观察空间(view space)又称视空间。在观察空间中,相机作为坐标系原点,观察方向的正方向作为-z轴,正上方作为+y轴,正右边作为+x轴(当然也有的情况喜欢把观察正方向作为+z轴)。此外,还会由视点,视角,视线方向,远近平面组成一个视锥(view frustum),然后这样一个切去塔尖部分金字塔形状的视锥将会被”挤压“成一个单位立方体(canonical view volume),边界点是(-1,-1,-1)和(1,1,1)。其内的物体将会被投影(Projection)到近平面中,即从三维变成了二维。其中由分为正交(orthographic)透视(perspective)两种方式,简单来说透视投影更符合我们现实的视角有近大远小的效果,而正交投影则没有。

 

 

 

 

所有的渲染管线都有上面提到的vertex shader(必须的),在继续下一阶段的fragment shader(必须的)之前,我们还有一些可自由选择的阶段:

Tessellation曲面细分是将一个三角形拆分成多个三角形的技术,它可以实现比如LOD等技术,使得当镜头靠近时用细分后的”高模“渲染而镜头远离时就用低模,从而达到节省内存,降低运算量等目的。但注意只有在某些较新版本图像API和硬件上才能使用。

 

 

Geometry Shader会调用单个图元作为输入,并可能会输出多个,也可能不输出图元。在应用中的表现可以为,输入顶点,然后对这些顶点做处理,可能会删除一些顶点,也可能会生成一些顶点,移动一些顶点。比如,特效粒子的生成(模拟烟火),实现大面积的草地树林效果等。

紧接而来的步骤就是视锥裁剪(clipping)了,在显示世界中事物是客观存在的,无论我们人眼看不看向它们都会存在,但在游戏渲染中可不一样,如果无时无刻都在渲染整个游戏场景无疑是对硬件巨大的压力和一种资源的浪费。因此我们只会渲染完全视锥内或者是物体处于视锥内的部分,视锥外的物体就会剔除掉不进行渲染以优化渲染效率。

 

 只有在视锥内被裁剪过的图元(primitive)才会被送到屏幕映射(screen mapping)这一阶段,虽然上面说到把模型从三维投影到二维,但实际上传入此阶段的顶点坐标仍然是三维的。现在我们已经把要渲染的场景”挤压“在了一个单位立方体中(unit cube),尽管GPU已经得到了顶点的x、y坐标,但他们处于[-1,1]区间中的,GPU还需要进行一定的计算才能把他们映射到我们的1920*1080,2560*1440等不同分辨率的屏幕。得到的新坐标系称为窗口坐标系,虽然只需要两个坐标把顶点投射到屏幕上,但记住它仍然是三维的,这个多出来的z值就是在上面算出来的深度,这个深度值会传到下一阶段光栅化中。

 

 

以上提到的object space→world space,view space,projection,其实都是通过矩阵来实现的,要想更好去理解这里转换的过程的话,我们需要去学习一下线性代数的知识。

光栅化阶段(Rasteriztion)

至此我们得到了一些经过处理的顶点,但是他们还不是像素,不能被显示在屏幕上。

图元组装(Primitive Assembly)

有的资料也会称此阶段为三角形设置(Triangle Setup)。简单来说此阶段的工作就是根据上一阶段传入的顶点数据(不光是位置,还有索引等)把点做连起来变成三角形或线,甚至还会有但一个点的情况。

三角形遍历(Triangle Traversal)

此阶段会检查屏幕中的哪些像素会被三角形所覆盖,然后生成”片元“,片元内的各种数据是由三角形的顶点做插值产生的,比如颜色,法线,深度等。因此,片元并不等于像素,而是包含了很多数据的集合,并利用这些数据最终计算出了像素的颜色。此阶段会产生锯齿问题,而各种抗锯齿技术也就随之被提出来缓解图像锯齿问题。

 

 

片元着色器(Fragment Shader)在DirectX(图形API的一种)上也被叫做像素着色器(Pixel Shader),这个阶段是完全可编程的,我们可以在这个阶段进行各种着色计算(比如光照),别忘了,我们还有上一阶段传进来的各种数据。在此阶段可以使用非常多的技术,比如采样贴图(Texturing),能把贴图纹理”贴“在模型上。

逐片元操作(Per-Fragment Operation)

在DirectX中,此阶段也被称为合并阶段(output merger)。此阶段的工作主要是对每个片元进行操作,将片元的颜色以某种方式混合,得出最终像素的颜色。在混合之前,此阶段还会有各种测试(Test)。

透明度测试(Alpha Test),在透明度测试中,允许程序员对片元的透明度值进行检测,仅仅允许透明度值达到设置的阈值后才可以会绘制。如下图我为立方体每个面的四种不同颜色设置不同的Alpha值,假设边框是不透明物体Alpha值为1.0,其余四个面片为0.2, 0.4, 0.6, 0.8当我们把阈值从0.3, 0.5 ,0.7, 0.9这样向上调时,不通过测试的面片增加,最后呈现镂空效果。

 

 深度测试(Depth Test),非常重要的一个测试,将当前片元的深度值(还记得上一阶段传进来的三维坐标z分量吗?)跟深度缓冲区中的值做比较(大于等于小于由开发者自己决定),通过了测试才会被保留。简单来说该测试就是让我们决定物体的遮挡关系的,正常的效果就是前面的物体能挡住后面的物体。假设片元离相机越远深度越大的话,深度小的片元会挡住深度大的片元,即离相机近的物体挡住离相机远的物体(当然这是可配置更改的)。

 

 

还有模板测试(Stencil Test)裁剪测试(Scissor Test)等测试,感兴趣的同学可以自己进一步细究。

至此所有通过测试的片元颜色就会被送到颜色缓冲区,形成最终呈现在屏幕上的图像。GPU会使用双重缓冲(Double Buffering)的策略,即屏幕上显示前置缓冲(Front Buffer),而渲染好的颜色先被送入后置缓冲(Back Buffer),再替换前置缓冲,以此避免在屏幕上显示正在光栅化的图元。

 

最后再简单回顾一下整个渲染流水线过程:

(再次提醒,计算机图形学发展至今天有不同的硬件,不同的图形API,不同的实现,渲染管线并不是一模一样的,但已简单介绍了基本具有代表性的部分)

  • 应用阶段:粗粒度剔除,进行渲染设置,准备基本数据,输出到几何阶段
  • 几何阶段:顶点着色器,曲面细分着色器,几何着色器,顶点裁剪,屏幕映射
  • 光栅化阶段:三角形设置,三角形遍历,片段着色器
  • 逐片元操作:透明度测试,深度测试,模板测试,颜色混合
  • 图像后处理阶段

 

 

  • 绿色表示此阶段是可编程的。
  • 黄色表示此阶段是可配置的。
  • 浅蓝色表示此阶段是由GPU(硬件厂商)固定实现的。
  • 实线着色器表示此阶段是必须由开发者实现的。
  • 虚线着色器表示此阶段是可选择的。

 

参考资料:

  1. Unity Shader入门精要--冯乐乐
  2. GAMES101-现代计算机图形学入门--闫令琪
  3. Foundamentals of Computer Graphic,Fourth Edition

 

posted on 2022-04-08 13:57  PISCOnoob  阅读(131)  评论(0编辑  收藏  举报