Shader学习-渲染流水线


简单记录了关于学习《Unity Shader 入门精要》的一些内容。

什么是渲染流水线

渲染流水线的工作是从一个三维场景出发,生成一张二维图片。《Real-Time Rendering,The Edition》一书中将渲染流程分为3个阶段:
应用阶段几何阶段光栅化阶段

应用阶段

渲染流水线的起点是CPU,即是应用阶段。在应用阶段开发者需要准备好场景数据,例如摄像机位置,光源,场景中的模型等;其次可以进行粗粒度剔除工作,以把那些不可见的物体剔除出去,这样就不需要再移交给几何阶段进行处理;最后需要设置好渲染状态。应用阶段最重要的输出就是渲染图元。应用阶段可分3个 阶段:

  1. 加载数据到显存中。 渲染所需要的数据都需要从硬盘中加载到内存(RAM),然后网格纹理等渲染所需要的信息又被加载到显存(VRAM)中。
  2. 设置渲染状态。 渲染状态定义了场景中的网格怎样被渲染的。例如,使用哪个顶点着色器、片元着色器、光源属性、材质等。如果没有定义渲染状态,那么所有网格都会使用同一种渲染状态。
  3. 调用Draw Call 。 准备好上述所有工作后,CPU就需要调用渲染命令来告知GPU可以开始渲染了。Draw Call实际上就是一个命令,由CPU发起,GPU接收,命令指向一个需要被渲染的图元列表。当给定了一个Draw Call时,GPU就会根据渲染状态(材质、纹理、着色器等) 和所有输入的顶点数据来计算,最终完成屏幕显示。

GPU流水线

几何阶段和光栅化阶段的实现载体是GPU,开发者无法拥有绝对的控制权,几何阶段和光栅化阶段可以分成若干个更小的流水线阶段,每个阶段GPU提供了不同的可配置性和可编程性。

绿色表示完全可编程控制,实线表示该Shader必须由开发者编程实线,虚线是可选的。
黄色表示可配置但不可编程。
蓝色表示由CPU固定实现,开发者没有任何控制权。

GPU的渲染流水线实现

顶点着色器

顶点着色器处理单位是顶点,即输入进来的每个顶点都会调用一次顶点着色器。顶点着色器的任务主要是:顶点变换和逐顶点光照。

  • 坐标变换。顶点着色器必须完成的工作是把顶点坐标从模型空间转换到齐次裁剪空间,接着由硬件设备做透视除法后,得到归一化的设备坐标(NDC)。 顶点着色器可以在这一步中改变顶点的位置,实现类似水面流动的顶点动画。

裁剪

不在摄像机视野范围内的物体将不会被处理,完全在和完全不在摄像机视野范围内的很好理解。跟范围边界有交叉情况时,会在线段和边界生成新的顶点,外部的则被裁剪掉。

屏幕映射

把每个图元的x,y坐标转换到屏幕坐标系。屏幕坐标是二维坐标系,所以最终会是一个从[-1,1]到窗口坐标x1,y1,x2,y2缩放的过程。输入的z会与屏幕坐标系一起构成窗口坐标系传入光栅化阶段。

三角形设置和三角形遍历

计算一个三角形网格所需要的信息。从上一阶段传入都是网格的顶点信息,为了得到整个三角网格的覆盖情况,必须知道每条边上的像素坐标。
三角形遍历会检查每个像素是否被一个三角形网格所覆盖,如果被覆盖该像素就会生成一个片元(fragment)。这一步的输出就是一个片元序列。片元并不是真正意义上的像素,它还包含了很多状态的集合,这些集合用于计算每个像素最终显示出来的颜色。这些状态包括了(屏幕坐标、深度信息、法线、纹理坐标)等。

片元着色器

片元着色器输出是一个或多个颜色值。这一阶段可以完成很多重要的渲染技术,其中最重要的技术之一是纹理采样。为了在片元着色器中进行纹理采样,我们通常会在顶点着色器阶段输出每个顶点所对应的纹理坐标,然后通过光栅化阶段对三角网格的三个顶点对应的纹理坐标进行插值后,就可以得到其覆盖的片元的纹理坐标。
除最后一个阶段的逐片元操作会对像素产生影响外,光栅化的前几个阶段都不会影响屏幕像素的颜色值,而是会产生一系列的数据信息。

