3D游戏从入门到精通-20

时间:2020-11-30 19:58:04
有了上面绕着坐标轴旋转的矩阵,就可以轻松地构造我们的太阳系了。在我们太阳系中,太阳是自转的,在这个例子里要就要让太阳绕着Y轴自转。在让太阳旋转之前,就要先创建太阳这个物体,在这里我采用一个大圆球代表太阳。创建太阳圆球的程序如下:
 
LPD3DXMESH m_pMeshSun;          //太阳。
D3DXMATRIX m_mSunSpin;          //自转矩阵
 
这里使用类成员变量m_pMeshSun保存太阳的三角形列表,类成员m_mSunSpin保存自转矩阵。然后调用D3DXCreateSphere函数来创建太阳,这个函数就是创建一个球体的三角形网格。它具体参数如下:
HRESULT D3DXCreateSphere(
 LPDIRECT3DDEVICE9 pDevice,
 FLOAT Radius,
 UINT Slices,
 UINT Stacks,
 LPD3DXMESH * ppMesh,
 LPD3DXBUFFER * ppAdjacency
);
第一个参数pDevice是D3D设备。第二个参数Radius是球半径长度。第三个参数是连接主轴两端的线条数,也就是经度线的条数。第四个参数是纬度线的条数。第五个参数是创建返回的球体三角形列表。第六个参数是ID3DXBuffer指针。最后如下面调用它:
 
//创建太阳。
HRESULT hr = D3DXCreateSphere(m_pd3dDevice, 1.0f, 30, 30, &m_pMeshSun, NULL);
if (hr != D3D_OK)
{
 return hr;
}
 
这里创建弧度为1.0,经度线是30条,纬度线是30条的球体。接着下来创建地球的模型,当然也是采用上面的球体,创建月亮也是一样的。代码如下:
 
//创建地球。
 hr = D3DXCreateSphere(m_pd3dDevice,1.0f,20,20,&m_pMeshEarth,NULL);
 if (hr != D3D_OK)
 {
       return hr;
 }
 
 //创建月亮.
 hr = D3DXCreateSphere(m_pd3dDevice,1.0f,10,10,&m_pMeshMoon,NULL);
 if (hr != D3D_OK)
 {
       return hr;
 }
 
这里创建地球和月亮是一样的大小,但它们的经度线和纬度线的条数是不一样。这样表现出来的结果是地球的三角形总数多于月亮的三角形总数,看起来就更加像圆形。由于后面会把月亮缩小,这样看起来就没有太大的差别了。
太阳、地球和月亮都已经创建好模型,接着下来的事情,就是怎么样让太阳自转,怎么样让地球绕着太阳转和自转,怎么样让月亮绕着地球和太阳转,以及它自己的自转。下面先来看看怎么实现地球自转:
 
//
 //显示太阳为自转。
 //  
 //缩放矩阵   
 D3DXMATRIX mSunScale;    
 D3DXMatrixScaling(&mSunScale,1.5,1.5,1.5);
 
 //转动90度。
 D3DXMATRIX mSunRotationX;
 D3DXMatrixRotationX(&mSunRotationX,D3DX_PI/2);
 
 //自转
 D3DXMATRIX mSunRotation;
 D3DXMatrixRotationY(&mSunRotation,fTime);
 m_mSunSpin *= mSunRotation;
 
 //组合所有矩阵。
 D3DXMATRIX mSun = mSunScale*mSunRotationX*m_mSunSpin;
 m_pd3dDevice->SetTransform( D3DTS_WORLD, &mSun );
 m_pMeshSun->DrawSubset(0);
 
这段代码不是很长,看下来很快的。由于上面创建的太阳模型太小了,那么有什么办法可以把太阳放大呢?当然是有的,前面已经学习过使用矩阵可以缩放模型,那就先拿来试用一下,是否真的可以缩放物体呢?调用D3DXMatrixScaling函数,并且在X,Y,Z轴都进行放在1.5倍,这样就构造好缩放矩阵mSunScale。又因为创建的太阳的主轴是跟Z轴平行的,那么在屏幕里看的就是主轴的方向。我想把它转到与Y轴平行,那么就需要绕着X轴旋转90度,这样就可以与Y轴平行了。要实现太阳的自转,也就是需要太阳绕着Y轴来旋转,因此就需要构造一个绕着Y轴转动的矩阵。调用函数D3DXMatrixRotationY,设置好返回矩阵mSunRotation,每次转动的弧度fTime,这样就可以构造出转动矩阵。然而这个矩阵只是每次转动的增量,那么怎么样才能实现持续地转动呢?这里采用保存旋转矩阵做法,m_mSunSpin成员变量就是保存每次转动后的变换矩阵,只要每次改变这个矩阵转动,就相当于太阳在自转了。m_mSunSpin矩阵与mSunRotation矩阵相乘,就是把太阳每次都转动一点。到这里,已经把所有变换的矩阵构造出来,但是还没有把所有变换组合到一起,其实非常简单,只要把前面所有矩阵按顺序地作乘法,就行了。但是要注意一点,就是矩阵的乘法不满足交换律,不能随便更换乘数的次序,否则结果就与想表达的内容大不一样了。矩阵的组合有很多优点,可以把许多变换组合到一个矩阵里,只作一次变换计算,就达到目标了。在程序后面,就是把这个复合矩阵mSun设置到世界变换矩阵里,这样调用DrawSubset显示出来的太阳,就达到自转的目的了。
 
 
接着下来,就需要计算地球的公转和自转。
//
 //显示地球的自转和公转。
 //
 D3DXMATRIX mEarthScale;  
 D3DXMatrixScaling(&mEarthScale,0.5f,0.5f,0.5f);
 
 //自转
 D3DXMATRIX mEarthRotation;
 D3DXMatrixRotationY(&mEarthRotation,1.5f*fTime);
 m_mEarthSpin *= mEarthRotation;
 
 //平移矩阵
 D3DXMATRIX mEarthTranslation;
 D3DXMatrixTranslation(&mEarthTranslation,0,0,5);
 
 //绕太阳公转矩阵
 D3DXMATRIX mEarthRotSun;
 D3DXMatrixRotationY(&mEarthRotSun,fTime);
 m_mEarthRotSun *= mEarthRotSun;
 
 
 D3DXMATRIX mEarth = mEarthScale*m_mEarthSpin*mEarthTranslation*m_mEarthRotSun;
 m_pd3dDevice->SetTransform( D3DTS_WORLD, &mEarth );
 m_pMeshEarth->DrawSubset(0);
 
