第06章 动画初步 ***p103-105 6.1.1世界变换 矩阵(Matrix) Direct3D将矩阵存储为D3DMATRIX结构中: typedef struct_D3DMATRIX { D3DVALUE _11, _12, _13, _14; D3DVALUE _21, _22, _23, _24; D3DVALUE _31, _32, _33, _34; D3DVALUE _41, _42, _43, _44; } D3DMATRIX 单位矩阵: 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 D3DMATRIX mat; mat._11 = mat._22 = mat._33 = mat._44 =1.0f; mat._12 = mat._13 = mat._14 =0.0f; mat._21 = mat._23 = mat._24=0.0f; mat._31 = mat._32 = mat._34=0.0f; mat._41 = mat._42 = mat._43=0.0f; 单位矩阵函数: D3DMATRIX *D3DXMatrixIdentity(D3DMATRIX *pOut); A11 A12 A13 0 A21 A22 A23 0 A31 A32 A33 0 A41 A42 A43 1 平移: 1 0 0 0 0 1 0 0 0 0 1 0 Tx Ty Tz 1 矩阵平移函数: D3DMATRIX *D3DXMatrixTranslation { D3DMATRIX *pOut, //输出矩阵 FLOAT x, //X坐标偏移量 FLOAT y, //Y坐标偏移量 FLOAT z, //Z坐标偏移量 } inline VOID D3DXMatrixTranslation(D3DXMATRIX* m, FLOAT tx, FLOAT ty, FLOAT tz) { D3DXMatrixIdentity(&m); m._41 =tx; m._42 = ty; m._43 = tz; } 绕X轴旋转矩阵: 1 0 0 0 0 cosθ sinθ 0 0 -sinθ cosθ0 0 0 0 1 伪代码实现: mat._22 = cosf(theta) mat._23 = sinf(theta) mat._32 = -sinf(theta) mat._33 = cosf(theta) D3DMATRIX *D3DXMatrixRotationX { D3DMATRIX *pOut, //输出矩阵 FLOAT Angle //X轴上的旋转弧度值 } inline VOID D3DXMatrixRotationX (D3DXMATRIX* mat, FLOAT fRads) { D3DXMatrixIdentity(mat); mat._22 = cosf(fRads); mat._23 = sinf(fRads); mat._32= -sinf(fRads); mat._33 = cosf(fRads); } 绕Y轴旋转矩阵: cosθ 0 -sinθ 0 0 1 0 0 sinθ 0 cosθ 0 0 0 0 1 伪代码实现: mat._11 = cosf(theta) mat._13 = -sinf(theta) mat._31 = sinf(theta) mat._33 = cosf(theta) inline VOID D3DXMatrixRotationY (D3DXMATRIX* mat, FLOAT fRads) { D3DXMatrixIdentity(mat); mat._11 = cosf(fRads); mat._13 = -sinf(fRads); mat._31 = sinf(fRads); mat._33 = cosf(fRads); } D3DMATRIX *D3DXMatrixRotationY { D3DMATRIX *pOut, //输出矩阵 FLOAT Angle //Y轴上的旋转弧度值 } 绕Z轴旋转矩阵: cosθsinθ 0 0 -sinθcosθ 0 0 0 0 1 0 0 0 0 1 伪代码实现: mat._11 = cosf(theta) mat._12 = sinf(theta) mat._21 = -sinf(theta) mat._22= cosf(theta) inline VOID D3DXMatrixRotationZ (D3DXMATRIX* mat, FLOAT fRads) { D3DXMatrixIdentity(mat); mat._11 = cosf(fRads); mat._12 = sinf(fRads); mat._21 = -sinf(fRads); mat._22 = cosf(fRads); } D3DMATRIX *D3DXMatrixRotationZ { D3DMATRIX *pOut, //输出矩阵 FLOAT Angle //Z轴上的旋转弧度值 } 缩放: Sx 0 0 0 0 Sy 0 0 0 0 Sz 0 0 0 0 1 矩阵缩放函数: D3DMATRIX *D3DXMatrixScaling { D3DMATRIX *pOut, //输出矩阵 FLOAT sx, //X缩放 FLOAT sy, //Y缩放 FLOAT sz //Z缩放 } D3DMATRIX* D3DXMatrixScaling (D3DXMATRIX* pOut, FLOAT sx, FLOAT sy, FLOAT sz) { pOut->_11 =sx; pOut->_22 =sy; pOut->_33 =sz; pOut->_44 =1; return pOut; } 将矩阵合并: D3DMATRIX *D3DXMatrixMultiply { D3DMATRIX *pOut, //输出矩阵 CONST D3DMATRIX *pM1, //源矩阵1 CONST D3DMATRIX *pM2 //源矩阵2 } D3DMATRIX *D3DXMatrixMultiply ( D3DMATRIX *pOut, CONST D3DMATRIX *pM1, CONST D3DMATRIX *pM2 ) { FLOAT pM[16]; ZeroMemory(pM, sizeof(D3DMATRIX)); for(WORD i=0; i<4; i++) for(WORD j=0; j<4; j++) for(WORD k=0; k<4; k++) pM[4*i+j] += pM1[4*i+k] * pM2[4*k+j]; memcpy(pOut, pM, sizeof(D3DXMATRIX)); return(pOut); } p77注意:使用旋转函数来旋转三维空间中的物体时,可能会导致丢失一维,这便是“万向节锁(gimbal lock)”现象。大多数游戏只用得“2.5维”就够了。对于万向节锁的一种解决方法是:通过向量来实现三维空间中物体的旋转、或使用四元数。 6.1.1观察变换 观察变换用于描述一个观察者在场景中的位置和朝向。 世界矩阵和观察矩阵的主要区别在于3个朝向向量的存储方式。世界矩阵以行的次序存储它们,而观察矩阵以列的次序存储。 世界坐标系和摄像机坐标系都是3维坐标系,但屏幕上显示的都是2维画面,因此需要进行3维坐标系道2维坐标系的转换。将3维坐标系变换为2维坐标系的一个最简便的方法就是去掉一个轴,例如,如果所有顶点的Z值都是0,只有X,Y值,3维坐标系也就自然而然地变成了2维坐标系,这种变换叫做:正交投影(Orhographic Projection),只适合CAD、CAM等非常有限的领域。事实上,现在常用的投影变换是透视投影(Perspective Projection),摄像机的距离不同,投影的比率也不同,这样可以表现出物体的距离感。 在3维空间绘制2个物体时,需要变换它们自己的局部坐标系,将它们引入同一个坐标系,也就是世界坐标系。将局部坐标系变换为世界坐标系的矩阵称为:变换矩阵(Transform Matrix),也可以缩略为TM。 设置世界坐标系变换:SetTransform(D3DTS_WORLD, &matWorld); 观察变换/摄像机变换:以摄像机为基准点,将世界坐标系变换为摄像机坐标系的过程 摄像机变换矩阵运算函数: D3DMatrix* D3DMatrixLookAtLH ( D3DMatrix* pout, //存放变换后矩阵的矩阵结构体 CONST D3DVECTOR3* pEye, //眼睛的位置,就是观察者在三维坐标系中的坐标; //观察点的位置坐标 CONST D3DVECTOR3* pAt, //观察的方向,这也是一个点的坐标值, //函数通过pEye参数表示的点坐标, //到pAt参数的点坐标方向来确定观察的方向。 //所以pAt参数可以是从观察点向观察方向上的任何一个点的坐标值 //被观察点的位置坐标 CONST D3DVECTOR3* pUp //虚拟相机(观察者)的向上向量,通常为(0,1,0) ) 确定物体的位置至少需要:摄像机位置的世界坐标、摄像机观察位置的世界坐标、摄像机的上方向量。 设置摄像机变换:SetTransform(D3DTS_VIEW, &matView); D3D渲染管道(Rendering Pipeline),表现了从最初的局部坐标系到最终坐标系的变换过程: 顶点-> 世界矩阵 ->摄像机矩阵->投影矩阵 ->变换后的顶点 局部坐标系->世界坐标系->视图坐标系->摄影坐标系->视图区坐标系 D3DXVec3Dot()计算两个3D向量的点积: FLOAT D3DXVec3Dot ( CONST D3DXVECTOR3 *pV1, //指向D3DXVECTOR3 结构的源向量 CONST D3DXVECTOR3 *pV2 //指向D3DXVECTOR3 结构的源向量 ); D3DXMatrixInverse()计算矩阵的逆矩阵: D3DXMATRIX *WINAPI D3DXMatrixInverse ( D3DXMATRIX *pOut, //指向D3DXMATRIX结构的逆矩阵 FLOAT *pDeterminant, //指向FLOAT 类型的对角线[1,1][2,2][3,3][4,4]积, //如果不需要,可以设置为 NULL。 CONST D3DXMATRIX *pM //指向 D3DXMATRIX 结构源矩阵 ); p88 D3DMatrixLookAtLH实现 p93代码分析 6.1.3投影变换 投影变换矩阵运算函数: D3DMATRIX *D3DXMatrixPerspectiveFovLH ( D3DMATRIX *pOut, //用来存放变换后矩阵的矩阵结构体 FLOAT fovY, //field of view,视域,视角,在y轴上的成像度,单位是弧度。 //指投影视野的宽度,这个数越大,能够看到的范围就越多 FLOAT Aspect, //截头体的纵横比 FLOAT zn, //近裁剪平面的Z(深度)值(near clipping plane), //可以观察到的三维世界的最近距离。 FLOAT zf //远裁剪平面的Z(深度)值(far clipping plane), //可以观察到的三维世界的最远距离。 ); 设置投影变换:SetTransform(D3DTS_PROJECTION, &matProj); 6.1.4使用视口 typedef struct _D3DVIEWPORT9 { DWORD X; //视口左上角X坐标,StartX DWORD Y; //视口左上角Y坐标,StartY DWORD Width; //视口宽度 DWORD Height; //视口高度,X、Y、Width、Height值对应于视口矩阵 float MinZ; //视口内景物的最小深度值,0.0f~1.0f之间,一般设为0.0f float MaxZ; //视口内景物的最大深度值,0.0f~1.0f之间,一般设为1.0f }D3DVIEWPORT9; 通常将MinZ和MaxZ分别设为0.0f和1.0f,这样可以渲染深度缓冲器中整个深度范围内的值。如果将这两个值都设为0.0f,则强制系统在前景中进行渲染。 HRESULT IDirect3DDevice9::SetViewport ( CONST D3DVIEWPORT9 *pViewport //存放新视口的地址 ); SetViewport()相当于把投影空间的顶点P(x,y,z,1)乘以矩阵Mviewport: Width/2 0 0 0 0 -Height/2 0 0 0 0 MaxZ-MinZ 0 StartX+Width/2 StartY+Height/2 MinZ 0 因此屏幕上P’(x’, y’)的坐标等于: x’ = x*Width/2 + StartX+Width/2; y’ =-y*Height/2 + StartY+Height/2; z’ =z*(MaxZ-MinZ) + MinZ; z’被用来进行裁剪 SetViewport()定义了窗口中的一个区域,随后绘图操作的结果都将显示在该矩阵区域内。如果一个新视口定义在设备边界之外,那么SetViewport()则会失败。请注意,视口矩阵的高宽比应与投影矩阵所定义的高宽比相匹配,否则物体看上去会像被挤压过一样。 HRESULT IDirect3DDevice9::GetViewport ( D3DVIEWPORT9 *pViewport //存放视口的地址 ); HRESULT IDirect3DDevice9::SetTransform { D3DTRANSFORMSTATETYPE State, //可取:D3DTRANSFORMSTATE_WORLD、 //D3DTRANSFORMSTATE_VIEW、 //D3DTRANSFORMSTATE_PROJECTION CONST D3DMATRIX *pMatrix //指向变换矩阵的内存地址 }; 绘制字符串: INT IDDXFont: rawText ( LPD3DXSPITE pSprite, //指定字符串所属的对象接口,默认值是0,表示在当前窗口绘制 LPCSTR pString, //指定将要绘制的字符串 INT Count, //指定绘制的个数,-1表示自动绘制到字符串结束为止 LPRECT pRect, //指定字符串绘制的矩形区域位置 DWORD Format, //在矩形区域的摆放属性 D3DCOLOR Color //指定字符串显示的颜色值,属于D3DCOLOR结构。 ) 6.1.5深度缓冲(Depth Buffering)p99 渲染场景最有效的方法是:只渲染那些可以被观察者看到的像素。如果渲染了那些看不到的像素(比如,房间里那些通过窗口无法看到的物体),那么这种多余的操作称为:覆绘(overdrawing)。 深度复杂度(depth complexity)是对场景中覆绘数量的一种度量值,它表示在所有渲染的像素中最终可见像素的比例。例如,一个场景的深度复杂度为3,则表示被渲染的像素数量是屏幕上实际可见像素数量的3倍。也就是说,在某种特定的帧速率情况下,需要以3倍于无覆绘的填充率来显示一个场景。覆绘事使3D游戏变得低效率的一个主要原因。 Direct3D深度缓冲器可以防止覆绘。 HRESULT Clear ( DWORD Count,//矩形数量 const D3DRECT *pRects,//矩形指针 DWORD Flags,//要清除的缓冲类型(Z缓冲器、渲染目标、模板缓冲器) float Z,//Z缓冲设置的值 DWORD Stencil //模板缓冲设置的值 ) 在一个透视观察环境中,世界空间的距离值与深度缓冲器中的值(Z值)不是成比例对应的。也就是说任意2个相邻的深度值之间的距离并不是相等的,其中较小的深度值间的距离比较相近些。 如果丢失了深度缓冲的精度,那么就极有可能会发生渲染错误。而深度精度取决于深度缓冲器的位数。不过这些问题通常只会发生在Z缓冲器中,解决该问题的方便就是选择更高精度的深度缓冲器格式,比如24位。 W缓冲器:减少了Z缓冲器在处理远距离物体时所遇到的问题,并且无论对于多远或多近的物体都具有稳定的执行性能。 一个基于W算法的深度缓冲器与Z缓冲器算法相比,它可以更均匀地分布于近裁剪面与远裁剪面之间。这样的好处就是解决了近/远裁剪面间的距离比例问题。使在仍能获得相对精确的深度缓冲的前提下,支持更大的深度范围。缺点是有时对于较近的物体会显露出表面被隐藏的视觉错误、不如Z缓冲那样广泛地为硬件所支持。 通过SetRenderState()函数可以切换为W缓冲器: m_pd3dDevice-> SetRenderState(D3DRS_ZENABLE, D3DZB_USEW); //w-buffer 使用下列代码可以检查是否支持W缓冲器: if(d3dCaps.RasterCaps & D3DPRASTERCAPS_WBUFFER) m_bDeviceSupportW = TRUE; else m_bDeviceSupportW = FALSE; SetRenderState() HRESULT IDirect3DDevice9::SetRenderState ( D3DRENDERSTATETYPE State, DWORD Value ); typedef enum D3DRENDERSTATETYPE { D3DRS_ZENABLE = 7, D3DRS_FILLMODE = 8, D3DRS_SHADEMODE = 9, D3DRS_ZWRITEENABLE = 14, D3DRS_ALPHATESTENABLE = 15, D3DRS_LASTPIXEL = 16, D3DRS_SRCBLEND = 19, D3DRS_DESTBLEND = 20, D3DRS_CULLMODE = 22, D3DRS_ZFUNC = 23, D3DRS_ALPHAREF = 24, D3DRS_ALPHAFUNC = 25, D3DRS_DITHERENABLE = 26, D3DRS_ALPHABLENDENABLE = 27, D3DRS_FOGENABLE = 28, D3DRS_SPECULARENABLE = 29, D3DRS_FOGCOLOR = 34, D3DRS_FOGTABLEMODE = 35, D3DRS_FOGSTART = 36, D3DRS_FOGEND = 37, D3DRS_FOGDENSITY = 38, D3DRS_RANGEFOGENABLE = 48, D3DRS_STENCILENABLE = 52, D3DRS_STENCILFAIL = 53, D3DRS_STENCILZFAIL = 54, D3DRS_STENCILPASS = 55, D3DRS_STENCILFUNC = 56, D3DRS_STENCILREF = 57, D3DRS_STENCILMASK = 58, D3DRS_STENCILWRITEMASK = 59, D3DRS_TEXTUREFACTOR = 60, D3DRS_WRAP0 = 128, D3DRS_WRAP1 = 129, D3DRS_WRAP2 = 130, D3DRS_WRAP3 = 131, D3DRS_WRAP4 = 132, D3DRS_WRAP5 = 133, D3DRS_WRAP6 = 134, D3DRS_WRAP7 = 135, D3DRS_CLIPPING = 136, D3DRS_LIGHTING = 137, D3DRS_AMBIENT = 139, D3DRS_FOGVERTEXMODE = 140, D3DRS_COLORVERTEX = 141, D3DRS_LOCALVIEWER = 142, D3DRS_NORMALIZENORMALS = 143, D3DRS_DIFFUSEMATERIALSOURCE = 145, D3DRS_SPECULARMATERIALSOURCE = 146, D3DRS_AMBIENTMATERIALSOURCE = 147, D3DRS_EMISSIVEMATERIALSOURCE = 148, D3DRS_VERTEXBLEND = 151, D3DRS_CLIPPLANEENABLE = 152, D3DRS_POINTSIZE = 154, D3DRS_POINTSIZE_MIN = 155, D3DRS_POINTSPRITEENABLE = 156, D3DRS_POINTSCALEENABLE = 157, D3DRS_POINTSCALE_A = 158, D3DRS_POINTSCALE_B = 159, D3DRS_POINTSCALE_C = 160, D3DRS_MULTISAMPLEANTIALIAS = 161, D3DRS_MULTISAMPLEMASK = 162, D3DRS_PATCHEDGESTYLE = 163, D3DRS_DEBUGMONITORTOKEN = 165, D3DRS_POINTSIZE_MAX = 166, D3DRS_INDEXEDVERTEXBLENDENABLE = 167, D3DRS_COLORWRITEENABLE = 168, D3DRS_TWEENFACTOR = 170, D3DRS_BLENDOP = 171, D3DRS_POSITIONDEGREE = 172, D3DRS_NORMALDEGREE = 173, D3DRS_SCISSORTESTENABLE = 174, D3DRS_SLOPESCALEDEPTHBIAS = 175, D3DRS_ANTIALIASEDLINEENABLE = 176, D3DRS_MINTESSELLATIONLEVEL = 178, D3DRS_MAXTESSELLATIONLEVEL = 179, D3DRS_ADAPTIVETESS_X = 180, D3DRS_ADAPTIVETESS_Y = 181, D3DRS_ADAPTIVETESS_Z = 182, D3DRS_ADAPTIVETESS_W = 183, D3DRS_ENABLEADAPTIVETESSELLATION = 184, D3DRS_TWOSIDEDSTENCILMODE = 185, D3DRS_CCW_STENCILFAIL = 186, D3DRS_CCW_STENCILZFAIL = 187, D3DRS_CCW_STENCILPASS = 188, D3DRS_CCW_STENCILFUNC = 189, D3DRS_COLORWRITEENABLE1 = 190, D3DRS_COLORWRITEENABLE2 = 191, D3DRS_COLORWRITEENABLE3 = 192, D3DRS_BLENDFACTOR = 193, D3DRS_SRGBWRITEENABLE = 194, D3DRS_DEPTHBIAS = 195, D3DRS_WRAP8 = 198, D3DRS_WRAP9 = 199, D3DRS_WRAP10 = 200, D3DRS_WRAP11 = 201, D3DRS_WRAP12 = 202, D3DRS_WRAP13 = 203, D3DRS_WRAP14 = 204, D3DRS_WRAP15 = 205, D3DRS_SEPARATEALPHABLENDENABLE = 206, D3DRS_SRCBLENDALPHA = 207, D3DRS_DESTBLENDALPHA = 208, D3DRS_BLENDOPALPHA = 209, D3DRS_FORCE_DWORD = 0x7fffffff, } D3DRENDERSTATETYPE, *LPD3DRENDERSTATETYPE; typedef enum D3DSHADEMODE { D3DSHADE_FLAT = 1, D3DSHADE_GOURAUD = 2, D3DSHADE_PHONG = 3, D3DSHADE_FORCE_DWORD = 0x7fffffff, } D3DSHADEMODE, *LPD3DSHADEMODE;
|