D3DMATRIX matWorld = mat;
pd3dDevice->SetTransform( D3DTRANSFORMSTATE_WORLD, &matWorld );
// 视图矩阵定义了照相机的位置和方向,这里只是将其沿z轴向后移动了10
// 个单位
D3DMATRIX matView = mat;
matView._43 = 10.0f;
pd3dDevice->SetTransform( D3DTRANSFORMSTATE_VIEW, &matView );
// 投影矩阵定义了怎样将3-D场景投影到2-D绘制目标表面上
// 设置一个非常简单的投影,x和y的单位为2,用 -1.0来翻译z
D3DMATRIX matProj = mat;
matProj._11 = 2.0f;
matProj._22 = 2.0f;
matProj._34 = 1.0f;
matProj._43 = -1.0f;
matProj._44 = 0.0f;
pd3dDevice->SetTransform( D3DTRANSFORMSTATE_PROJECTION, &matProj );
当设置了转换之后,Triangle就完成了设置场景的任务。应用程序定义的App_InitDeviceObjects函数向调用者返回S_OK。然后,Initialize3DEnvironment函数向WinMain返回该值,WinMain继续处理系统消息。
4. 监视系统消息
在创建了应用程序窗口以后创建了DirectX对象,然后初始化了场景,接着就可以绘制场景了。在大多数情况下,Windows应用程序在其消息循环中监视系统消息,并在消息队列为空时绘制帧。Triangle例子程序没有不同,它在其消息循环中使用了下列代码:
BOOL bGotMsg;
MSG msg;
PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
g_bReady = TRUE;
while( WM_QUIT != msg.message )
{
// 在应用程序处于活动状态时使用函数Use PeekMessage(),因此可以使用
// 空闲时间来绘制场景。另外,用函数GetMessage()来避免消耗CPU时间
if( g_bActive )
bGotMsg = PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE );
else
bGotMsg = GetMessage( &msg, NULL, 0U, 0U );
if( bGotMsg )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
// 在空闲时间内绘制一帧(没有消息在等待)
if( g_bActive && g_bReady )
Render3DEnvironment();
}
}
上面的代码使用了一个全局标志变量g_bActive来跟踪应用程序是否处于活动状态,另一个变量g_bReady用于表明所有的系统对象是否准备好绘制场景。应用程序在窗口不可见时将g_bActive设置为 FALSE,在需要重建用于绘制场景的对象时将g_bReady变量设置为FALSE。
如果应用程序处于活动状态,就会检查消息队列是否存在未决消息。如果在该队列中有消息,代码就像任何其他Windows应用程序那样将它们分发出去。否则,就调用应用程序定义的Render3DEnvironment函数来绘制一帧场景。
5. 绘制与显示场景
在应用程序没有处理系统消息时,可以绘制一幅场景的框架。当消息队列为空时,Triangle例子程序在WinMain中调用应用程序定义的Render3DEnvironment函数来绘制场景。Render3DEnvironment函数将绘制任务分为三个子步骤。
(1)更新场景
Triangle例子程序中在调用Render3DEnvironment函数后,立即调用App_ FrameMove(另一个应用程序定义的函数)。App_FrameMove函数简单地更新Direct3D用于几何图形的世界矩阵,以便反映沿y轴方向的基于一个内部计数值的旋转,该计数值以fTimeKey参数的形式传递给函数。因为旋转对每一帧都要执行一次,最终结果看起来好像模型被实地旋转一样。代码如下:
HRESULT App_FrameMove( LPDIRECT3DDEVICE7 pd3dDevice, FLOAT fTimeKey )
{
// 在这个例子中沿y轴方向旋转三角形。设置一个4×4矩阵来定义旋转并将它
// 设置为新的世界转换
D3DMATRIX matSpin;
matSpin._11 = matSpin._22 = matSpin._33 = matSpin._44 = 1.0f;
matSpin._12 = matSpin._13 = matSpin._14 = matSpin._41 = 0.0f;
matSpin._21 = matSpin._23 = matSpin._24 = matSpin._42 = 0.0f;
matSpin._31 = matSpin._32 = matSpin._34 = matSpin._43 = 0.0f;
matSpin._11 = (FLOAT)cos( fTimeKey );
matSpin._33 = (FLOAT)cos( fTimeKey );
matSpin._13 = -(FLOAT)sin( fTimeKey );
matSpin._31 = (FLOAT)sin( fTimeKey );
pd3dDevice->SetTransform( D3DTRANSFORMSTATE_WORLD, &matSpin );
return S_OK;
}
当然,在现实世界中,应用程序将执行比对一个三角形实施一次简单的旋转更多的任务。
(2)绘制场景
一旦几何图形更新后反映出所需要的动画,就可以绘制场景了。Triangle例子程序采用了一种典型的方法。在例子程序的Render3DEnvironment函数中调用的应用程序定义的App_Render函数通过清除视口开始:
HRESULT App_Render(LPDIRECT3DDEVICE7 pd3dDevice, D3DRECT* prcViewRect )
{
// 将视口清除为蓝色
pd3dDevice->Clear( 1UL, prcViewRect, D3DCLEAR_TARGET, Ox000000FF,
0L, 0L );
上面的代码调用IDirect3DDevice7::Clear方法来清除视口。Clear方法接收的前两个参数为:一个用于描述绘制目标表面上要清除的区域的矩形数组的地址;另一个用于通知该方法应当清除多少个来自数组的矩形的值。在大多数情况下会使用一个覆盖了整个绘制目标的矩形。第三个参数决定了该方法的行为。可以清除一个绘制目标表面、一个相关深度缓冲区、模板缓冲区,或者这三者的任意组合。这个例子没有使用深度缓冲区,D3DCLEAR _TARGET是所使用的惟一标志。最后三个参数用于为绘制目标、深度缓冲区和模板缓冲区反映清除值。Triangle例子程序将绘制目标表面的清除颜色设置为蓝色。因为剩下的参数在例子程序中没有使用,代码将它们设置为零。Clear方法在相应的标志没有出现时会忽略它们。
在清除了视口之后,Triangle例子程序通知Direct3D绘制将要开始,然后绘制场景,最后发出信号表明绘制已经完成,如下所示:
// 开始场景
if( FAILED( pd3dDevice->BeginScene() ) )
return E_FAIL;
// 调用函数DrawPrimitive()来绘制三角形,接着例子程序将进入更多的调用各
// 种绘制多边形的函数的细节
pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, D3DFVF_VERTEX,
g_pvTriangleVertices, 6, NULL );
// 结束场景
pd3dDevice->EndScene();
return S_OK;
}