1基础-渲染流水线

渲染流水线的最终目的在于生成或者说是渲染一张二维纹理,即我们在电脑屏幕上看到的所有效果。它的输入是一个虚拟摄像机、一些光源、一些Shader以及纹理等。

要学会怎么使用Shader,我们首先要了解Shader是怎么工作的。实际上,Shader仅仅是渲染流水线中的一个环节,想要让我们的Shader发挥出它的作用,我们就需要知道它在渲染流水线中扮演了怎样的角色。而本节会给出简化后的渲染流水线的工作流程。

整体流程

什么是渲染流水线

渲染流水线的工作任务在于由一个三维场景出发、生成(或者说渲染)一张二维图像。换句话说,计算机需要从一系列的顶点数据、纹理等信息出发,把这些信息最终转换成一张人眼可以看到的图像。而这个工作通常是由CPU和GPU共同完成的。

《Render-Time Rendering, Third Edition》[1]一书中将一个渲染流程分成3个阶段:应用阶段(Application Stage)、几何阶段(Geometry Stage)、光栅化阶段(Rasterizer Stage)。

注意,这里仅仅是概念性阶段,每个阶段本身通常也是一个流水线系统,即包含了子流水线阶段。

各个阶段概括

·应用阶段

从名字我们可以看出,这个阶段是由我们的应用主导的,因此通常由CPU负责实现。换句话说,我们这些开发者具有这个阶段的绝对控制权。在这一阶段中,开发者有3个主要任务:首先,我们需要准备好场景数据,例如摄像机的位置、视锥体、场景中包含了哪些模型、使用了哪些光源等等;其次,为了提高渲染性能,我们往往需要做一个粗粒度剔除(culling)工作,以把那些不可见的物体剔除出去,这样就不需要再移交给几何阶段进行处理;最后,我们需要设置好每个模型的渲染状态。这些渲染状态包括但不限于它使用的材质(漫反射颜色、高光反射颜色)、使用的纹理、使用的Shader等。这一阶段最重要的输出是渲染所需的几何信息,即渲染图元(rendering primitives)。通俗来讲,渲染图元可以是点、线、三角面等。这些渲染图元将会被传递给下一个阶段——几何阶段。

概括:准备的是场景,你的对象的基本数据,比如说场景里面的物体,他们的位置朝向、大小以及物体对应的模型里面每一个顶点的位置、法线、切线等,再比如场景光源的位置朝向和一些基本属性,还有摄像机的位置朝向等等

应用阶段准备这些数据的作用:为几何阶段的计算做准备

·几何阶段

几何阶段用于处理所有和我们要绘制的几何相关的事情。例如,决定需要绘制的图元是什么,怎样绘制它们,在哪里绘制它们。这一阶段通常在GPU上进行。几何阶段负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作。这个阶段可以进一步分成更小的流水线阶段,这在下一章中会讲到。几何阶段的一个重要任务就是把顶点坐标变换到屏幕空间中,再交给光栅器进行处理。通过对输入的渲染图元进行多步处理后,这一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息,并传递给下一个阶段。

概括:几何阶段有个顶点着色器需要计算顶点光照,顶点光照需要知道光源的位置和朝向以及摄像机的位置和朝向,还有当前顶点的世界位置,当前顶点的世界位置又需要知道顶点在模型里的位置,以及模型本身的位置等等,几何阶段的曲面细分着色器,需要通过现有的顶点生成更多的顶点,也需要知道现有顶点在模型里的位置信息,再比如几何阶段的几何着色器,需要通过现有的图元做一些几何方面的操作,来生成更多的顶点和图元,比如对现有图元所在的平面生成法线同样需要知道现有图元的顶点位置,顶点裁剪干掉看不到的屏幕以外的顶点,屏幕映射将顶点从3D坐标空间转换为2D坐标空间

·光栅化阶段

