Unity&Shader基础篇-可编程GPU图形绘制管线

时间:2023-02-06 22:10:21

Cg是最早的为可编程图形硬件设计的高级编程语言。它是英伟达和微软公司一起合作开发出来了语言,如果你非常熟悉C语言或者其他的编程语言,如C++、C#或者Java等,那对于Cg语言你将会非常容易掌握。Cg程序通常都比较的小,结构也非常的简单,尽管如此,不了解基本的Cg语言的语法和它的运行机制也是很难理解一段Cg程序到底做了什么以及要怎么控制它到达预期的效果

1.1、可编程GPU图形绘制管线

  所谓可编程图形绘制管线,就是给定虚拟摄像机、模型、光源等元素输出一副电脑屏幕显示的二维图像。《实时计算机图形学》的书中概况图形绘制管线为三个主要的阶段:应用程序阶段、几何阶段和光栅阶段。

    应用程序阶段,是使用高级语言进行开发,主要和CPU进行交流,如我们熟知的碰撞检测就是在此阶段执行的。基于此特点开发者能够对该阶段进行完全的控制,因此可以通过一些算法的应用来提高性能。

    几何阶段,主要负责顶点变换、光照、裁剪、投影以及屏幕映射。该阶段是在GPU中执行的,考虑到要将三维模型最终显示在一个二维的屏幕上,因此该阶段最重要就是顶点坐标空间的转换。值得注意的是光照计算也是在几何阶段被处理。

    光栅化阶段,决定哪些像素最终显示在屏幕上的过程,雾化和透明度处理都是在这个阶段被处理。

    GPU是高度并行的处理器,实际上它包含两种并行机制:纵向并行和横向并行。可以这样去理解,想象一个工厂的工人在一个流水线上同时工作,这个就是纵向并行,而一个国家的相同工厂在同时工作,这就叫纵向并行。在OpenGL ES 1.X、核心OpenGL 1.X以及Direct3D 7.x中的管线都是配置了固定功能的管线,这个管线中都不能通过程序进行控制。而在OpenGL(ES)、WebGL和Direct3D8.0中上述两个并行处理管线是可编程的,即称为可编程图形绘制管线,Cg程序就是在这个阶段被执行。如图所示虚框部分为可编程阶段,涂黑框部分为图元数据,其他部分为固定功能阶段。

Unity&Shader基础篇-可编程GPU图形绘制管线

 

其中顶点着色器和片段着色器会在接下来的章节中具体讲述,也是本书的重点。

1.1.1、顶点坐标空间的转换

    在可编程图形绘制管线中顶点着色器最重要的任务就是将图元顶点的从模型局部坐标空间转换到屏幕坐标空间。虽然可编程顶点着色器允许顶点变换的方法很多,但是有些变换过程还是发生在之后的固定功能阶段。当编写顶点着色器程序的时候,理解那些过程各自发生在哪个阶段是非常重要的。如图1.2所示

为具体的转换过程,前三个过程都发生在顶点着色器中,透视剔除和视口转换发生在固定功能阶段,在该阶段都被自动执行并且不能对其进行任何控制。

模型坐标转换:将模型坐标或者局部坐标转换到常见的世界坐标。模型坐标通常都是美术通过模型工具,如maya或者3D Max等工具指定。世界坐标是场景中所有对象共用的坐标系,这些对象如光源、3D音效和所有3D模型。 Unity&Shader基础篇-可编程GPU图形绘制管线

视点转换:将世界坐标转换成视点坐标,视点就相当于Unity的虚拟摄像机,视点坐标系就以虚拟摄像机为原点的坐标系。如图所示

Unity&Shader基础篇-可编程GPU图形绘制管线

视点转换就是将模型的世界坐标转换成视点所在的坐标系。而通常Unity中所说的摄像机的视场角就是在坐标轴方向上所展开的角度,如1.3图中的 角。

