会员: 密码:  免费注册 | 忘记密码 | 会员登录 网页功能: 加入收藏 设为首页 网站搜索  
游戏开发 > 程序设计 > 3D图形
Direct3D 8.0 Immediate Mode 教學 II
发表日期:2007-01-16 17:05:07作者: 出处:  


Immediate Mode 教學 1 介紹了畫 "已經Transform成螢幕座標" 的三角形,感覺上跟2D功能很像,那當三角形資訊是尚未 Transform的,要如何投影到螢幕座標系統呢?整個投影的原理先不詳細介紹,先藉助 Direct3D本身的Transform機制,提供 Direct3D 世界座標的端點資料,由 Direct3D 幫我們轉換成螢幕座標並畫出來。

這次的範例仍然是畫一個三角形,但是三角形座標為世界座標。同時要畫世界座標的東西了,當然就要指定攝影機的位置與對哪一點拍攝,此時2D螢幕呈現的,其實就是以攝影機角度觀看的世界。
完整程式列表:
//-----------------------------------------------------------------------------
// File: d3d8im_2.cpp
//
// Desc: This is the second tutorial for using Direct3D 8.0.
//
// http://latte.fanmesh.com
//-----------------------------------------------------------------------------
#include <d3d8.h>
#include <d3dx8.h>
#include <d3dx8math.h>

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D8 pD3D = NULL; // Used to create the D3DDevice
LPDIRECT3DDEVICE8 pDev = NULL; // Our RenderD3Ding device
LPDIRECT3DVERTEXBUFFER8 pVB = NULL;

//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Initializes Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
    // Create the D3D object, which is needed to create the D3DDevice.
    if( NULL == ( pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    // Get the current desktop display mode
    D3DDISPLAYMODE d3ddm;
    if( FAILED( pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
        return E_FAIL;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );

    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = d3ddm.Format;

    if( FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &pDev ) ) )
    {
        return E_FAIL;
    }

    D3DXMATRIX matWorld;
    D3DXMatrixIdentity( &matWorld );
    pDev->SetTransform( D3DTS_WORLD, &matWorld );

    D3DXMATRIX matView;
    D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 50.0f,-200.0f ),
                        &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
                        &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
    pDev->SetTransform( D3DTS_VIEW, &matView );

    D3DXMATRIX matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 500.0f );
    pDev->SetTransform( D3DTS_PROJECTION, &matProj );

    pDev->SetRenderState( D3DRS_LIGHTING, FALSE );

    // Device state would normally be set here
    return S_OK;
}

//-----------------------------------------------------------------------------
// Name: CloseD3D()
// Desc: Releases all previously initialized objects
//-----------------------------------------------------------------------------
void CloseD3D(void)
{
    if( pVB != NULL)
        pVB->Release();

    if( pDev != NULL)
        pDev->Release();

    if( pD3D != NULL)
        pD3D->Release();
}

//-----------------------------------------------------------------------------
// Name: RenderD3D()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
struct MYVERTEX
{
    FLOAT x,y,z;
    DWORD color;
};

#define NUM_VERT 3

struct MYVERTEX vert[NUM_VERT]=
{
    0, 50, 0, 0x00FF0000,
    40, 0, 0, 0x000000FF,
    -40, 0, 0, 0x0000FF00
};

BOOL NeedCreateVBFlag=true;
float rad=0.0f;

void RenderD3D(void)
{
    if( NULL == pDev )
        return;

    // Clear the backbuffer to a blue color
    pDev->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );

    // Begin the scene
    pDev->BeginScene();

//-----------------------------------------------------------------------------
// RenderD3Ding of scene objects can happen here
//-----------------------------------------------------------------------------
    if(NeedCreateVBFlag) {
        pDev->CreateVertexBuffer(sizeof(MYVERTEX)*NUM_VERT,
                                D3DUSAGE_WRITEONLY,
                                D3DFVF_XYZ|D3DFVF_DIFFUSE,
                                D3DPOOL_DEFAULT,
                                &pVB);
        NeedCreateVBFlag=false;
    }

    MYVERTEX *v;
    pVB->Lock(0,0,(BYTE **)&v,0);
    memcpy(v,vert,sizeof(MYVERTEX)*NUM_VERT);
    pVB->Unlock();

    D3DXMATRIX matWorld;
    rad+= 0.001f;
    D3DXMatrixRotationY(&matWorld, rad);
    pDev->SetTransform( D3DTS_WORLD, &matWorld );

    pDev->SetVertexShader(D3DFVF_XYZ|D3DFVF_DIFFUSE);
    pDev->SetStreamSource(0,pVB,sizeof(MYVERTEX));
    pDev->DrawPrimitive(D3DPT_TRIANGLELIST,0,NUM_VERT/3);
//-----------------------------------------------------------------------------

    // End the scene
    pDev->EndScene();

    // Present the backbuffer contents to the display
    pDev->Present( NULL, NULL, NULL, NULL );
}

//-----------------------------------------------------------------------------
// Name: MsgProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
    case WM_DESTROY:
        PostQuitMessage( 0 );
        return 0;
    }

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

//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
    MSG msg;

    // Register the window class
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, "D3D Tutorial", NULL };
    RegisterClassEx( &wc );

    // Create the application's window
    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D8 Tutorial 02", WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, GetDesktopWindow(), NULL, wc.hInstance, NULL );

    // Initialize Direct3D
    if( SUCCEEDED( InitD3D( hWnd ) ) )
    {
        // Show the window
        ShowWindow( hWnd, SW_SHOWDEFAULT );
        UpdateWindow( hWnd );

        while(1)
        {
            if(PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ))
            {
                if( GetMessage( &msg, NULL, 0, 0 ) )
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else
                {
                    // This msg is WM_QUIT.
                    break;
                }
            }
            else
            {
                RenderD3D();
            }
        }
    }

    // Clean up everything and exit the app
    CloseD3D();
    UnregisterClass( "D3D Tutorial", wc.hInstance );
    return 0;
}


1.#include 除了 d3d8.h , 還要多個 d3dx8.h 與 d3dx8math.h 。 D3DX 提供了許多以 Direct3D 為基礎,有用的functions,到了世界座標幾乎是非用不可了。
//-----------------------------------------------------------------------------
// File: d3d8im_2.cpp
//
// Desc: This is the second tutorial for using Direct3D 8.0.
//
// http://latte.fanmesh.com
//-----------------------------------------------------------------------------
#include <d3d8.h>
#include <d3dx8.h>
#include <d3dx8math.h>


2.此範例只需要3個全域變數 pD3D、pDev、pVB,其中 pD3D 就是最新的 Direct3D/DirectDraw 綜合體,或者該稱為 Direct Graphics, 得先產生就對了。 pDev是用來通知3D 硬體畫三角形。pVB 是 Vertex Buffer,現在通知 pDev畫三角形,三角形的資訊一定要放在 Vertex Buffer 了。這次的 Vertex Buffer ,放的就是世界座標的三角形。

//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
LPDIRECT3D8 pD3D = NULL; // Used to create the D3DDevice
LPDIRECT3DDEVICE8 pDev = NULL; // Our RenderD3Ding device
LPDIRECT3DVERTEXBUFFER8 pVB = NULL;

3.WinMain與 Message Procedure 與上一個範例一樣,不再介紹了。