由于地球的模型比较大,就得想办法缩小它,这样就需要使用D3D提供的缩放矩阵函数D3DXMatrixScaling。这个函数前面已经作了详细的介绍,这里是把地球模型各向同性地缩小一半。地球的大小解决了,那么地球的自转和公转是先计算那个先的呢?由于公转是绕着太阳转的,也就是说需要绕着世界坐标原点来转动。而自转只是绕着自己模型坐标系里的原点来转动,因此,就需要把自转放在前面,把公转放在后面计算。上面使用函数D3DXMatrixRotationY实现自转矩阵的计算,然后再这个矩阵旋转变换累加起来,而这个累加就是使用矩阵乘法来实现。最后m_mEarthSpin矩阵就是保存了从模型坐标系变换到世界坐标的旋转矩阵,这样就构造完了自转矩阵,其实是跟太阳的自转矩阵是一样的。
 
下面再来看看怎么样实现地球的公转?由于地球的位置是在世界坐标系的原点,而太阳的位置也是在世界坐标系里的原点,要想地球绕着太阳转,就需要把地球位置平移到另外一个位置上,也就是说地球到原点的距离要大于太阳半径与地球半径之和。在这里使用平移函数D3DXMatrixTranslation,并设置参数在Z轴的正方向上平移5个单位。接着再计算绕着原点旋转矩阵,在这里同样是采用函数D3DXMatrixRotationY来构造绕着Y轴旋转的矩阵mEarthRotSun,然后再累加到m_mEarthRotSun矩阵里。最后把缩放矩阵mEarthScale、地球自转矩阵m_mEarthSpin、地球平移矩阵mEarthTranslation、地球绕着太阳旋转矩阵m_mEarthRotSun作乘法运算,就相当把所有变换都计算了,再设置到世界坐标矩阵里,然后显示出来的地球,就会不断旋转和自转。在这里的变换一定搞清楚,如果矩阵顺序搞错了,就会显示不同的结果。
 
最后来看看怎么样实现更加复杂的月亮转动。实现代码如下:
 
 //
 //显示月亮。
 //
 D3DXMATRIX mMoonScale;  
 D3DXMatrixScaling(&mMoonScale,0.2f,0.2f,0.2f);
 
 //自转
 D3DXMATRIX mMoonRotation;
 D3DXMatrixRotationY(&mMoonRotation,3*fTime);
 m_mMoonSpin *= mMoonRotation;
 
 //离地球平移矩阵
 D3DXMATRIX mMoonTranslationEarth;
 D3DXMatrixTranslation(&mMoonTranslationEarth,0,0,1);
 
 
 //绕地球公转矩阵
 D3DXMATRIX mMoonRotEarth;
 D3DXMatrixRotationY(&mMoonRotEarth,2.5f*fTime);
 m_mMoonRotEarth *= mMoonRotEarth;
 
 //绕太阳公转矩阵
 m_mMoonRotSun *= mEarthRotSun;  
 
 
 D3DXMATRIX mMoon = mMoonScale*m_mMoonSpin*mMoonTranslationEarth*m_mMoonRotEarth;
 mMoon *= mEarthTranslation*m_mMoonRotSun;
 m_pd3dDevice->SetTransform( D3DTS_WORLD, &mMoon );
 m_pMeshMoon->DrawSubset(0);
 
这里跟地球的矩阵变换有很大一部分是相同的,由于月亮比地球小,因此就需要把月亮模型缩得比地球还要小一点。使用函数D3DXMatrixScaling各向性地缩放0.2倍,而地球是0.5倍,这样月亮就比地球小一半以上。接着下来就是计算月亮的自转矩阵m_mMoonSpin,后面接着计算绕着地球旋转矩阵m_mMoonRotEarth,而月亮是跟地球一起绕着太阳公转。因此采用地球绕着太阳旋转的矩阵来计算月亮的公转矩阵m_mMoonRotSun。但这里还需要注意一点的,就是月亮同样跟着地球作一个平移运算。在最后的组合矩阵变换时,就是月亮的缩放矩阵mMoonScale、自转矩阵m_mMoonSpin、月亮与地球的平移矩阵mMoonTranslationEarth、月亮与地球公转矩阵m_mMoonRotEarth、地球与太阳的平移矩阵mEarthTranslation、月亮绕着太阳公转矩阵m_mMoonRotSun。通过这样一系列的变换,就可以实现月亮运行。
 
从上面看来,采用矩阵变换是很方便的。可以把多种变换组合起来,然后再计算模型顶点的位置,简化了计算量,同样也有利于思考问题。通过这个例子,已经把矩阵变换搞得很清楚了,也把它们应用到各个地方了。最后实现的图如下所示:
3D游戏从入门到精通-20
 
电子书 MM3D 引擎源程序 例子源程序 49 元一套
联系人:蔡军生 
联系方式:
QQ: 9073204
EMAIL: caimouse1976 at sina.com