这一阶段将会使用上个阶段传递的数据来产生屏幕上的像素,并渲染出最终的图像。这一阶段也是在GPU上运行。光栅化的任务主要是决定每个渲染图元中的哪些像素应该被绘制在屏幕上。它需要对上一个阶段得到的逐顶点数据(例如纹理坐标、顶点颜色等)进行插值,然后再进行逐像素处理。和上一个阶段类似,光栅化阶段也可以分成更小的流水线阶段。

概括:拿到映射到2D空间里的顶点位置,我们需要把它组装成三角形,这就是三角形设置,还要知道三角形包含了哪些2D空间的像素点,这就是三角形遍历,最后,我们需要对这些点使用它们包含的数据来着色,并且为后面的逐片元着色准备数据

·逐片元操作

概括:到了逐片元阶段,我们的操作对象就变成了光栅化操作输出的片元数据,片元可以理解为屏幕上的某一个像素点,对于这些片元我们需要进行一系列的测试,比如透明测试、深度测试和模板测试,通过测试的片元就保存起来,否则就丢弃掉,然后在2D屏幕坐标系统中,同一个位置上的像素点有可能对应于多个不同的片元,我们可能还需要把这些通过测试的片元的颜色进行一个混合操作,从而得到像素点最终输出的颜色。

·后处理    

概括:逐片元操作完成后,我们就得到一个类似于贴图的数据保存在内存里,然后我们就可以对这个数据做一个后处理,可以理解为图像处理,比如模糊、景深、高光等

各个阶段详细过程


1 应用阶段

应用阶段是在cpu中完成,比如从磁盘或者内存中读取贴图或者模型数据,然后加载到应用程序中

准备场景基本数据:比如说场景里面有哪些需要渲染的物体,模型、房屋、山川、河流等,然后是摄像机的数据,代表的是观察视角,然后是光源以及一些全局性数据,准备好这些需要渲染的场景对象之后,我们下一步还要做一些优化

加速算法、粗粒度剔除:算法加速、干掉看不到的不需要渲染物体、做一些剔除

设置渲染状态、准备渲染参数:对于前面渲染好的数据,我们用什么方式去渲染,比如对于场景物体,我们是由远到近渲染,还是先渲染不透明的,再渲染半透明的,这个就是绘制顺序 ,再比如对于不同的物体我们可能采用不同的shader去渲染,这个可能涉及到一些绘制的设置,以及渲染完成后我们需要把渲染得到的结果输出到哪里,是renderTexture还是FrameBuffer?这就是渲染目标,还有一些渲染模式,比如unity常见的成像渲染或者延迟渲染等等

调用DrawCall:设置好渲染状态和参数,我们就调用DrawCall,把带有渲染数据的图元输出到显存去交给GPU处理

渲染流水线的起点是CPU,即应用阶段。应用阶段大致可分为下面3个阶段:(1)把数据加载到显存中。(2)设置渲染状态。(3)调用Draw Call(在本章的最后我们还会继续讨论它)。

把数据加载到显存中:

所有渲染所需的数据都需要从硬盘(Hard Disk Drive, HDD)中加载到系统内存(Random Access Memory, RAM)中。然后,网格和纹理等数据又被加载到显卡上的存储空间——显存(Video Random Access Memory, VRAM)中。这是因为,显卡对于显存的访问速度更快,而且大多数显卡对于RAM没有直接的访问权利。下图所示给出了这样一个例子。渲染所需的数据(两张纹理以及3个网格)从硬盘最终加载到显存中。在渲染时,GPU可以快速访问这些数据需要注意的是,真实渲染中需要加载到显存中的数据往往比图中所示复杂许多。例如,顶点的位置信息、法线方向、顶点颜色、纹理坐标等。当把数据加载到显存中后,RAM中的数据就可以移除了。但对于一些数据来说,CPU仍然需要访问它们(例如,我们希望CPU可以访问网格数据来进行碰撞检测),那么我们可能就不希望这些数据被移除,因为从硬盘加载到RAM的过程是十分耗时的。在这之后,开发者还需要通过CPU来设置渲染状态,从而“指导”GPU如何进行渲染工作。

