渲染流程的3个阶段:应用阶段,几何阶段,光栅化阶段
应用阶段:由开发者主导,CPU负责实现。
3个任务:a.准备场景数据(模型,光源,摄像机等) b.剔除(提高渲染性能) c.设置渲染状态(材质,纹理,shader)。这一阶段最重要是输出渲染所需的几何信息,就是渲染图元(包括点线面等)
几何阶段:在GPU上处理几何相关事情,进行逐个顶点,多边形操作(如绘制的图元是什么,怎么绘制,在哪绘制)
重要任务:把顶点坐标变化到屏幕空间里,输出屏幕空间二维顶点坐标,每个顶点对应的深度值,着色相关信息。
光栅化阶段:GPU运行。使用几何阶段数据,产生屏幕像素,渲染最终图像。
2个主要任务:a.计算每个图元覆盖了哪些像素 b.为这些像素计算它们的颜色。
CPU和GPU通信3个阶段:数据加载到显存,设置渲染状态,调用DrawCall
数据加载至显存中:渲染数据从硬盘加载到内存,再加载至显存。显卡对RAW没有直接访问权限,显卡对显存访问更快。
设置渲染状态:定义场景中网格怎么被渲染(如使用哪个shader,light,material)
调用Draw Call :CPU调用图像编程接口,命令GPU进行渲染操作。
GPU流水线(几何阶段+光栅化阶段)
顶点着色器 Vertex Shader :顶点着色器是完全可编程的,通常用于实现顶点的空间变换、顶点着色等功能。
GPU第一阶段,输入来源于CPU。处理单位是顶点。本身不可创建和销毁顶点。独立性,处理速度快。
主要任务:1,坐标变化 2,计算顶点颜色。
曲面细分着色器 Tessellation Shader :曲面细分着色器是一个可选的着色器,用于细分图元。
几何着色器 Geometry Shader :几何着色器是一个可选的着色器,用于执行逐图元着色操作,或产生更多图元。
裁剪 Clipping : 图元和摄像机的视野3种关系。是将那些不在摄像机视野内的顶点剪裁掉,并剔除某些三角图元的面片。
屏幕映射 Screen Mapping :屏幕映射是不可配置和编程的,负责把每个图元的坐标转换到屏幕坐标系。实际就是一个缩放过程。
三角形设置 :计算光栅化一个三角网格所需的信息,它的输出是为下一步骤做准备。
三角形遍历:找到哪些像素被三角网格覆盖的过程。对应像素会生成一个片元,而片元中的状态是对三个顶点的信息进行插值得到的。
片元着色器 Fragment Shader(DX:Pixel Shader) :输入是上一步对顶点信息插值得到的结果,输出是颜色值(着色)
补充:片元着色器这一步骤可以完成很多重要的渲染技术,比如 纹理采样:在顶点着色器阶段输出每个顶点对应的UV坐标(也叫纹理坐标),然后经过光栅化阶段对三角网格的3个顶点对应的UV坐标进行插值后,就可以得到覆盖的片元的UV坐标了。
可以这样理解贴图:不妨把纹理图片想象成一片弹性很好的橡皮薄膜,贴图就相当于用钉子把橡皮薄膜固定在与其纹理坐标相对应的顶点上。
逐片元操作:OpenGL中的说法,在DX中,叫输出合并阶段。
该阶段的任务:
决定每个片元的可见性。涉及了很多测试工作,例如模板(Stencil)测试(小于或者大于等于模板缓冲区中该片元的模板值就舍弃该片元,通常用于限制渲染区域。)、深度(Depth)测试(大于等于深度缓冲区中该片元的深度值就舍弃该片元)等;
如果一个片元通过了所有测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区中的颜色进行混合,最后再写入颜色缓冲区中。也就是将片元的颜色值一个个地堆叠起来。
总结:只需要在一个Unity Shader中设置一些输入、编写顶点着色器和片元着色器、设置一些状态就可以达到大部分常见的屏幕效果。
CPU、OpenGL/DirectX、显卡驱动和GPU之间的关系图:
OpenGL/DirectX是图像应用编程接口,架起了应用程序和GPU沟通的桥梁。应用程序向这些接口发送渲染指令,而这些接口会依次向显卡驱动发送渲染命令。显卡驱动就是显卡的操作系统,它知道如何和显卡中的GPU打交道,并将渲染命令翻译成GPU能理解的机器语言。
优化:批处理可以有效减少Draw Call。Draw Call造成性能问题的元凶是CPU 。很简单的一个道理:10000个1kb的小文件和1个10M的大文件同时传输,肯定是1个10M的文件传得快许多。
为避免我们看到正在光栅化的图元,GPU采取双重缓冲的机制,渲染发生在后置缓冲中,渲染结束后,GPU就会交换后置缓冲区和前置缓冲区中的内容。
最后附上万大的神图: