D3D学习笔记之六---简单的动画实现。

来源:互联网 发布:qt网络编程账号登录 编辑:程序博客网 时间:2024/06/04 19:30

终于回来了,呵呵,以后可以继续我们的学习了,这下间隔的时间真不短,真不好意思

这次我们要做的东西比较有趣,前面我们虽然讨论了很多的东西,可是都是些静态的,这次我们就让它动起来。首先还是老规矩,先把代码上来吧。

下面是代码:

 

 

 

//=============================================================================
// Desc: 坐标变换
//=============================================================================

#include <d3dx9.h>  //D3DX实用库函数, 该头文件中又包含了d3d9.h头文件


//-----------------------------------------------------------------------------
// Desc: 全局变量
//-----------------------------------------------------------------------------
LPDIRECT3D9                g_pD3D       = NULL;    //Direct3D对象
LPDIRECT3DDEVICE9          g_pd3dDevice = NULL;    //Direct3D设备对象
LPDIRECT3DVERTEXBUFFER9    g_pVB        = NULL;    //顶点缓冲区对象
HWND        g_Wnd     = NULL;    //窗口句柄
D3DRECT                    g_ClentRect;


//-----------------------------------------------------------------------------
// Desc: 顶点结构
//-----------------------------------------------------------------------------
struct CUSTOMVERTEX
{
    D3DXVECTOR3 position;
    DWORD color;
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)


//-----------------------------------------------------------------------------
// Desc: 设置世界矩阵
//-----------------------------------------------------------------------------
VOID SetupWorldMatrice()
{
 //建立一个绕X轴动态旋转的世界矩阵
    D3DXMATRIX matWorld;
    UINT  iTime  = timeGetTime() % 1000;
    FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;
 D3DXMatrixIdentity( &matWorld );
    D3DXMatrixRotationX( &matWorld, fAngle );

 //设置世界矩阵
    g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
}


//-----------------------------------------------------------------------------
// Desc: 设置观察矩阵和投影矩阵
//-----------------------------------------------------------------------------
VOID SetupViewandProjMatrices()
{
    //建立并设置观察矩阵
    D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f );
    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
    D3DXMATRIX matView;
    D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
    g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

    //建立并设置投影矩阵
    D3DXMATRIX matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f );
    g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}


//-----------------------------------------------------------------------------
// Desc: 设置视区
//-----------------------------------------------------------------------------
VOID SetupViewPort()
{
 RECT rect;
 GetClientRect(g_Wnd,&rect);
 D3DVIEWPORT9 vp;
 vp.X      = 0;
 vp.Y      = 0;
 vp.Width  = rect.right;
 vp.Height = rect.bottom;
 vp.MinZ   = 0.0f;
 vp.MaxZ   = 1.0f;
 g_pd3dDevice->SetViewport(&vp);
}


//-----------------------------------------------------------------------------
// Desc: 初始化Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
 //创建Direct3D对象, 该对象用于创建Direct3D设备对象
    if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
        return E_FAIL;

 //设置D3DPRESENT_PARAMETERS结构, 准备创建Direct3D设备对象
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;

 //创建Direct3D设备对象
    if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                                      &d3dpp, &g_pd3dDevice ) ) )
    {
        return E_FAIL;
    }

    //设置剔出模式为不剔出任何面(正面和反面)
    g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );

 //关闭光照处理, 默认情况下启用光照处理
    g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );

 //设置观察和投影矩阵
 SetupViewandProjMatrices();

 //设置视区
 SetupViewPort();

 return S_OK;
}


//-----------------------------------------------------------------------------
// Desc: 创建场景图形
//-----------------------------------------------------------------------------
HRESULT InitGeometry()
{
 //创顶点缓冲区
    if( FAILED( g_pd3dDevice->CreateVertexBuffer( 50*2*sizeof(CUSTOMVERTEX),
                                                  0, D3DFVF_CUSTOMVERTEX,
                                                  D3DPOOL_DEFAULT, &g_pVB, NULL ) ) )
    {
        return E_FAIL;
    }

 //填充顶点缓冲区
    CUSTOMVERTEX* pVertices;
    if( FAILED( g_pVB->Lock( 0, 0, (void**)&pVertices, 0 ) ) )
        return E_FAIL;
    for( DWORD i=0; i<50; i++ )
    {
        FLOAT theta = (2*D3DX_PI*i)/(50-1);
        pVertices[2*i+0].position = D3DXVECTOR3( sinf(theta),-1.0f, cosf(theta) );
  pVertices[2*i+0].color   =0xffffff00;
        pVertices[2*i+1].position = D3DXVECTOR3( sinf(theta), 1.0f, cosf(theta) );
  pVertices[2*i+1].color   =0xffffff00;
    }
    g_pVB->Unlock();

    return S_OK;
}


