绘制流水线(2)
2.3 渲染管线
一旦我们描述几何学上的3D场景和设置了虚拟照相机,我们要把这个场景转换成2D图象显示在显示器上。这一系列必须完成的操作就叫做渲染管线。图2.7展示了一个简化的渲染管线,随后将详细解释图中的每一部分。
渲染管线中的许多级都是从一个坐标系到另一个坐标的几何变换。这些变换都通过矩阵变换来实现。Direct3D为我们进行变换计算并且如果显卡支持硬件变换的话那就更有利了。使用Direct3D进行矩阵变换,我们唯一要做的事就是提供从一个系统变换到另一个系统的变换矩阵就可以了。我们使用IDirect3DDevice9::SetTranform方法提供变换矩阵。它输入一个表示变换类型的参数和一个变换矩阵。如图2.7所示,为了进行一个从自身坐标系到世界坐标系的变换,我们可以这样写:
Device->SetTransform(D3DTS_WORLD, &worldMatrix);
2.3.1自身坐标系(Local Space)
自身坐标系又叫做建模空间,这是我们定义物体的三角形列的坐标系。自身坐标系简化了建模的过程。在物体自己的坐标系中建模比在世界坐标系中直接建模更容易。例如,在自身坐标系中建模不像在世界坐标系中要考虑本物体相对于其他物体的位置、大小、方向关系。图 2.8所示是一个在自身局部坐标系中定义的茶壶。
2.3.2世界坐标系(World Space)
一旦我们构造了各种模型,它们都在自己的自身坐标系中,但是我们需要把它们都放到同一个世界坐标系中。物体从自身坐标系到世界坐标系中的变换叫做世界变换。世界变换通常是用平移、旋转、缩放操作来设置模型在世界坐标系中的位置、大小、方向。世界变换就是通过各物体在世界坐标系中的位置、大小和方向等相互之间的关系来建立所有物体。图2.9所示是相对于世界坐标系描述的几个3D物体。
世界变换由一个矩阵表示,并且在Direct3D中调用IDirect3DDevice9::SetTransform方法设置它,记住将转换类型设为D3DTS_WORLD。例如我们要在世界坐标系中放置一个立方体定位在(-3,2,6)和一个球体定位在(5,0,-2),我们可以这样写程序:
//创建立方体的世界矩阵(一个平移矩阵)
D3DXMATRIX cubeWorldMatrix;
D3DXMatrixTranslation(&cubeWorldMatrix, -3.0f, 2.0f, 6.0f);
//创建球体的世界矩阵(一个平移矩阵)
D3DXMATRIX sphereWorldMatrix;
D3DXMatrixTranslation(&sphereWorldMatrix, 5.0f, 0.0f, -2.0f);
// 变换立方体,然后绘制它
Device->SetTransform(D3DTS_WORLD, &cubeWorldMatrix);
drawCube(); // draw the cube
// 因为球体使用一个不同的世界变换,我们必须更改世界矩阵为球体的,
// 如果不更改,球体将绘制在上一个世界矩阵的位置上(立方体的世界矩阵)
Device->SetTransform(D3DTS_WORLD, &sphereWorldMatrix);
drawSphere(); // 绘制球体
这是个非常简单的实例,没有用到矩阵的旋转和缩放。但是一般很多物体都需要进行这些变换,不过这个例子也还是展示了世界变换是怎样进行的。
2.3.3视图坐标系(View Space)
世界坐标系中的几何图与照相机是相对于世界坐标系而定义的,如图2.10所示。然而在世界坐标系中当照相机是任意放置和定向时,投影和其它一些操作会变得困难或低效。为了使事情变得更简单,我们将照相机平移变换到世界坐标系的原点并把它的方向旋转至朝向Z轴的正方向,当然,世界坐标系中的所有物体都将随着照相机的变换而做相同的变换。这个变换就叫做视图坐标系变换(view space transformation)。
视图坐标的变换矩阵可以通过如下的D3DX函数计算得到:
D3DXMATRIX *D3DXMatrixLookAtLH(
D3DXMATRIX* pOut, // 指向返回的视图矩阵
CONST D3DXVECTOR3* pEye, // 照相机在世界坐标系的位置
CONST D3DXVECTOR3* pAt, // 照相机在世界坐标系的目标点
CONST D3DXVECTOR3* pUp // 世界坐标系的上方向(0, 1, 0)
);
pEye参数指定照相机在世界坐标系中的位置,pAt参数指定照相机所观察的世界坐标系中的一个目标点,pUp参数指定3D世界中的上方向,通常设Y轴正方向为上方向,即取值为(0,1,0)。
例如:假设我们要把照相机放在点(5,3,-10),并且目标点为世界坐标系的中点(0,0,0),我们可以这样获得视图坐标系变换矩阵:
D3DXVECTOR3 position(5.0f, 3.0f, –10.0f);
D3DXVECTOR3 targetPoint(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 worldUp(0.0f, 1.0f, 0.0f);
D3DXMATRIX V;
D3DXMatrixLookAtLH(&V, &position, &targetPoint, &worldUp);
视图坐标系变换也是通过IDirect3DDevice9::SetTransform来实现的,只是要将变换类型设为D3DTS_VIEW,如下所示:
Device->SetTransform(D3DTS_VIEW, &V);
2.3.4背面消除(Backface Culling)
一个多边形有两个表面,我们将一个标为正面,一个为背面。通常,后表面总是不可见的,这是因为场景中大多数物体是密封的。例如盒子、圆柱体、箱子、角色等,并且我们也不能把照相机放入物体的内部。因此照相机永不可能看到多边形的背面。这是很重要的,如果我们能看背面,那么背面拣选就不可能工作。
图2.11表示了一个物体在视图坐标系中的正面。一个多边形的边都是面向照相机叫正面多边形,而一个多边形的边都背对照相机叫背面多边形。
由图2.11可知,正面多边形挡住了在它后面的背面多边形,Direct3D将通过消除(即删除多余的处理过程)背面多边形来提高效率,这种方法就叫背面拣选。图2.12展示了背面拣选之后的多边形,从照相机的观察点来看,仍将绘制相同的场景到后备表面,那些被遮住的部分无论如何都永远不会被看见的。
当然,为了完成这项工作,Direct3D需要知道哪个多边形是正面,哪个是背面。Direct3D中默认顶点以顺时针方向(在观察坐标系中)形成的三角形为正面,以逆时针方向形成的三角形为背面。
如果我们不想使用默认绘制状态,我们可以通过改变D3DRS_CULLMODE来改变渲染状态:
Device->SetRenderState(D3DRS_CULLMODE, Value);
Value可以是如下一个值:
D3DCULL_NONE——完全不使用背面消除
D3DCULL_CW——消除顺时针方向环绕的三角形
D3DCULL_CCW——消除逆时针方向环绕的三角形,这是默认值。