Shader(着色器)
渲染流水线
渲染流水线的工作任务在于由一个三位场景出发、渲染一张二维图像。
渲染流水线的三个概念阶段:
应用阶段
通常由CPU负责实现。
主要三个任务:
- 准备好场景数据
- 粗粒度剔除(culling)工作,把不可见的物体剔除出去,提高渲染性能
- 设置好每个模型的渲染状态。渲染状态包括但不限于使用的材质(漫反射颜色、高光反射颜色)、使用的纹理、使用的shader等。这一阶段重要的输出是渲染所需的几何信息,即渲染图元(渲染图元可以是点、线、三角面等。
应用阶段大致可分为:
- 把数据加载到显存中
- 设置渲染状态
- 调用Draw Call
把数据加载到显存中
所有渲染所需的数据都需要从硬盘中加载到系统内存中。然后网格和纹理等数据又被加载到显卡上的存储空间–显存。这是因为显卡对于显存的访问速度更快,而且大多数显卡对于RAM没有直接的访问权利。
设置渲染状态
调用Draw Call
Draw Call是一个命令,发起方是CPU,接收方是GPU。这个命令仅仅会指向一个需要被渲染的图元列表,而不会再包含任何材质信息(这是因为我们已经在设置渲染状态阶段中完成了)
CPU通过调用Draw Call来告诉GPU开始进行一个渲染过程。一个Draw Call会指向本次调用需要渲染的图元列表
几何阶段
用于处理所有和绘制几何相关的事情。通常在GPU上进行。
负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作。
重要任务:把顶点坐标变换到屏幕空间中,再交给光栅器处理。通过对渲染图元进行多步处理后,这一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息,并传递给下一阶段。
光栅化阶段
会使用上个阶段传递的数据来产生屏幕上的像素,并渲染出最终的图像。在CPU上进行。
任务主要是决定每个渲染图元重的哪些像素应该被绘制在屏幕上。
GPU流水线
绿色:完全可编程控制
黄色:可配置但不可编程
蓝色:GPU固定实现,开发者没有控制权。
实线表示该Shader必须由开发者编程实现,虚线表示该Shader是可选的。
- 顶点着色器(Vertex Shader) 通常用于实现顶点的空间转换、顶点着色等功能
- 曲面细分着色器(Tessellation Shader) 用于细分图元
- 几何着色器(Geometry Shader) 用于执行逐图元的着色操作,或被用于产生更多的图元。
- 裁剪(Clipping) 目的是将不在摄像机视野内的顶点裁剪掉,并剔除某些三角图元的面片。
- 屏幕映射(Screen Mapping) 负责把每个图元的坐标转换到屏幕坐标系中
三角形遍历(Triangle traversal)
会检查每个像素是否被一个三角网格所覆盖,如果覆盖,就会生成一个片元(fragment)。也被成为扫描变换(Scan Conversion)
CPU和GPU如何实现并行工作
命令缓冲区(Command Buffer)包含一个命令队列,CPU向其中添加命令,GPU从中读取命令,添加和读取的过程是相互独立的。命令缓冲区使得CPU和GPU可以相互独立工作。当CPU需要渲染一些对象时,它向命令缓冲区添加命令,而当GPU完成了上一次的渲染任务后,它就可以从命令队列中再取出一个命令执行它。
Draw Call影响帧率
在每次调用Draw Call之前,CPU需要向GPU发送很多内容,包括数据、状态和命令等。在这一阶段CPU需要完成很多很多工作,例如检查渲染等。如果Draw Call数量太多,CPU就会把大量时间花费在提交Draw Call上,造成CPU过载。
Shader
Shader就是GPU流水线上一些可高度编程的阶段,而由着色器编译出来的最终代码会在GPU上运行