前言:
在之前的文章中,我们补充了图像绘制管线的三个阶段:应用程序阶段、几何阶段和光栅阶段,其中几何阶段和光栅阶段使我们理解绘制过程的重点,在这里我们需要着重分析一下这两个过程。
几何阶段
几何阶段的主要任务就是:“变换三维顶点坐标”和“光照计算”,这也与显卡中的两个标示“T&L”对应(Transform & Lighting)。
一、变换三维顶点坐标:
输入到计算机中的数据其实就是一系列的三维坐标数据,但是最后通过GPU计算后显示在二维的屏幕上,这就是说:GPU的计算过程实际上就是将三维的坐标数据绘制到二维屏幕上。
要完全理解上述过程,我们需要先知道几个坐标类型:
1.Object space —— 模型坐标空间
2.World space —— 世界坐标空间,
3.Eye space —— 观察坐标空间
4.Clip and Project space —— 屏幕坐标空间
如下是GPU工作流程中,顶点坐标空间的变换流程:
二、Object space转换到World space:
Object space(模型坐标空间),其实就是模型在建模时建立的一个与其他物体没有任何参照关系的基于自己原点的三维坐标空间,包含模型的顶点值。模型坐标空间对于操作模型自身属性比较实用,但当空间中有许多个物体,为了表达各个物体间的相对关系,由于模型坐标空间不包含与任何物体的参照关系,所以这里我们需要引用一个统一的空间坐标,每个物体在这个坐标中都有一个自己的相对于此坐标原点的确定坐标位置,这个坐标空间就是World space(世界坐标空间)。
从Object space到World space的转换过程由一个四阶矩阵控制,称之为world matrix。
光照计算通常在World space中进行的,当然也可以在Eye space中得到相同的光照效果,因为,在同一观察空间中物体之间的相对关系是保持不变的。
注意:顶点法向量是在模型文件中属于Object space,在GPU的顶点程序中,必须将法向量转换到world space中才能使用,如同必须将顶点坐标从object space转到world space中一样,但是两者的转换矩阵是不同的,法向量从object space到world space的转换矩阵是world matrix的转置矩阵的逆矩阵。
三、World space转换到Eye space:
在计算机中,每次只能从唯一的视角触发渲染物体。在游戏中,引入了视点漫游的功能,屏幕中显示的内容随着视点的变化而变化。这些都是因为GPU将物体顶点坐标从world space转换到了eye space。
Eye space(视点坐标空间),即以Camera(视点或相机)为原点,由视线方向、视角和远近平面,共同组成的一个梯形体的三维空间,称之为viewing frustum(视锥体),如下图所示:
远近平面:近平面,是梯形体中较小的矩形面,作为投影平面;远平面,是梯形体中较大的矩形。在这个梯形体中的所有顶点数据都是可见的,而超出这个梯形体之外的场景数据,会被视点去除(Frustum Culling,也称为视锥裁剪)。
四、Eye space转换到Clip and Project space:
一旦顶点坐标被转换到eye space中,就需要判断哪些点是视点可见的。位于视锥体以内的顶点为可见,以外的则为不可见的,会被视点去除。这个步骤就是clip(裁剪),识别指定区域内或者区域外的图形部分的过程称之为裁剪算法。
裁剪算法包括:视域裁剪(View Frustum Culling)、背面剔除(Back-Face Culling)、遮挡剔除(Occlusing Culling)和视口裁剪等。
裁剪的具体实现是在一个单位立方体中进行的,该立方体的对角顶点分别是(-1,-1,-1)和(1,1,1),通常称这个单位立方体为规范立方体(CVV)。CVV的近平面(梯形体中较小的矩形面)的XY坐标对应屏幕像素坐标(左下角为0,0),Z坐标则代表画面像素深度。多边形的裁剪就是在CVV中完成的。
所以,从视点坐标空间到屏幕坐标空间的操作步骤有三个:
1.用透视变换矩阵把顶点从视锥体中变换到裁剪空间的CVV中;
2.在CVV中进行图元裁剪;
3.屏幕映射:将经过上述过程的到的坐标映射到屏幕坐标系上。
只有当图元完全或者部分存在于视锥体内部时,才需要将去光栅化。当一个图元完全位于视体内部时,直接进入下一个步骤;完全位于视体外的图元,将被剔除;部分位于视体内的图元,将进行裁剪。
五、Primitive Assembly && Triangle setup
1.Primitive Assembly:
图元装配,即将顶点根据primitive(原始的连接关系),还原出网格结构。网格由顶点和索引组成,之前的操作都是对顶点的处理,在此步骤,根据索引将顶点连起来,组成线或面单元。之后就是对超出屏幕外的三角形进行裁剪,想象一下:一个三角形其中一个顶点在画面外,另外两个顶点在画面内,这是我们在屏幕上看到的就是一个四边形。然后将该四边形切成两个小的三角形。
此外还有一个操作涉及到三角形的顶点顺序 (其实也就是三角形的法向量朝向),根据右手定则来决定三角面片的法向量,如果该法向量朝向视点(法向量与到视点的方向的点积为正) ,则该面是正面。一般是顶点按照逆时针排列。如果该面是反面,则进行背面去除操作(Back-face Culling)。在 OpenGL 中有专门的函数 enable 和 disable 背面去除操作。所有的裁剪剔除计算都是为了减少需要绘制的顶点个数。
2.Triangle setup:
处理三角形的过程称为Triangle Setup,到这一步操作之后,我们得到一堆在屏幕坐标上的三角面片,这些面片是用于光栅化的。