什么是渲染状态呢?一个通俗的解释就是,这些状态定义了场景中的网格是怎样被渲染的。例如,使用哪个顶点着色器(Vertex Shader)/片元着色器(FragmentShader)、光源属性、材质等。如果我们没有更改渲染状态,那么所有的网格都将使用同一种渲染状态。图下显示了当使用同一种渲染状态时,渲染3个不同网格的结果。图下 在同一状态下渲染3个网格。由于没有更改渲染状态,因此3个网格的外观看起来像是同一种材质的物体。

draw call

在准备好上述所有工作后,CPU就需要调用一个渲染命令来告诉GPU:“嘿!老兄,我都帮你把数据准备好啦,你可以按照我的设置来开始渲染啦!”而这个渲染命令就是Draw Call。 调用Draw Call相信接触过渲染优化的读者应该都听说过Draw Call。实际上,Draw Call就是一个命令,它的发起方是CPU,接收方是GPU。这个命令仅仅会指向一个需要被渲染的图元(primitives)列表,而不会再包含任何材质信息——这是因为我们已经在上一个阶段中完成了!图下形象化地阐释了这个过程。 CPU通过调用Draw Call来告诉GPU开始迚行一个渲染过程。一个DrawCall会指向本次调用需要渲染的图元列表,当给定了一个Draw Call时,GPU就会根据渲染状态(例如材质、纹理、着色器等)和所有输入的顶点数据来进行计算,最终输出成屏幕上显示的那些漂亮的像素。而这个计算过程,就是我们下一节要讲的GPU流水线。



1.1 应用阶段各个子阶段分析


2 几何阶段


各个子阶段概括

顶点着色:可编程的、对应的就是顶点着色器

可选顶点处理:顶点着色器输出的数据会交给曲面细分和几何着色器,这两步是可选的,既可以做也可以不做

投影:这一步是GPU自动完成的,分为正交和透视两种,前面这三步就完成了顶点空间到投影空间的过程

裁剪:CVV:就是说这个顶点是否在视椎体内不再剔除,正面或者背面剔除:可配置,在代码中可以设置其开关

屏幕映射:在3D空间里的所有顶点是连续的,我们会把它从3D空间变换为2D屏幕空间,这个时候它就从连续变为离散,也就是屏幕空间里面一个个独立的像素点

2.1 各个子阶段详细

顶点着色器:主要会对顶点做坐标变换,同时会进行一定的着色操作,坐标变换:先将顶点的位置变换到世界坐标系,首先需要知道这个三角形(物体)在世界坐标系中的位置,然后每一个顶点也就是三角形里面的顶点,需要通过旋转平移缩放来得到顶点在世界坐标系当中的位置,因为最终渲染的是我们看到的东西,所以需要把顶点从世界坐标系转换为观察坐标系,接下来还需要做投影变化,得到投影坐标系的位置,这一步是为了最后3D到2D投影做准备,分两种正交和透视,正交就是远近一样大,透视就是近大远小,投影还有一个裁剪的作用,即该顶点是不是在视椎体包裹的范围之内,前面这三步模型变换、视图变换、投影变换对应的就是MVP矩阵,在unity的顶点着色器中,顶点会从模型坐标系转换为投影坐标系,最后一步投影变换是否将顶点变换到屏幕的2D坐标系,是由unity自动完成的,然后顶点着色器还有一个作用是顶点着色,比如计算顶点光照等等


可选顶点处理:曲面细分着色器:是一个可选步骤,主要使用顶点着色器输出的顶点,按照一定的规则和算法,生成更多的顶点,将现有的网格和图元细分,比如立方体的网格,在顶点之间插入更多的顶点,并且迭代多次,最后生成一个光滑的球体,几何着色器:顶点着色器里面的运算都是使用单个顶点数据的,而几何着色器的操作对象是一个图元,这个图元可能是一个顶点、也可能是一个线段和两个顶点、也可能是多个顶点构成的连续线段、也可能是三个顶点构成的三角形,作用是根据给定的图元生成更多的图元

 