逐片元操作

最后一个阶段在OpenGL中叫做逐片元操作,在DirectX中,叫做输出合并阶段。这一阶段的主要任务有:

  • 决定每个片元的可见性。这涉及了很多测试工作。例如深度测试,模板测试等。
  • 如果一个片元通过测试后,会将这个片元的颜色和已经存在在颜色缓存区的颜色进行混合。

片元 → 模板测试 → 深度测试 → 混合 → 颜色缓冲区
逐片元操作阶段所做的操作。
只有通过所有测试后,新生成的片元才能和颜色缓冲区中已经存在的像素颜色进行混合,最后再写入颜色缓
冲区。

  • 模板测试(Stencil Test)。与之相关的是模板缓冲,如果开启了模板测试,GPU会首先读取(使用读取掩码?)模板缓冲区中该片元位置的模板值,然后进行比较,比较函数可以自定义,片元没有通过测试将会被舍弃掉。无论片元有没有通过模板测试,我们都可以根据与模板测试和深度测试结果来修改模板缓冲区(??这里不太明白)。模板测试通常用于限制渲染的区域。模板测试还用于更高级的用法,如渲染阴影、轮廓渲染等。
  • 深度测试。如果一个片元通过模板测试,接下来便是深度测试。如果开启了深度测试。GPU会把该片元的深度与已经存在于深度缓冲区的中的深度进行对比,比较函数可以设置。如果一个片元没有通过深度测试,会被舍弃,同时没有权力改写深度缓冲区。如果通过测试开发者可以通过是否开启深度写入来限制该片元是否能够覆盖缓冲区的值。
  • 合并。当一个片元通过所有操作,将会进行合并操作。完全用新的颜色覆盖原有颜色还是与原有颜色混合是合并操作需要决定的事情。对于不透明物体可以选择关闭 混合(Blend) 操作,直接覆盖,而对于半透明物体,我们需要使用混合操作来达到所需效果。如果开启了混合,GPU会取出源颜色(片元着色器得到的颜色)和目标颜色(存在于颜色缓冲区中的颜色)进行混合。这里需要一个混合函数,函数与透明通道息息相关。例如根据透明通道进行相加、相减、相乘等。

上面给的测试顺序并不是唯一的,虽然从逻辑上来说这些测试在片元 着色器之后进行,但对于大多数GPU,都会尽可能在执行片元着色器之前就进行这些测试,片元着色器阶段花费很多算出片元的颜色,却因为没有通过测试,片元被舍弃到,那计算成本将全部浪费掉。但是这种Early-Z技术有可能会跟片元着色器中的操作相冲突,例如在片元着色器中进行了透明度测试,会手动舍弃掉不满足测试的片元。那么GPU就不能进行提前测试。现代GPU会判断片元着色器中的操作是否与提前测试相冲突,如果冲突,会禁用掉提前测试,这样会造成性能上的下降。

避免我们看到正在进行光栅化的图元,GPU会使用双重缓冲策略,场景的渲染在幕后发生,即后置缓冲中,一旦渲染完成,就会交换前置缓冲和后置缓冲,保证看到的图像都是连续的。

问题

关于Draw Call

Draw Call 是CPU向GPU发起的一个渲染命令。CPU和GPU通过命令缓冲区实现并行工作,CPU向命令缓冲区添加命令,GPU从缓冲区中读取命令,添加和读取操作相互独立。命令缓冲区中命令有很多,Draw Call 只是其中一种,其他还有改变渲染状态等(改变使用的shader,使用不同的纹理等,改变渲染状态更加耗时)
Draw Call 多了会影响帧率。 CPU每次调用Draw Call之前,都会向GPU发送很多内容,包括数据、状态和命令等,CPU需要完成很多操作,CPU完成这些准备工作后,GPU就可以开始本次渲染,GPU的渲染速度是往往快于CPU提交命令的速度的,Draw Call过多时,CPU会把大量时间花费在提交Draw Call上,造成CPU过载。。
减少Draw Call。 批处理。批处理需要在CPU在内存中合并网格,更适用于静态物体。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值