绘制流水线

顶点结构与顶点格式

在Direct3D中,顶点除了包含空间信息外,还可以包含其他的附加属性,例如顶点可以有颜色属性,也可以有法线属性,Direct3D赋予了我们自定义顶点格式的自由,允许我们定义顶点的各分量。
为创建自定义的顶点格式,我们首先需要创建一个包含了我们希望具有的顶点数据的结构体,下面列举俩种顶点格式,第一种包含位置和颜色属性,第二种包含位置、法线以及纹理坐标。

struct ColorVertex
{
	float _x, _y, _z;
	DWORD _color;
};

struct NormalTexVertex
{
	float _x, _y, _z;
	float _nx, _ny, _nz;
	float _u, _v;
};

顶点结构定义好之后,就需要用灵活顶点格式(Flexible Vertex Format,FVF)标记的组合来描述顶点的组织结构,定义灵活顶点格式时,必须遵循的一个约定是:灵活顶点格式标记的指定顺序必须与顶点结构中相应类型数据定义的顺序保持一致

#define FVF_COLOR (D3DFVF_XYZ | D3DFVF_DIFFUSE)  //包含了位置属性和漫反射颜色属性
#define FVF_NORMAL_TEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1) //包含了位置、法线以及纹理坐标属性

常用的顶点格式
D3DFVF_XYZ:包含未经过坐标变换的顶点坐标值,不可以和D3DFVF_XYZRHW一起使用
D3DFVF_XYZRHW:包含经过坐标变换的顶点坐标值,不可以和D3DFVF_XYZ与D3DFVF_NORMAL一起使用
D3DFVF_DIFFUSE:包含漫反射的颜色值
D3DFVF_SPECULAR:包含镜面反射的数值
D3DFVF_NORMAL:包含法线向量的数值
D3DFVF_TEX1-TEX8:表示包含1~8个纹理坐标信息,是几重纹理后缀就用几,最多8层纹理

索引

通常构成一个3D物体的众多三角形单元之间会共享许多公共顶点,如果模型的细节和复杂度增加时,出现重合的顶点数量会急剧增加,为了解决该问题,我们引入了索引的概念,原理如下,我们创建了一个顶点列表和一个索引列表,顶点列表包含了全部独立顶点,索引列表包含了指向顶点列表的索引,这些索引规定了为创建三角形单元,各顶点应按照何种方式来组织。


以该图为例,顶点列表和索引列表可以这样创建,借助索引列表来定义顶点列表中顶点的组织方式以构成俩个三角形单元

	Vertex vertexList[4] = { v0,v1,v2,v3 };
	WORD indexList[6] = { 0,1,2,  //triangle0
						  0,2,3 };//triangle1

 上面对inexList的定义是说:用顶点列表中索引为0、1、2(vertexList[0]、vertexList[1]、vertexList[2])的元素构建triangle0(三角形单元0),用顶点列表中索引为0、2、3(vertexList[0]、vertexList[2]、vertexList[3])的元素来构建triangle1(三角形单元1)

虚拟摄像机

摄像机指定了场景对观察者的可见部分,即我们将依据哪部分3D场景来创建2D图像,摄像机有一定的位置和方向,定义了可见的空间体积,上述的空间体积是一个平截头体,在3D图形学中可称之为视域体或视截体,采用视域体的主要原因是显示屏为矩形,那些位于视域体之外的物体是不可见的,在进一步处理时就应该将其丢弃,丢弃这类数据的运算过程称为裁剪。投影窗口是一个2D区域,位于视域体中的3D几何体通过投影映射到该区域中,便创建了3D场景的2D表示,很重要的一点是,矩形投影窗口在x和y方向坐标的最小值是min=(-1,-1),最大值是max=(1,1)

 绘制流水线

绘制流水线的功能:在给定3D场景和指定观察方向的虚拟摄像机的几何描述时,创建一幅2D图像

一旦建立了3D场景的几何描述,并设置到虚拟摄像机,下面的任务就是在显示器中建立该场景的2D表示,为实现这一目标所必须实施的运算称为绘制流水线。

局部坐标系

 局部坐标系是用于定义构成物体的三角形单元列表的坐标系

 世界坐标系

构建各种物体模型时,每个模型都位于自身的局部坐标系中,我们还需要将将这些物体组织在一起构成世界坐标系中的场景,位于局部坐标系中的物体通过世界变换的运算变换到世界坐标系中,世界变换用一个矩阵表示,并由Direct3D通过IDirect3DDevice9::SetTransform方法来加以应用,该方法第一个参数为变换类型,若为世界变换可设为D3DTS_WOLRD,第二个参数 所采用的世界变换矩阵。

例:让一个立方体的中心位于世界坐标系中的点(-3,2,6)上

D3DXMATRIX cubeWorldMatrix;
D3DXMatrixTranslation(&cubeWorldMatrix, -3.0f, 2.0f, 6.0f);
Device->SetTransform(D3DTS_WORLD, &cubeWorldMatrix);
drawCube();			//画立方体实现

观察者坐标系

世界空间中,几何体和摄像机都是相对世界坐标系定义的,当摄像机的位置和朝向任意时,投影变换及其他类型的变换就略显困难或效率不高,为了简化运算,我们将摄像机变换至世界坐标系原点,并将其旋转,使摄像机的光轴世界坐标系z轴方向一致,同时,世界空间中的所有几何体都随着摄像机一同进行变换,以保证摄像机的视场恒定,这种变换称为取景变换,我们称变换后的几何体位于观察坐标系中。

取景变换矩阵(观察矩阵)可由D3DXMatrixLookAtLH计算得到