投影:前面的步骤生成的坐标都是在3D空间,我们要把它转换为2D屏幕坐标,就需要投影,前面顶点着色器里的顶点会从模型空间转为世界空间,转到观察空间,再转到裁剪空间,对于顶点在裁剪空间中的位置xyzw会做一个透视除法,把xyz除以w来完成投影操作,这样就从投影坐标系转换为了标准坐标系也就是NDC,在正交投影中w=1,那么xyz除以w还是原来的xyz,投影到屏幕后还是原来的数字,所以不论远近都是一样大的,透视模式中,w近处小远处大,所以投影到2D屏幕就会呈现近大远小的效果,  


裁剪:顶点从投影坐标系转换到标准设备坐标系,也就是立方体之中,他们的xyz都会除以w,除过以后如果xyz超出了-1到1之间,就认为不需要显示,需要裁剪掉


屏幕映射:上个阶段的操作和输出都是在标准设备坐标系当中,我们要把它映射到屏幕坐标系统中,

3 光栅化阶段

光栅化:三角形设置:对于一个图元,比如一条直线或者一个三角形,我们知道它的顶点,那怎么知道它覆盖的屏幕像素呢,这就需要知道这个图元的边界信息,计算边界信息的过程,叫做三角形设置,当我们得到三角形的边界信息之后,就会遍历所有相关的像素,寻找被三角形网格所覆盖的所有像素的过程,就叫三角形遍历,光栅化阶段还可能做一些事情,就是抗锯齿


4 逐片元操作

各个子阶段概括

光栅化阶段结束以后,就得到被三角形覆盖的片元序列,对序列中每个片元操作就是逐片元操作

片元着色:就是对每个片元进行着色,这一步是可编程的

颜色混合:屏幕上同一个像素点有多个片元时,对其进行取舍,需要对其进行一系列测试,透明度测试、深度测试和模板测试,通过了才留下来,通过的这些片元用某种方式对其进行混合最终输出到一个像素,得到了所有的输出结果后,我们把它输出到目标缓冲区    

各个子阶段详细

    

片元着色:前面在光栅化阶段,对于一个三角形所覆盖的片元,我们会使用三个顶点数据,对其进行线性插值   ,然后再用片元数据和其他的全局数据,比如光照、阴影和时间等等,得到最终的片元颜色


颜色混合: 需要测试,首先是透明度测试,那么透明度测试的原理就是透明度小于给定阈值的片元就会被舍弃而不渲染,深度测试的对象就是片元的深度值,我们会把片元的深度值和对应的深度缓冲当中的深度值进行比较

1到4 整体回顾

首先有房子的模型,对其进行不同的位置、朝向以及缩放变换,得到了世界空间里的两个房子的实体,同时场景里还有光照,到了顶点着色阶段,对房子进行一个顶点光照处理,那么房子就有了明暗变化,接着还对房子进行一个几何形状的变化,方房子变成圆房子,然后进行视图变化和投影变化,并且观察空间中被挡住的圆房子就不渲染了,到了光栅化阶段,设置这个房子所有顶点组成的三角形,并且找出这些三角形覆盖的片元和像素,再对每个片元进行着色,那么可能是对顶点取得顶点颜色然后进行插值,也可能是对顶点uv信息进行插值取得贴图的数据,最后通过深度模板测试得到了最终渲染结果,然后显示在屏幕上

5 后处理

前面基础的渲染流程完了之后,就得到输出到缓冲区的图像,我们再对这个图像进行后续处理,就是后处理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,001评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,210评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,874评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,001评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,022评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,005评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,929评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,742评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,193评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,427评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,583评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,305评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,911评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,564评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,731评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,581评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,478评论 2 352

推荐阅读更多精彩内容