- 应用阶段
- 需要准备好场景数据,比如摄像机、模型、光源等。
- 需要再这里做粗粒度剔除(culling)工作,把不可见的物体剔除出去,提高渲染性能。
- 设置好每个模型的渲染状态,比如材质,纹理,shader等。
这个阶段主要是输出渲染所需的几何信息,即渲染图元(rendering primitives),然后传递给下一个阶段(CPU)
- 几何阶段
- 把顶点坐标变换成屏幕空间坐标
- 输出每个顶点对应的深度值,着色等信息
变换坐标是这个阶段的主要任务,逐顶点、逐多边形操作(GPU)
- 光栅化阶段
- 产生屏幕上的像素
- 渲染出最终图像
决定每个渲染图元中哪些像素被绘制在屏幕上,根据上一阶段得到的逐顶点数据进行插值,然后再逐像素处理
1 CPU流水线
也就是应用阶段,渲染流水线的起点,大致可分为三个阶段:
- 把数据加在到显存中
大多数显卡对系统内存是没有访问权限的,并且显卡对显存的访问速度更快。数据加载到显存中后内存(RAM)中的数据就会被移除。 - 设置渲染状态
也就是我们使用哪个顶点/片元着色器、光源属性、材质等 - 调用Draw Call
Draw Call是CPU发送给GPU的一个指令,会指向一个需要被渲染的图元列表。
2 GPU流水线
- 绿色:完全可编程控制
- 黄色:可以配置,不可编程
- 蓝色:GPU固定实现
- 实线:shader由开发者编程实现
- 虚线:shader可选
2.1 顶点着色器
顶点着色器的主要任务有两个:坐标变换和计算顶点颜色(如:逐顶点光照)
顶点着色器最基本的工作就是把顶点坐标从模型空间转换到齐次剪裁空间
o.pos = mul(UNITY_MVP,v.position);
类似上面这句代码的功能,就是把顶点坐标从模型空间转换到齐次剪裁坐标系下
2.2 剪裁
剪裁就是为了不处理摄像机视野范围外的物体
2.3 屏幕映射
屏幕映射是为了把每个图元的x和y坐标转换到屏幕坐标系下
实际上这个过程就是一个缩放过程,生成的屏幕坐标系和z坐标,一起构成了窗口坐标系。
需要注意的是,OpenGL把屏幕左下角当做最小的窗口坐标,DirectX把屏幕的左上角当做最小的窗口坐标。所以如果图像倒转了,可能是这个原因。
2.4 三角形设置
计算三角形每条边的两个端点,计算每条边上的像素坐标
2.5 三角形遍历
检查每个像素是否被三角形网格所覆盖,被覆盖了就会生成一个片元。这个过程就是三角形遍历,也叫扫描变换。然后对整个覆盖区域的像素进行插值。
2.6 片元着色器
片元着色器的输入,是上一个阶段对顶点信息插值得到的结果,输出则是一个或者多个颜色值。
片元着色器仅可以影响单个片元。
2.7 逐片元操作
- 决定每个片元的可见性。例如:深度测试、模板测试等。
- 如果一个片元通过了所有的测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区中的颜色进行混合
上图为逐片元操作的流程
- 模板测试
如果开启了模板测试,GPU会首先读取模板缓冲区中该片元位置的模板值,然后将该值和读取到的参考值进行比较,比如:大于或小于舍弃该片元。如果这个片元没有通过这个测试,那么该片元就会被舍弃。不管一个片元有没有通过模板测试,我们都可以根据结果来修改模板缓冲区,比如在失败时模板缓冲区保持不变,通过时将模板缓冲区对应位置值加1等。通常用于限制渲染区域。 - 深度测试
基本和模板测试的规则差不多,不同的是,如果一个片元没有通过深度测试,那么它就没有权利更改缓冲区中的值。如果它通过了测试,开发者还可以指定是否要用这个片元的深度值覆盖掉原有的深度值,这是通过开启/关闭深度写入来做到的。
- 合并
- 渲染过程就是一个物体接着一个物体画到屏幕上的,每个像素的颜色信息被存储在一个名为颜色缓冲的地方。因此,当我们执行这次渲染时,颜色缓冲中往往已经有了上次渲染之后的颜色结果,那我们使用这次渲染得到的颜色完全覆盖掉之前的结果,还是进行其他处理?这就是合并需要解决的问题。
- 对于不透明的物体,开发者可以关闭混合(Blend) 操作。这样片元着色器计算得到的颜色值就会直接覆盖掉颜色缓冲区中的像素值。但对于半透明物体,我们就需要混合,来让物体看起来透明。
- 双缓冲
- 为了避免看到正在进行光栅化的图元,GPU会使用双缓冲的策略。一旦有场景进入了后置缓冲区,那么后置缓冲区就会合前置缓冲区进行交换。从而保证我们看到的图像是连续的。
本文来源《unity shader入门精要》冯乐乐著
做为学习的记录,想学习的可以看这本书