渲染流水线

学习Unity Shader 入门精要的笔记。

什么时渲染流水线

渲染流水线的任务在于由一个三维场景出发,生成一张二维图像。这个工作时CPU和GPU共同完成的。

一般将渲染流水线分成三个阶段:应用阶段,几何阶段,光栅化阶段,这里仅仅是概念性阶段,每个阶段通常也是一个流水线系统。

应用阶段

这个阶段是我们自己主导的,因此通常由CPU负责实现。

开发者有三个主要的任务:

1.我们要准备好场景的数据,例如摄像机的位置,视锥体,场景中包含的模型,使用的光源等。

2.为了提高渲染性能,我们需要做一个粗粒度剔除工作,把不可见的物体剔除出去,这样就不用交给几何阶段去处理了。

3.我们需要设置好每一个模型的渲染状态。这一阶段最重要的输出是渲染所需的几何信息,即渲染图元。

几何阶段

几何阶段用于处理所有和我们需要绘制的几何相关事情。这一阶段通常在CPU上进行。

几何阶段负责和每一个渲染图元打交道,进行逐顶点逐多边形的操作。几何阶段最重要的就是把顶点的坐标转到屏幕空间中,再交给光栅器进行处理。

光栅化阶段

这一阶段将会使用上个阶段传递的数据来产生屏幕上的像素,并且渲染出最终的图像。这一阶段也是再GPU上运行的。光栅化的任务主要是决定每一个渲染图元中的那些像素应该被绘制出来。

 

CPU和GPU之间的通信

渲染流水线的起点是CPU,即应用阶段。该阶段可以分为一下三步。

1.把数据加载到显存中。

2.设置渲染状态。

3.调用Draw Call。

把数据加载到显存中

所有的渲染数据都要从硬盘中加载到系统内存中。然后,网格和纹理等数据又被加载到显卡上的存储空间——显存中。因为显卡对于显存的访问速度更快,而且大多数显卡对于RAM没有直接访问的权限。

设置渲染状态

这些状态定义了场景里的网格是怎么样被渲染的。

调用Draw Call

Draw Call就是一个命令。他的发起方是CPU,接收方是GPU。这个命令仅仅指向一个需要被渲染的图元列表,而不包含任何材质的信息——这是因为我们已经在上一个阶段中完成。

GPU流水线

GPU从CPU哪里得到命令后,就会进行一系列的流水线操作,最终把图元渲染到屏幕上。

几何阶段和光栅化阶段可以分成若干个跟小的流水线阶段,这些流水线阶段由GPU来实现,每一个阶段GPU提供不同的可配置性或可编程性。

顶点着色器

顶点着色器是流水线的第一个阶段,他的输入来自于CPU。顶点着色器的处理单元是顶点,也就是说,输入进来的每一个顶点都会调用一次顶点着色器。顶点着色器本身不可以创建或者销毁任何顶点,而且无法得到顶点和顶点之间的关系。我们无法得知两个顶点是否属于同一个三角网格。

顶点着色器需要完成的工作有:坐标转换和逐顶点光照。顶点着色器还可以输出后续阶段需要的数据。

裁剪

不在相机内的物体不需要被处理。而裁剪就是为了完成这个目标。

一个图形有三种关系,完全在视野内,部分在视野内,完全在视野外。那些部分在视野内的图元需要进行一个处理,就是裁剪。

屏幕映射

这一步输入的坐标仍然是三维坐标系下的坐标。屏幕映射得到屏幕坐标决定了这个顶点对应屏幕上哪个像素以及距离这个像素有多远。

三角形设置

光栅化阶段有两个重要的目标:计算每个图元覆盖了那些像素,以及为这些像素计算他们的颜色。

光栅化的第一个流水线阶段是三角设置。

三角形遍历

这个阶段将会检查每个像素是否一个三角网格所覆盖。如果被覆盖的话,就会生成一个片元。

片元着色器

前面光栅化阶段实际上并不会影响屏幕上每一个像素的颜色值,而是产生一系列的数据信息,用来表述一个三角网格是怎么覆盖每一个像素的。而每个片元就负责存储这样一系列数据。真正对像素产生影响的阶段是下一个流水线——逐片元操作。

逐片元操作

这一阶段主要有一下任务

1.决定每个片元的可见性。这里涉及到很多的测试工作,例如深度测试,模板测试等。

2.如果每个片元通过了所有的测试,就需要把这个片元的颜色值和已经存储在颜色传冲区中的颜色进行合并,或者混合。

这个阶段首先要解决每个片元的可见性。

什么是 DrawCall

 DrawCall就是一个命令,从CPU发起,GPU接收。这个命令指向一个需要被渲染的图元(primitives)列表,告诉GPU开始进行一个渲染过程。相当与OpenGL中的glDrawElements命令,DirectX中的DrawIndexedPrimitive命令。

CPU和GPU并行工作

使用一个命令缓冲区(Command Buffer),CPU添加命令,GPU读命令。命令有多种,DrawCall是其中一种,其他命令还有改变渲染状态等。

为什么 Draw Call多了会影响帧率?

在每次调用Draw Call之前,CPU需要向GPU发送很多内容,包括数据﹑状态和命令。在这一阶段,CPU需要完成很多工作,例如检查渲染状态等。而一旦CPU完成了这些准备工作,GPU就可以开始本次的渲染。GPU的渲染能力是很强,渲染200个还是2000个三角网格通常没有什么区别,因此渲染速度往往快于CPU提交命令的速度。如果Draw Call的数量太多,CPU就会把大量时间花费在提交Draw Call 上,造成CPU的过载。

如何减少Draw Call

此处仅说批处理

提交大量很小的Draw Call会造成CPU的性能瓶颈,即CPU把时间都花费在准备Draw Call的工作上了。那么,一个很显然的优化想法就是把很多小的Draw Call合并成一个大的Draw Call,这就是批处理。

需要注意的是,由于我们需要在CPU的内存中合并网格,而合并的过程是需要消耗时间的。因此,批处理技术更加适合于那些静态的物体,例如不会移动的大地,石头等,对于这些静态物体我们只需要合并一次即可。我们也可以对动态物体进行批处理。但是由于这些物体是不断运动的,因此每一帧都需要重新进行合并然后再发送给GPU,这对时间和空间都会造成一定的影响。

1.避免使用大量很小的网格。当不可避免的需要使用很小的网格结构时,考虑是否可以合并它们。

2.避免使用过多的材质。尽量在不同的网格之间共用同一个材质。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值