4.架構與上一個範例一樣,分為 InitD3D(), RenderD3D(), CloseD3D() 三大塊。先看看 InitD3D() 與 CloseD3D()。

  1. InitD3D() 前半段沒變,到 CreateDevice()之前都一樣。
  2. pDev(LPDIRECT3DDEVICE8) 初始化好了後,有三個跟投影有關的矩陣要通知它,分別是 World, View, Projection。任何一個世界座標的物件,一定是依序乘上 world, view, projection 這三個矩陣。
  3. 如果要畫的物件還需要位移或旋轉,那就放進World Matrix。如果都不要動了,那就要利用D3DXMatrixIdentity(),將 matWorld 設定成一個 4x4 的單位矩陣。世界座標乘單位矩陣,不會有任何變化。
  4. View 矩陣則代表攝影機的角度,利用 D3DX 提供的D3DXMatrixLookAtLH() 產生較方便 - 第二與第三個參數分別為 LookFrom 與 LookAt,在此範例為 (0, 50,-200) 與 ( 0,0,0 ), 表示這台攝影機放在 (0, 50, -200) 這個座標,鏡頭看著 (0,0,0) 。第四個參數為 Up Vector - (0,1,0) ,表示這個世界中,"上"的抽象概念為 (0,1,0),如果你不填 (0,1,0),整個世界就改觀了。
  5. Projection 矩陣則控制投影,利用 D3DX 提供的D3DXMatrixPerspectiveFovLH產生較方便 - 第二個參數為 FOV (Field of View) ,單位為徑度,在此範例為D3DX_PI/4,也就是 45 度的視角。第三個參數為長寬比例的調整,沒什麼意外的話,填 1.0f ,表示長寬的比例不變。第四個與第五個參數為 Z 的區間,不在這個區間的,不畫出來。
  6. 本範例還不預備用到光,端點資料裡面的色彩是手動給的,所以不需要 Normal 值。從 DirectX 7.0 開始,不帶有Normal 值的端點,因為無法算"光",導致色彩都變成黑色,所以要用 pDev->SetRenderState( D3DRS_LIGHTING, FALSE ); 將計算光源的部分關掉,通知D3D直接使用端點資料裡面的彩色值。

至於 CloseD3D(),就是單純地釋放 pD3D, pDev, pVB 那三個全域變數。

//-----------------------------------------------------------------------------
// Name: InitD3D()
// Desc: Initializes Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
    // Create the D3D object, which is needed to create the D3DDevice.
    if( NULL == ( pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) )
        return E_FAIL;

    // Get the current desktop display mode
    D3DDISPLAYMODE d3ddm;
    if( FAILED( pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )
        return E_FAIL;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory( &d3dpp, sizeof(d3dpp) );

    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = d3ddm.Format;

    if( FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &pDev ) ) )
    {
        return E_FAIL;
    }

    D3DXMATRIX matWorld;
    D3DXMatrixIdentity( &matWorld );
    pDev->SetTransform( D3DTS_WORLD, &matWorld );

    D3DXMATRIX matView;
    D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 50.0f,-200.0f ),
                    &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
                    &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
    pDev->SetTransform( D3DTS_VIEW, &matView );

    D3DXMATRIX matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 500.0f );
    pDev->SetTransform( D3DTS_PROJECTION, &matProj );

    pDev->SetRenderState( D3DRS_LIGHTING, FALSE );

    // Device state would normally be set here
    return S_OK;
}

//-----------------------------------------------------------------------------
// Name: CloseD3D()
// Desc: Releases all previously initialized objects
//-----------------------------------------------------------------------------
void CloseD3D(void)
{
    if( pVB != NULL)
        pVB->Release();

    if( pDev != NULL)
        pDev->Release();

    if( pD3D != NULL)
        pD3D->Release();
}

5.RenderD3D() 就是畫三角形的部分:
  • 這次自行設計的Vertex 格式為 (x,y,z) 與 端點顏色,其中 x,y,z 預備放的是世界座標。在此範例中,三個點分別為 (0, 50, 0), (40, 0, 0), (-40, 0, 0),各位可以開始想像,一台放在(0, 50, -200)的攝影機,看到這片三角形會長什麼樣了。
  • 既然是3D了,當然要做點跟 2D不一樣的事。在此範例,提供一個隨時間改變的旋轉矩陣放進 World Matrix ,對 Y 軸旋轉。所有的端點,Transform之前,都一定會乘上這個 World Matrix。如此表現出來的,就是一片不斷旋轉的三角形。讀者可以將這片三角形擴充為一個立方體,會更清楚。