//-----------------------------------------------------------------------------
// Desc: 释放创建的对象
//-----------------------------------------------------------------------------
VOID Cleanup()
{
 //释放顶点缓冲区对象
    if( g_pVB != NULL )
        g_pVB->Release();

 //释放Direct3D设备对象
    if( g_pd3dDevice != NULL )
        g_pd3dDevice->Release();

 //释放Direct3D对象
    if( g_pD3D != NULL )
        g_pD3D->Release();
}


//-----------------------------------------------------------------------------
// Desc: 渲染图形
//-----------------------------------------------------------------------------
VOID Render()
{
 //清空后台缓冲区
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(45, 50, 170), 1.0f, 0 );

 //开始在后台缓冲区绘制图形
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
  //设置世界矩阵
        SetupWorldMatrice();

  //在后台缓冲区绘制图形
        g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
        g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
        g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2*50-2 );

  //结束在后台缓冲区渲染图形
        g_pd3dDevice->EndScene();
    }

 //将在后台缓冲区绘制的图形提交到前台缓冲区显示
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}


//-----------------------------------------------------------------------------
// Desc: 消息处理
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
 switch( msg )
 {
 case WM_DESTROY:
  Cleanup();
  PostQuitMessage( 0 );
  return 0;
 }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}


//-----------------------------------------------------------------------------
// Desc: 入口函数
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
 //注册窗口类
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC , MsgProc, 0L, 0L,
                      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
                      L"ClassName", NULL };
    RegisterClassEx( &wc );

 //创建窗口
    HWND hWnd = CreateWindow( L"ClassName", L"坐标变换",
                              WS_OVERLAPPEDWINDOW, 200, 100, 500,500,
                              GetDesktopWindow(), NULL, wc.hInstance, NULL );
 g_Wnd=hWnd;

 //初始化Direct3D
    if( SUCCEEDED( InitD3D( hWnd ) ) )
    {
  //创建场景图形
        if( SUCCEEDED( InitGeometry() ) )
        {
   //显示窗口
            ShowWindow( hWnd, SW_SHOWDEFAULT );
            UpdateWindow( hWnd );

   //进入消息循环
            MSG msg;
            ZeroMemory( &msg, sizeof(msg) );
            while( msg.message!=WM_QUIT )
            {
                if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
                {
                    TranslateMessage( &msg );
                    DispatchMessage( &msg );
                }
                else
    {
                    Render();  //渲染图形
    }
            }
        }
    }

    UnregisterClass( L"ClassName", wc.hInstance );
    return 0;
}

 

这次的代码相对以前有了比较大的改动,这里需要做点必须的解释,那就是Directx3D的T&L流水线。

其实这个流水线我们前面已经用到了,只不过用的全部是他们的默认值,也就没有进行一些设置。让我们来看看这个这个流水线都有些什么东西:

这个流水线要做几个变换:世界变换->取景变换->光照效果的计算->投影变换->视区变换->进行渲染。

乍一看,晕,这么多,都是什么东西,呵呵,其实也没有很难的地方,而且这些工作我们的Directx都为我们做了,我们要做的就是去调用它的函数,让我们先看看世界变换:

物体在三维空间中的运动和变形的过程成为世界变换,比如:物体的平移、旋转、缩放等。物体在其中运动的空间成为世界空间,所对应的是世界坐标系。(需要主要这里需要涉及几个坐标系,要搞清楚,不要混淆,其他的坐标系还有物体坐标系、观察坐标系等)物体在该坐标系中的变换称为世界变换。我们的代码中实现这部分变换的是:

VOID SetupWorldMatrice()
{
 //建立一个绕X轴动态旋转的世界矩阵
    D3DXMATRIX matWorld;
    UINT  iTime  = timeGetTime() % 1000;
    FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;
     D3DXMatrixIdentity( &matWorld );
    D3DXMatrixRotationX( &matWorld, fAngle );

 //设置世界矩阵
    g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
}

这部分代码设置了一个世界坐标系,并调用g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
将我们定义的世界坐标系提交到了引擎里面。需要注意的是我们的这个世界坐标不是一成不变的,它是在不断变化的,因为这句代码:FLOAT fAngle = iTime * (2.0f * D3DX_PI) / 1000.0f;  角度在随着时间不断的变换,所以会有动画的效果。

 

世界变换完成以后我们就要进行取景变换,所谓的进行观察变换也就是生成一个观察变换的矩阵,并将这个矩阵提交到引擎里面,仔细的讲一下呢,取景,也就是相当于咱们人眼的观察,也就是在3D世界里一个相机的坐标系,其中这个相机的位置为坐标原点,并相机的观察方向为Z轴,这个变换的内容就是把其他的坐标转换到这个相机的坐标系中来,以便后面的计算。

 

