|

楼主 |
发表于 2006-2-23 08:24:00
|
显示全部楼层
第十三讲: 传奇中应用到的dx的基础知识
D3D世界的转换
只要计算机以二维的屏幕作为显示介质,那么不论它显示的图像多么有立体感,也仍然是二维的。既然显示器的屏幕是二维的,那么我们就要对图像作些处理,让它可以欺骗我们的眼睛,产生一种立体的真实感。在D3D中,这种处理是使用一系列的空间变换的过程:从模型空间变换到世界空间,从世界空间变换到视图空间,再从视图空间变换到投影空间最后显示在显示器屏幕上。
•模型空间、世界空间与世界矩阵
什么是模型空间呢?每个模型(3D物体)都有占有它自己的空间,这种以某个特定的模型为中心的空间称为模型空间,某一个模型空间的的中心(原点)就是该模型的中心。在模型空间里,模型上的不同点有位置的相对关系,不同的模型在某个模型空间里的位置是相对的。而世界空间是设定的一种物体(模型)所存在的绝对的空间。当我们把一个模型放进世界空间里去,那么它就有了一个绝对的世界坐标,这个世界坐标是用来标记世界中不同的模型所处的位置的。在世界空间里,世界的中心就是原点(0, 0, 0),一般是你显示器屏幕中间的那一点,当然我们可以设定世界的中点在任何位置。我们可以在世界空间里摆放很多个模型,并且设置它们在世界空间中的坐标,这样模型与模型之间就有了相对的位置。
世界矩阵就是世界空间里的一个子空间的矩阵表示,矩阵中的点阵就是世界空间里的点阵.我们可以利用它来定义模型上各点在世界空间里的坐标。这样,在世界空间里面的这个世界矩阵的移动、旋转、和缩放就对应为世界中的模型的移动、旋转和缩放了。
D3D提供了产生世界矩阵和进行矩阵转换的函数。例如产生一个绕X轴旋转的转阵:D3DXMatrixRotationX(&matrix,1)。利用matrix这个矩阵,就可以使世界空间中的物体绕X轴转动1弧度。
•视图空间与视图矩阵
世界空间建立起来后,里面建立的模型仅仅只是世界空间内的一种存在,没有透视关系,这种数据并不能投影到二维的投影空间产生屏幕上“立体”的视觉,如果直接将世界空间转换到二维的投影空间,输出到屏幕的画面将类似与机械制图里的正投影视图。为了能产生与我们的视觉相应的“立体”效果,我们需要将世界空间转换到视图空间的技术,在D3D里我们可以通过在世界空间里建立我们的眼睛--摄像机来实现这种转换,我们就是通过这个虚拟的摄像机来观察世界空间中的模型的,所以视图空间也叫摄像机空间。
要建立起这个虚拟的摄像机,我们需要一个视图矩阵,产生视图矩阵的一个函数是:
D3DXMATRIX *D3DXMatrixLookAtLH (
D3DXMATRIX* pOut, //返回的视图矩阵指针
CONST D3DXVECTOR3* pEye, //设置摄像机的位置
CONST D3DXVECTOR3* pAt, //设置摄像机的观察方向
CONST D3DXVECTOR3* pUp ); //设置摄像机的“上”方向
这个函数的后缀LH是表示左手系的意思,还有个右手系的叫D3DXMatrixLookAtRH的函数。视图矩阵其实就是在定义了摄像机在世界空间中的位置、观察点、和设置摄像机的 “上”方向后,原来的世界空间转换到设定的摄像机空间(即透视空间)后的信息,这些信息保存在pOut所指向的矩阵中,这个矩阵就称为视图矩阵或摄像机矩阵。
•投影空间与投影矩阵
有了透视空间,如何获取我们所看到的世界空间的模型的视觉信息呢?这类似于我们在真实的世界里照相,物体存在于真实的世界里,通过我们的眼睛我们看到一个具有透视效果的透视世界,如果我们调整我们的照相机所取的景物,我们就能获得一张与我们的视觉感受相符的真实世界的某个角度的相片。定义投影矩阵很像是定义摄像机的镜头,下面看它的函数声明:
D3DXMATRIX *D3DXMatrixPerspectiveFovLH(
D3DXMATRIX* pOut,
FLOAT fovY,
FLOAT Aspect,
FLOAT zn,
FLOAT zf
);
pOut:返回的投影矩阵指针
fovY:定义镜头垂直观察范围,以弧度为单位。如果定义为D3DX_PI/4(90度角),那么就是表示以摄像机的观察方向为平分线,上方45度角和下方45度角就是摄像机在垂直方向所能看到的范围了。
Aspect:设置纵横比。如果定义为1,那么所看到的物体大小不变。如果定义为其它值,你所看到的物体就会变形。不过一般情况下这个值设为显示器屏幕的长宽比。
zn:设置摄像机所能观察到的最近距离
zf:设置摄像机所能观察到的最远距离
请看以下代码片段:
D3DXMATRIXA16 matWorld;
D3DXMatrixIdentity( &matWorld );
D3DXMatrixRotationX( &matWorld, timeGetTime()/1000.0f );
g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f );
D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
D3DXMATRIXA16 matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/2, 1.0f, 1.0f, 500.0f );
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
经由这三个转换,我们就建立了一个可以通过显示器屏幕来观察的3D世界;
从模型空间到世界空间的世界转换:SetTransform( D3DTS_WORLD, &matWorld )。
从世界空间到视图空间的视图转换:SetTransform( D3DTS_VIEW, &matView )。
从视图空间到到屏幕的投影转换:SetTransform( D3DTS_PROJECTION, &matProj )。
现在来观察matWorld,matView,matProj这三个矩阵的特点。我们使用D3DXMatrixRotationX函数来产生了一个绕X轴旋转的转换矩阵,通过设置世界转换,在世界空间里面的物体将绕X轴作旋转。然后我们定义了三个三维的向量,用来设置摄像机的位置,观察方向和定义方向“上”。使用D3DXMatrixLookAtLH函数来把这三个向量放进视图矩阵里面去。然后通过设置视图转换,我们就建立了一个虚拟的摄像机。最后通D3DXMatrixPerspectiveFovLH函数,我们得到一个投影矩阵,用来设置虚拟摄像机的镜头。
所谓摄像机的“上”方向其实指的就是摄像机在刚建立的时候是如何摆放的,是向左边侧着摆,还是向右边侧着摆,还是倒过来摆,都是通过这个方向“上”来指定的。按照正常的理解,摄像机的“上”方向就是Y轴的正方向,但是我们可以指定方向“上”为Y轴的负方向,这样世界建立起来后就是颠倒的了。
设置上面三个转换的先后顺序并不一定得按照世界到视图到投影这个顺序,不过习惯上按照这种顺序来写,感觉会好一点。
•使用矩阵相乘来创建新的世界矩阵
在世界空间中的物体运动往往是很复杂的,比如物体自身旋转的同时,还绕世界的原点旋转。怎么实现这种运动呢?通过矩阵相乘来把两个矩阵“混”在一起。现在我们假设某一物体建立在世界的原点上,看以下代码:
//定义三个矩阵
D3DXMATRIX matWorld, matWorldY,matMoveLeft;
//一个矩阵把物体移到(30,0,0)处,一个矩阵使物体绕原点(0,0,0)旋转
D3DXMatrixTranslation(&matMoveRight,30,0,0);
D3DXMatrixRotationY(&matWorldY, radian/1000.0f);
//第一次矩阵相乘。先旋转,再平移
D3DXMatrixMultiply(&matWorld, &matWorldY, &matMoveRight);
//第二次矩阵相乘。在第一次矩阵相乘的结果上,再以Y轴旋转
D3DXMatrixMultiply(&matWorld, &matWorld, &matWorldY);
//设置世界矩阵
m_pD3DDevice->SetTransform( D3DTS_WORLD, &matWorld );
矩阵相乘的时候,矩阵的先后顺序很重要,如果顺序弄错了,物体就不会按我们预料的那样运动。我们从最后一次矩阵相乘看起,最后相乘的两个矩阵是matWorld和matWorldY,其中matWorld又是由matWorldY和matMoveRight相乘得来的,那么这三个矩阵相乘的顺序就是(matWorldY,matMoveRight,matWorldY)。这个顺序意味着什么呢?第一个matWorldY使物体绕Y轴旋转,这时候的物体还处于原点,所以它绕Y轴旋转也就是绕自身的旋转。它转呀转呀,这时候matMoveRight来了,它把物体从(0,0,0)移到了(30,0,0),这时候物体就不再是绕Y轴旋转了,它是在(30,0,0)这个位置继续绕自身旋转。然后matWorldY又来了,它使物体再次以Y轴旋转,不过此时物体不在原点了,所以物体就以原点为中心作画圆的运动(它自身的旋转仍在继续),这个圆的半径是30。如果换一个顺序,把matMoveRight放在第一的话,那么就是先移动再旋转再旋转(第二次旋转没用),这时候物体就只是画圆运动而已,它自身没有旋转。如果把matMoveRight放在最后,那么就是先旋转再旋转(第二次旋转没用)再移动,这时候物体就没有作画圆运动了,它只是在(30,0,0)这个位置上作自身旋转。好了,理解这个需要一点点想象力。你可以先写好几个矩阵相乘的顺序,自己想象一下相乘的结果会使物体作什么运动,然后再编译执行程序,看看物体的运动是不是和自己想像中的一样,这样可以锻炼自己的空间思维能力。
|
|