//-----------------------------------------------------------------------------
// Name: RenderD3D()
// Desc: Draws the scene
//-----------------------------------------------------------------------------
struct  MYVERTEX
{
    FLOAT x,y,z;
    DWORD color;
};

#define NUM_VERT 3

struct MYVERTEX vert[NUM_VERT]=
{
    0, 50, 0, 0x00FF0000,
    40,  0, 0, 0x000000FF,
    -40,  0, 0, 0x0000FF00
};

BOOL    NeedCreateVBFlag=true;
float   rad=0.0f;

void RenderD3D(void)
{
    if( NULL == pDev )
        return;

    // Clear the backbuffer to a blue color
    pDev->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );

    // Begin the scene
    pDev->BeginScene();

//-----------------------------------------------------------------------------
// RenderD3Ding of scene objects can happen here
//-----------------------------------------------------------------------------
    if(NeedCreateVBFlag)
    {
        pDev->CreateVertexBuffer(sizeof(MYVERTEX)*NUM_VERT,
                                D3DUSAGE_WRITEONLY,
                                D3DFVF_XYZ|D3DFVF_DIFFUSE,
                                D3DPOOL_DEFAULT,
                                &pVB);
        NeedCreateVBFlag=false;
    }

    MYVERTEX *v;
    pVB->Lock(0,0,(BYTE **)&v,0);
    memcpy(v,vert,sizeof(MYVERTEX)*NUM_VERT);
    pVB->Unlock();

    D3DXMATRIX matWorld;
    rad+= 0.001f;
    D3DXMatrixRotationY(&matWorld, rad);
    pDev->SetTransform( D3DTS_WORLD, &matWorld );

    pDev->SetVertexShader(D3DFVF_XYZ|D3DFVF_DIFFUSE);
    pDev->SetStreamSource(0,pVB,sizeof(MYVERTEX));
    pDev->DrawPrimitive(D3DPT_TRIANGLELIST,0,NUM_VERT/3);
//-----------------------------------------------------------------------------

    // End the scene
    pDev->EndScene();

    // Present the backbuffer contents to the display
    pDev->Present( NULL, NULL, NULL, NULL );
}

結論:

這片三角形,不斷地對 Y軸旋轉,各位應該會發現,有一半的時間會不見,這是因為 SetRenderState() 當中, D3DRS_CULLMODE 預設值是 D3DCULL_CCW,也就是說三角形的三個點必須順時鐘才畫,如果三個點變成逆時鐘,就會被過濾(CULL)掉。這片三角形對著 Y軸轉了 180度後,三個點的排列就變成逆時鐘了,也因此被過濾掉了。如果讀者想要全部的時間都出現,可下一道 SetRenderState(D3DRS_CULLMODE,D3DCULL_NONE); ,就會不管順時鐘、逆時鐘都畫。

不過排列為逆時鐘就過濾(CULL_CCW),是一個簡單又實用的加速技巧,比如說一個3D人物,是由 1000片三角形所組成,因此大約正面500片三角形,背面500片三角形,當那個人物面向攝影機時,背面500片三角形,其實畫了也沒用, 會被正面的三角形蓋掉,不畫反而能增加一倍的速度。 因此,CULL_CCW盡量是不要關掉。



如何編譯:
1.Visual C++ 的 Tools -> Options -> Directories 當中, include files 與 library files分別要將 c:\mssdk\include 與 c:\mssdk\library 優先順序放到最前面。



2.在 Visual C++ 當中 new 一個 Win32 Application的專案(切記,不是 Win32 Console Application),將 d3d8im_2.cpp 加入此專案的 source 區。

3.Visual C++ 的 Projects -> Projects Setting -> Link -> Object/library modules 最後面加上 d3d8.lib d3dx8.lib 兩個程式庫。

4.按下 F7 ,即可編譯完成。

返回顶部】 【打印本页】 【关闭窗口

关于我们 / 给我留言 / 版权举报 / 意见建议 / 网站编程QQ群   
Copyright ©2003- 2024 Lihuasoft.net webmaster(at)lihuasoft.net 加载时间 0.00398