D3DXMATRIX* WINAPI D3DXMatrixLookAtLH(
	D3DXMATRIX *pOut, 
	CONST D3DXVECTOR3 *pEye,		//摄像机在世界坐标系中的位置 
	CONST D3DXVECTOR3 *pAt,			//世界坐标系中的被观察点
	CONST D3DXVECTOR3 *pUp			//世界坐标系中表示向上方向的向量
);

例:摄像机为(5,3,-10),观察点为世界坐标系原点,可以这样创建取景变换矩阵,取景变换使用SetTransform方法设定时,对应变换类型需指定为D3DTS_VIEW

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);
Device->SetTransform(D3DTS_VIEW, &v);

 背面消隐

每个多边形都有俩个侧面,分为正面和背面,由于场景中多数物体都是封闭体,像盒子、桶等,所以通常背面是不可见的,如果某一个多边形的背面可见,则背面消隐就失去了意义。如图正面朝向摄像机的多边形称为正面朝向多边形,正面偏离摄像机的多边形称为背面朝向多边形,可以看到正面朝向多边形遮挡了位于其后的背面朝向多边形,Direct3D正是利用了这一点将背面朝向多边形剔除,这称为背面消隐

 为了实现背面消隐,Direct3D需要区分哪些多边形是正面朝向哪些是背面朝向,默认状态下,Direct3D认为在观察坐标系中顶点排序顺序为顺时针的三角形单元是正面朝向的,顶点排列顺序为逆时针的三角形单元是背面朝向的。如果由于某些原因默认的消隐方式不能满足应用要求,可通过修改绘制状态D3DRS_CULLMODE来达到目的,value为对应枚举取值。

Device->SetRenderState(D3DRS_CULLMODE, Value);
D3DCULL_NONE  //完全禁用背面消隐
D3DCULL_CW	  //只对顺时针绕序的三角形单元进行消隐
D3DCULL_CCW   //默认值 只对逆时针绕序的三角形单元进行消隐

 光照

光源在世界坐标系中定义,但必须经取景变换至观察坐标系方可使用,观察坐标系中,光源照亮了场景中的物体,从而获得较为逼真的显示效果。

裁剪

将那些位于视域体外的几何体剔除掉,这个过程称为裁剪,一个三角形单元与视域体的相对位置关系有3种

1.完全在内部:被保留并转向下一阶段的处理
2.完全在外部:被剔除
3.部分在内部分在外:内部被保留,外部剔除

投影

观察坐标系中,我们的任务是获取3D场景的2D表示,从n维变换为n-1维的过程称为投影,实现投影的方式有多种,但是我们只对透视投影感兴趣,透视投影会产生"透视缩短"的视觉效果,即近大远小,这类投影使得我们可以用2D图像表示3D场景。投影变换定义了视域体,并负责将视域体中的几何体投影到投影窗口中。投影窗口与平面z=1重合

可以使用D3DXMatrixPerspectiveFovLH函数依据视域体的描述信息创建一个投影矩阵,值的一提的是纵横比参数,投影窗口中的几何体最终变换到屏蔽显示区,从正方形(投影窗口)到矩形的显示屏的变换会导致拉伸畸变,所谓纵横比就是显示屏两维尺寸的比率(屏幕宽度/屏幕高度),常用于校正由方形到矩形的映射和引发的畸变。

	D3DXMATRIX* WINAPI D3DXMatrixPerspectiveFovLH(
		D3DXMATRIX *pOut,	
		FLOAT fovy,			//视域角
		FLOAT Aspect,		//纵横比
		FLOAT zn,			//近裁剪面到原点的距离
		FLOAT zf			//远裁剪面到原点的距离
	);

 投影矩阵的应用要用方法SetTransform来实现,对应变换类型参数需指定为D3DTS_PROJECTION,下面例子说明了如何根据视域体的描述参数来创建投影矩阵。

D3DXMATRIX proj;
D3DXMatrixPerspectiveFovLH(&proj, PI * 0.5f, (float)width / (float)height, 1.0f, 1000.0f);
Device->SetTransform(D3DTS_PROJECTION, &proj);

视口变换

视口变换的任务是将顶点坐标从投影窗口转换到屏幕的一个矩形区域中,该矩形区域称为视口,在游戏中,视口通常是整个矩形屏幕区域,但视口也可以是屏幕的一个子区域或客户区(如果程序运行在窗口模式下),矩形的视口是相对于窗口来描述的,因为视口总是处于窗口内部并且其位置要用窗口坐标来指定。

 在Direct3D中,视口用结构D3DVIEWPROT9来表示,该结构前4个数据成员定义了视口矩形相对于其父窗口的位置和大小,MinZ指定了深度缓存的最小深度值,MaxZ指定了深度缓存的最大深度值,Direct将缓存的深度范围设在[0,1]区间内,所以除非想要追求某种特效,MinZ和MaxZ应限制在指定区间内。

typedef struct _D3DVIEWPORT9 {
    DWORD       X;
    DWORD       Y;            /* Viewport Top left */
    DWORD       Width;
    DWORD       Height;       /* Viewport Dimensions */
    float       MinZ;         /* Min/max of clip Volume */
    float       MaxZ;
} D3DVIEWPORT9;
	D3DVIEWPORT9 vp = { 0,0,640,480,0,1 };
	Device->SetViewport(&vp);   //设置视口,完成视口变换

光栅化

顶点坐标变换为屏幕坐标后,我们就有了一个2D三角形单元的列表,光栅化的任务是为了绘制每个三角形单元,如何计算构成三角形单元的每个像素的颜色值,光栅化过程计算量非常大,我们应尽量借助专用图形卡的加速功能,光栅化的最终结果是显示在屏蔽上的一幅2D图像。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值