最近在b站上刷opengl课程被傅老师安利了一本叫做《Unity Shader 入门精要》的书,之前在实验室打算看的但是放假忘嫖回家了于是就入手了一本,今天开始更新自己的阅读理解心得。
首先,shader只是计算机3D渲染流水线中的一部分,如果想要真正学懂Shader的工作原理必须要先了解计算机渲染的整个流程。
为什么要使用流水线进行渲染?计算机渲染的过程和工厂生产一样,必须要追求高效率,即在单位时间能尽可能完成更多的工作,所以如果让每个流程由专门的“工人”来负责,比起非流水线的生产方式生产效率提高了n倍。在流水线模式中影响生产效率的则是消耗时间最长的部分,即瓶颈(bottleNeck)。
计算机渲染流水线分为三个大的阶段, 即应用阶段,几何阶段,光栅化阶段。接下来逐一概述这三个阶段。
应用阶段:这一部分主要由CPU负责由程序主导,这部分我个人理解是负责准备渲染的各种几何信息即渲染图元如点线面等,其此还有渲染状态(使用什么shader和贴图),使用哪些光源传递给几何阶段。
几何阶段:这一阶段由GPU负责,几何阶段负责将从应用阶段传递过来的渲染图元进行各种坐标变换比如将顶点坐标变换到屏幕空间,它决定需要绘制的图元是什么以及如何绘制。它将输出顶点的各种信息,比如深度值和着色信息传递到光栅化阶段,在这个阶段还将分为更细小的流水线,后面将会提到。
光栅化阶段:同样是GPU负责,这个阶段则是通过使用在上个阶段传过来的数据和运算在屏幕像素上生成最后的颜色。这个过程将使用顶点的深度值与着色信息和贴图坐标等信息通过插值运算计算每个图元是否被遮挡和颜色并与其和屏幕上相应像素点相对应。这一过程也可以分为更细小的流水线。
提一嘴:CPU与GPU的通信过程是通过CPU将数据从硬盘传输到RAM和VRAM(显存),GPU再从计算机内存中调用数据信息实现的。
现在详细说说GPU流水线的两个渲染流程是怎么实现的。先来看看GPU中的流程大概是怎样的。
顶点数据--->几何部份(顶点着色器--->裁剪--->屏幕映射)--->光栅化部分(三角形生成--->三角形遍历--->片元着色器--->逐片元操作)--->最终成像
其实一共就七个阶段。接下来从顶点着色器开始讲解。
顶点着色器:这其实就是一个Shader,而且是在渲染流程中非常重要和常用的一种。作为GPU流水线的第一关,这个Shader要负责将从CPU中传入的顶点数据进行几何变换,将坐标变换到坐标-1到1之间的点中,并且要负责计算输出每个顶点的着色信息。
裁剪:再上一个阶段我们将顶点变换到了-1到1的坐标之间,但是这个过程中有的点会变换到这个坐标之外,在这个过程中GPU需要将超出的顶点删去并用对应规定坐标内的顶点代替。就很向剪纸一样,我不需要的一个角就用剪刀裁去,然后这一个锐角就变成了两个钝角来替代。裁剪顶点的目的是为了为下一阶段屏幕映射准备数据,如果存在超出屏幕范围的顶点则无法找到屏幕对应的坐标。
屏幕映射:这个阶段就是要将顶点坐标投射到屏幕相应坐标上,将3d的点转换为2d。
三角形生成:这一阶段是光栅化部分的开始,GPU要使用屏幕上的顶点生成一个个三角形的顶点信息即每个顶点对应的是哪个三角形的哪一个顶点。在这之前GPU一直传输的只是一个个顶点,而这些顶点是哪个面的顶点是不清楚的。这个阶段是为下一阶段做准备。
三角形遍历:上一阶段我们可以得到每个三角形的顶点的所有信息包括坐标,法向量,深度,着色信息等,通过坐标我们遍历每个像素检查是否被三角形覆盖,如果被覆盖了就会生成一个“片元”用于存放各种信息包括坐标,深度值,纹理坐标法向量等等,而通过插值运算就能计算出三角形中的每个片元的各种信息。
片元着色器:这个阶段又是另外一个非常重要和常用的可编程Shader,这个shader的作用就是输出颜色信息。在这之前片元中只存放的是数据信息并没有输出真正的颜色,片元shader通过插值运算每个片元的纹理坐标即可计算出在该片元的颜色。但是在书上有一个部分我没看太懂,书上说执行片元shader时不可以将自己结果直接发送给他的邻居们。他的邻居们是什么?为什么不能?这些书上没有讲到。
逐片元操作:终于我没来到GPU流水线的最后一关,在这里之前每一个计算好的片元数据都会进行一系列的“最终试炼”才能获得显示在屏幕上的资格。逐片元操作是高度可配置的,即你可以决定如何对片元进行筛选,什么样的数据将会显示在屏幕上。常见的测试是模板测试和深度测试。都是将现有片元数据和对应缓冲区的数据进行对比得到结果,如果没有通过的话该片元就会被舍弃掉,之前在该片元进行的运算都白费了!如果片元通过了测试就会更新原缓冲区。在这一阶段还有一个重要操作就是混合,如果片元成功挺过最后一关可以写入到像素点的颜色缓冲中,那么我将如何写入呢?要知道颜色缓冲中还保存着上一次写入的颜色信息,直接覆盖掉还是需要与原来的信息进行混合以实现半透明等特殊效果呢?这就是混合需要完成的工作。
至此GPU流水线完整过程就结束了,但限于篇幅还有很多细节没有提到,比如逐片元操作是否可以提前进行以避免无用的片元信息计算,还有double buffer的作用等等。