VOID SetupViewandProjMatrices()

{

    //建立并设置观察矩阵

    D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f );

    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );

    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );

    D3DXMATRIX matView;

    D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );

    g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

 

    //建立并设置投影矩阵

    D3DXMATRIX matProj;

    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f );

    g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

}

上面的代码也有标明:第一部分就为建立观察矩阵。

建立的过程也显而易懂,就是声明了三个点坐标,然后生成了一个矩阵,也就是观察矩阵。三个点。

下面看下这个函数:D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );

这个函数的作用就是根据三个点所确定的相机位置生成观察矩阵。

D3DXMATRIX * D3DXMatrixLookAtLH(

  D3DXMATRIX * pOut,

  CONST D3DXVECTOR3 * pEye,

  CONST D3DXVECTOR3 * pAt,

  CONST D3DXVECTOR3 * pUp

);

Parameters

pOut

返回的观察矩阵

pEye

相机所在的位置

pAt

所要观察的位置。

pUp   

相机的上向量,通常都设置为(0,1,0)

取景变换其实做的就是将世界坐标系转换到相机的坐标系中,具体的变换过程就是,将世界坐标系的X,Y,Z轴对应到相机坐标系的XYZ坐标轴上,其实这些变换引擎已经给我们做了,以后在写自由的相机的时候我们再考虑具体怎么去变换。

 

将生成的取景变换矩阵提交到引擎里面,下面做的就是投影变换了。

投影变换就是将相机坐标系中的景象放缩到屏幕的2D画面上,

D3DXMATRIX matProj;

    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f );

    g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

这些代码的作用就是按照要求生成一个投影矩阵,下面我们看下这个函数:

D3DXMATRIX * D3DXMatrixPerspectiveFovLH(

  D3DXMATRIX * pOut,

  FLOAT fovy,

  FLOAT Aspect,

  FLOAT zn,

  FLOAT zf

);

Parameters

pOut

返回生成的投影矩阵

fovy

表示相机在Y轴上的成像角度,单位为弧度。

Aspect

表示截头体的从横比,我们用的是1.0,是一个方形

zn

近距平面距离相机的距离

zf

远距平面距离相机的距离。

 

这个函数生成的是一个透视投影的矩阵,这样的的投影,会产生物体近大远小的效果。符合我们的视觉习惯。

将我们生成的这个投影矩阵提交到殷勤里面。

 

下面我们要做的就是视区变换了,这个变换是渲染流水线的最后一个变换。它的作用是将我们得到的显示景物截取到我们屏幕可以容纳的一个范围之内,大小以像素为单位。

 

VOID SetupViewPort()

{

     RECT rect;

     GetClientRect(g_Wnd,&rect);

     D3DVIEWPORT9 vp;

     vp.X      = 0;

     vp.Y      = 0;

     vp.Width  = rect.right;

     vp.Height = rect.bottom;

     vp.MinZ   = 0.0f;

     vp.MaxZ   = 1.0f;

     g_pd3dDevice->SetViewport(&vp);

}

 

这个函数的作用就是设置了视区的大小,我们设置的就是我们客户区的大小。D3DVIEWPORT9的前面几个参数比较容易理解,后面的两个参数MinZ的意思是视区内景物的最小深度,同理,MaxZ是最大深度,通常设置成0和1,意思是取景范围的最近和最远的距离。设置好各项参数后,调用SetViewport函数提交到D3D引擎里面。

渲染流水线到这里就结束了。

 

需要注意的是,在每次的渲染过程中都要进行这一系列的变换,也就是每一帧都是这样,这点意识到时十分重要地。

 

 

剩下的就是渲染了:

VOID Render()
{
 //清空后台缓冲区
    g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(45, 50, 170), 1.0f, 0 );

 //开始在后台缓冲区绘制图形
    if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
    {
  //设置世界矩阵
        SetupWorldMatrice();

  //在后台缓冲区绘制图形
        g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
        g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
        g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2*50-2 );

  //结束在后台缓冲区渲染图形
        g_pd3dDevice->EndScene();
    }

 //将在后台缓冲区绘制的图形提交到前台缓冲区显示
    g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}

和以前的有很大的相似,注意的是在渲染的时候调用了SetupWorldMatrice(),这个函数前面我们已经看过了,就是设置世界矩阵,可以注意到,世界矩阵是在不断地变换的,就是应为世界矩阵的不断变化才造成了物体在不断变化的“假象”,在以后的模型渲染中也是一样的,必须要明白这一点,是世界矩阵的变化,造成了物体的移动。

这样渲染出来的图形就动起来了。生成一下,看到了吧,一个旋转地图形出现了。很神奇吧,呵呵

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/luoya263547560/archive/2009/03/28/4032660.aspx

原创粉丝点击