投影转换和裁剪剔除:投影分为正交投影和透视投影,图1.3所示为透视投影,正交投影即近裁剪面和远裁剪面一样大。它们的区别就是正交投影投出来的图像没有远近之分,而透视投影有;它们之间的联系就是都要进行裁剪剔除。实际上,投影转换就是将视点坐标系转换成裁剪坐标系,所有处于近、远裁剪面的梯形体外的部分都将被裁剪掉。最后这些转换后的裁剪坐标进行归一化设置,使得所有的坐标值都在-1到1的范围内,在这个阶段坐标仍然是三维的。

视口转换:将归一化后的坐标映射到屏幕上进行绘制,这个过程是发生在GPU的固定功能阶段,被GPU自动执行。屏幕映射会进行平移,x.y坐标会被改变,新的x和y坐标被称为屏幕坐标,将与z坐标一起进入光栅阶段。

1.1.2、图元装配和光栅化阶段

Unity&Shader基础篇-可编程GPU图形绘制管线

图元装配:前面经过顶点变换后得到都只是单个的顶点信息,在图元装配阶段会根据顶点的序列和分类信息将顶点装配成几何图元。最后会产生一序列的三角形、线段和点。如图1.4所示的片段纹理映射和着色,最后到光栅化的阶段。光栅化其实就是一个决定哪些像素被几何图元覆盖的过程,屏幕在刷新的过程其实就是像素不停的被覆盖的过程。光栅化后,一个图元拥有的顶点数目和产生的片段之间没有任何关系。例如,一个由顶点组成的三角形可以占据整个屏幕而需要生产上百万个片段。

片段和像素的区别:在光栅化阶段会把每个几何图元(如三角形)所覆盖的像素分解成像素大小的片段。而像素代表帧缓冲中某个指定位置的内容,如颜色、深度和其他与这个位置相关联的值;片段即是经过各种光栅化测试之后得到的将更新一个特定像素的潜在状态。

光栅化操作:将屏幕空间的二维顶点转换为屏幕上的像素。这个阶段会根据许多测试来检查每个片段,这些测试包括剪切、透明度混合、模板和深度等测试。这些测试过程会决定最后像素的位置、颜色的值,所以,如果任何一项测试不通过这个片段就会被丢弃掉。

双缓冲机制:为了避免出现闪屏或卡帧的现象,GPU都采用了双缓冲机制,一旦屏幕在后置缓冲器中绘制,后置缓冲器中的内容就会不断与显示在屏幕上的前置缓冲器的内容进行交换。

Z缓冲器:也称为深度缓冲器,前面提到的深度测试的过程也即是Z缓冲器的处理过程。每个像素都存储着一个z值,这个z值是图元到虚拟相机之间的距离。当将一个图元绘制成像素的时候,需要将像素位置处图元z值与处在Z缓冲器里的同一像素的z值进行比较。如果新得到的z值远远小于Z缓冲器中的z值,就说明这个图元距离相机更近,因此需要使用新得到的像素去更新当前图元的信息。反之,则图元信息不变。注意这里提到的是像素的z值,不是模型距离摄像机的距离z,像素有可能是前面的模型也有可能是后面的模型进行覆盖后的像素。

当图元经过以上这些变化之后,就可以在屏幕上看到图像。Z缓冲之所以重要,是因为z值决定了物体之间的相互遮挡关系,如果没有足够的精度,则两个相距很近的物体就会出现随机遮挡的现象。

透明度混合:为了绘制透明物体,通常需要对物体进行排序。首先,绘制不透明的物体;然后,在不透明物体的后面,对透明物体按照由后到前的顺序进行混合处理。实时绘制中对于透明度的混合有一定的局限性,如,因为透明度的厚度导致管线的逐步弱化和光线在物体内的折射效果。如果按照任意序列会产生严重的失真,因此对于排序会使用Z缓冲。关于透明度混合的相关技术可以参考《实时计算机图形学(第二版)》第四章4.5节(59页)。

模板缓冲:如果一个像素的模板缓冲中存放了1,则表示该像素对应的空间处于阴影体中。所有模板缓冲通常都是处理阴影效果的。