天天看點

【寒江雪】Alpha通道混合技術Alpha通道與混合技術Direct3D中的融合因子融合運算方式的取法融合因子取法Alpha的三處來源Alpha融合使用方法示例代碼

Alpha通道與混合技術

  Alpha通道是計算機中存儲一張圖檔的透明和半透明資訊的通道。它是一個8位的灰階通道,用256級灰階來記錄圖像中的透明度資訊,定義透明,不透明和半透明區域,其中黑色表示全透明,白色表示不透明,灰表示半透明。

  混合的英文術語為Blending,是計算機圖形學中常用的一種技術,即混合像素。我們通常用已經光栅化的像素光栅化同一位置的像素,或者說是在某圖元上混合圖元。

Direct3D中的融合因子

  在Direct3D中,使用Alpha通道來實作多個像素顔色值的融合。每個像素都包含四個分量:Alpha,紅色分量,綠色分量和藍色分量。其中,Alpha分量用于指定像素的透明度,在0~255之間取值。根據不同的顔色宏的差別,也有可能是在0.0~1.0之間

  在Direct3D中,融合這一領域有一個權威,那便是Alpha融合公式。Alpha融合公式如下:

OutPutColor=(RGB src×K src)op(RGB dst×K dst);

  其中,RGB src和RGB dst分别表示源像素和目标像素的顔色值,為包含四個顔色分量的顔色值。K src和K dst分别表示源融合因子和目标融合因子。它們指定了源像素和目标像素的顔色值在融合過程中所占的比例,在[0,1]之間取值。通過源融合因子和目标融合因子,我們能夠以多種方式來修改源像素和目标像素的顔色值,進而獲得我們滿意的最終融合後的顔色值。

  OP表示源和目标的融合運算方式,由D3DBLENDOP枚舉體來指定,需要注意的是,它的預設值是源計算結果和目标計算結果相加。

融合運算方式的取法

  我們在SetRenderState中設定參數,第二個參數在D3DBLENDOP枚舉體中取值,而第一個參數取D3DRS_BLENDOP

  D3DBLENDOP枚舉體的定義如下:

typedef enum D3DBLENDOP{
    D3DBLENDOP_ADD = ,
    D3DBLENDOP_SUB = ,
    D3DBLENDOP_REVSUBTRACT = ,
    D3DBLENDOP_MIN = ,
    D3DBLENDOP_MAX = ,
    D3DBLENDOP_FORCE_DWORD = 
}D3DBLENDOP,*LPD3DBLENDOP;
           
  • D3DBLENDOP_ADD:源像素計算結果與目标像素計算結果相加。即【最終結果】=【源】+【目标】
  • D3DBLENDOP_SUBTRACT:源像素計算結果與目标像素計算結果相減。即【最終結果】=【源】-【目标】
  • D3DBLENDOP_REVSUBTRACT:目标像素的計算結果與源像素計算結果相減。即【最終結果】=【目标】-【源】
  • D3DBLENDOP_MIN:在源像素計算結果和目标像素計算結果之間取小者。即【最終結果】=MIN(【源】,【目标】)
  • D3DBLENDOP_MAX:在源像素計算結果和目标像素計算結果之間取大者。即【最終結果】=MAX(【源】,【目标】)

融合因子取法

  源融合因子和目标融合因子可以在SetRenderState方法中第一個參數取D3DRS_SRCBLEND和D3DRS_DESTBLEND分别進行設定,而第二個參數都是在一個D3DBLEND枚舉體中進行的取值,它的原型如下:

typedef enum D3DBLEND {  
  D3DBLEND_ZERO              = ,  
  D3DBLEND_ONE               = ,  
  D3DBLEND_SRCCOLOR          = ,  
  D3DBLEND_INVSRCCOLOR       = ,  
  D3DBLEND_SRCALPHA          = ,  
  D3DBLEND_INVSRCALPHA       = ,  
  D3DBLEND_DESTALPHA         = ,  
  D3DBLEND_INVDESTALPHA      = ,  
  D3DBLEND_DESTCOLOR         = ,  
  D3DBLEND_INVDESTCOLOR      = ,  
  D3DBLEND_SRCALPHASAT       = ,  
  D3DBLEND_BOTHSRCALPHA      = ,  
  D3DBLEND_BOTHINVSRCALPHA   = ,  
  D3DBLEND_BLENDFACTOR       = ,  
  D3DBLEND_INVBLENDFACTOR    = ,  
  D3DBLEND_SRCCOLOR2         = ,  
  D3DBLEND_INVSRCCOLOR2      = ,  
  D3DBLEND_FORCE_DWORD       =    
} D3DBLEND, *LPD3DBLEND;
           

  下面通過一個表格來叙述融合因子的含義

* D3DBLEND_ZERO:融合因子(0,0,0,0)

* D3DBLEND_ONE:融合因子(1,1,1,1)

* D3DBLEND_SRCCOLOR:融合因子(Rsrc,Gsrc,Bsrc,Asrc)

* D3DBLEND_INVSRCCOLOR:融合因子(1-Rsrc,1-Gsrc,1-Bsrc,1-Asrc)

* D3DBLEND_SRCALPHASAT:融合因子(1-Asrc,Asrc,Asrc,Asrc)

* D3DBLEND_INVSRCALPHA:融合因子(1-Asrc,1-Asrc,1-Asrc,1-Asrc)

* D3DBLEND_DESTALPHA:融合因子(Adst,Adst,Adst,Adst)

* D3DBLEND_INVDESTALPHA:融合因子(1-Adst,1-Adst,1-Adst,1-Adst)

* D3DBLEND_DESTCOLOR:融合因子(Rdst,Gdst,Bdst,Adst)

* D3DBLEND_INVDESTCOLOR:融合因子(1-Rdst,1-Gdst,1-Bdst,1-Adst)

* D3DBLEND_SRCALPHASAT:融合因子(f,f,f,1),其中f=min(Asrc,1-Asrc)

Alpha的三處來源

  源像素和目标像素顔色值的Alpha分量分别來自頂點顔色的Alpha值,材質的Alpha值,紋理的Alpha值。我們通常在這三處源取一處就可以了。它們的優先級如下

紋理>材質>頂點顔色

  我們可以通過IDirect3DDevice9::SetTextureStageState方法指定Alpha值的來源,它的原型如下

HRESULT SetTextureStageState(
        [in] DWORD Stage,
        [in] D3DTEXTURESTAGESTATETYPE Type,
        [in] DWORD Value
    );
           
  • DWORD Stage:指定目前設定的紋理層為第幾層(0~7)
  • D3DTEXTURESTAGESTATETYPE Type:填将要設定的紋理渲染狀态,在枚舉類型D3DTEXTURESTAGESTATETYPE中任意取值。
  • DWORD Value:表示所設定的狀态值,它是根據第二個參數來決定具體取什麼值的。

頂點的Alpha分量

  如果在程式中指定每個頂點的顔色,那麼可以直接給出每個頂點顔色的Alpha值,并且這些頂點的Alpha值是可以在程式運作過程中動态修改的。

  對于頂點Alpha分量,我們就這樣寫:

g_pd3dDevice->SetTextureStageState(,D3DTSS_ALPHAARG1,D3DTA_DIFFUSE);
g_pd3dDevice->SetTextureStageState(,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);
           

材質的Alpha分量

  場景中物體沒有指定紋理的時候,才會啟用材質的Alpha分量。在這種情況下,頂點的Alpha值取決于材質屬性中漫反射顔色的Alpha系數以及燈光顔色中的Alpha系數,通過材質和光照中的Alpha系數互相作用,計算得到。我們可以這樣來設定某材質的Alpha分量值,這句代碼中,我們把這種材質的漫反射顔色值的Alpha分量設為了0.2

紋理的Alpha分量

  作為不可一世的“高富帥”——紋理,既然它在物體表面上使用了,就必須首先滿足它的要求,那麼,像素的Alpha值就是紋理Alpha混合之後的值了。

  是以這時候混合後的像素就取決于紋理的Alpha混合方式。而紋理Alpha混合方式決定了紋理Alpha混合之後的Alpha值是取自材質,還是取自紋理,抑或是取自這兩者的某種運算。若是取自紋理,我們就這樣寫:

g_pd3dDevice->SetTextureStageState(,D3DTSS_ALPHAARG1,D3DTA_TEXTURE);//Alpha提取自紋理
g_pd3dDevice->SetTextureStageState(,D3DTSS_ALPHAOP,D3DTOP_SELECTARG1);//将紋理顔色混合的第一個參數的Alpha值用于輸出
           

Alpha融合使用方法

  1. 啟用Alpha融合

      要啟用Alpha融合,我們可以通過設定D3DRS_ALPHABLENDENABLE渲染狀态為true,即寫上這句代碼:

g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
           
  1. 設定融合因子

      啟用了Alpha融合,第二步便是設定源融合因子和目标融合因子。源融合因子和目标融合因子可以分别在SetRenderState方法中第一個參數取D3DRS_SRCBLEND和D3DRS_DESTBLEND分别進行設定,第二個參數是在一個D3DBLEND枚舉體中取值

g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
           
  1. 設定Alpha融合運算方式
g_pd3dDevice->SetRenderState(D3DRS_BLENDOP,D3DBLENDOP_SUBTRACT);
           

示例代碼

#include<Windows.h>
#include<d3dx9.h>
#include<d3d9.h>
#include<time.h>
#include"DirectInput.h"
#pragma comment(lib,"d3dx9.lib")
#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"dxerr.lib")
#pragma comment(lib,"dxguid.lib")
#pragma comment(lib,"d3dx10.lib")
#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"comctl32.lib")
#define SAFE_RELEASE(p) {if((p)){(p)->Release();(p)=NULL;}}
struct CUSTOMVERTEX
{
    FLOAT _x, _y, _z;
    FLOAT _u, _v;
    CUSTOMVERTEX(FLOAT x, FLOAT y, FLOAT z, FLOAT u, FLOAT v)
        :_x(x),_y(y),_z(z),_u(u),_v(v)
    {

    }
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_TEX1)

//全局變量聲明區
const int WINDOW_WIDTH = ;
const int WINDOW_HEIGHT = ;
IDirect3DDevice9 *g_pd3dDevice = NULL;
LPD3DXFONT g_pTextFPS = NULL;   //字型COM接口
LPD3DXFONT g_pTextAdapterName = NULL;   //顯示卡資訊的2D文本
LPD3DXFONT g_pTextHelper = NULL;    //幫助資訊的2D文本
LPD3DXFONT g_pTextInfo = NULL;      //繪制資訊的2D文本

float g_FPS = ; //一個浮點數,表示幀率
wchar_t g_strFPS[] = {  };   //包含幀速率的字元數組
wchar_t g_strAdapterName[] = {  };//包含顯示卡名稱的字元數組

D3DXMATRIX g_matWorld;  //世界矩陣

LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer = NULL; //頂點緩存數組
LPDIRECT3DINDEXBUFFER9  g_pIndexBuffer = NULL;  //索引緩存數組
D3DMATERIAL9 *g_pMaterials=NULL;
LPDIRECT3DTEXTURE9 *g_pTextures = NULL;
DWORD g_dwNumMtrls;
LPD3DXMESH g_pMesh = NULL;

LPDIRECT3DTEXTURE9 g_pMipTexture = NULL;    //紋理接口對象
DInputClass *g_pDInput = NULL;  //一個DInputClass對象



HRESULT Direct3D_Init(HWND hwnd);
HRESULT Object_Init();
VOID Direct3D_Update(HWND hwnd);
VOID Direct3D_Render(HWND hwnd);
VOID Direct3D_CleanUp(HWND hwnd);
VOID Matrix_Set();
float GET_FPS();

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
{
    WNDCLASSEX wc = {  };
    wc.cbClsExtra = ;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.cbWndExtra = ;
    wc.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = NULL;
    wc.hIconSm = NULL;
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = L"MainWindow";
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;

    RegisterClassEx(&wc);
    HWND hwnd = CreateWindow(L"MainWindow", L"寒江雪", WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);
    if (S_OK == Direct3D_Init(hwnd)) {
        MessageBox(hwnd, L"DirectX3D 初始化完成", L"寒江雪消息視窗", );
    }

    g_pDInput = new DInputClass();
    g_pDInput->Init(hwnd, hInstance, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
    PlaySound(L"Through the Arbor.wav", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP); //循環播放背景音樂
    ShowWindow(hwnd, SW_SHOW);
    UpdateWindow(hwnd);


    MSG msg = {  };
    while (msg.message != WM_QUIT) {
        if (PeekMessage(&msg, , , , PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else {
            Direct3D_Update(hwnd);
            Direct3D_Render(hwnd);
        }
    }
    UnregisterClass(L"MainWindow", hInstance);
    return ;
}

HRESULT Direct3D_Init(HWND hwnd)
{
    LPDIRECT3D9 d3d9 = NULL;
    d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
    if (d3d9 == NULL)
        return E_FAIL;

    D3DCAPS9 caps;
    int vp = ;
    if (FAILED(d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps)))
        return E_FAIL;

    if (caps.DevCaps&D3DDEVCAPS_HWTRANSFORMANDLIGHT)
        vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;//支援頂點運算
    else
        vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof d3dpp);
    d3dpp.BackBufferWidth = WINDOW_WIDTH;
    d3dpp.BackBufferHeight = WINDOW_HEIGHT;
    d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
    d3dpp.BackBufferCount = ;
    d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
    d3dpp.MultiSampleQuality = ;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hwnd;
    d3dpp.Windowed = true;
    d3dpp.EnableAutoDepthStencil = true;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
    d3dpp.Flags = ;
    d3dpp.FullScreen_RefreshRateInHz = ;
    d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

    if (FAILED(d3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,vp, &d3dpp, &g_pd3dDevice)))
        return E_FAIL;


    //
    //擷取顯示卡資訊
    wchar_t TempName[] = L"目前顯示卡号:";

    D3DADAPTER_IDENTIFIER9 Adapter;//定義一個D3DADAPTER_IDENTIFIER9結構體,用于存儲顯示卡資訊
    d3d9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, , &Adapter);//調用GetAdapterIdentifier,擷取顯示卡資訊
    int len = MultiByteToWideChar(CP_ACP, , Adapter.Description, -, NULL, );

    MultiByteToWideChar(CP_ACP, , Adapter.Description, -, g_strAdapterName, len);

    wcscat_s(TempName, g_strAdapterName);
    wcscpy_s(g_strAdapterName, TempName);


    SAFE_RELEASE(d3d9);
    if (FAILED(Object_Init()))
        return E_FAIL;

    return S_OK;
}

#include<tchar.h>
HRESULT Object_Init()
{
    D3DXCreateFont(g_pd3dDevice, , , , , false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, , _T("Calibri"), &g_pTextFPS);
    D3DXCreateFont(g_pd3dDevice, , , , , false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, , L"楷體", &g_pTextAdapterName);
    D3DXCreateFont(g_pd3dDevice, , , , , false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, , L"楷體", &g_pTextHelper);
    D3DXCreateFont(g_pd3dDevice, , , , , false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, , L"楷體", &g_pTextInfo);

    //從X檔案中加載網格資料
    LPD3DXBUFFER pAdjBuffer = NULL;
    LPD3DXBUFFER pMtrlBuffer = NULL;

    D3DXLoadMeshFromX(L"miki.X", D3DXMESH_MANAGED, g_pd3dDevice, &pAdjBuffer,
        &pMtrlBuffer, NULL,&g_dwNumMtrls,&g_pMesh);

    //讀取材質和紋理資料
    D3DXMATERIAL *pMtrls = (D3DXMATERIAL*)pMtrlBuffer->GetBufferPointer();
    g_pMaterials = new D3DMATERIAL9[g_dwNumMtrls];
    g_pTextures = new LPDIRECT3DTEXTURE9[g_dwNumMtrls];

    for (DWORD i = ; i < g_dwNumMtrls; i++) {

        g_pMaterials[i] = pMtrls[i].MatD3D;
        //g_pMaterials[i].Ambient = g_pMaterials[i].Diffuse;
        g_pMaterials[i].Specular = g_pMaterials[i].Diffuse;
        //建立下一紋理
        g_pTextures[i] = NULL;
        //D3DXCreateTextureFromFileA(g_pd3dDevice, pMtrls[i].pTextureFilename, &g_pTextures[i]);
    }

    SAFE_RELEASE(pAdjBuffer);
    SAFE_RELEASE(pMtrlBuffer);

    D3DLIGHT9 light;
    ::ZeroMemory(&light, sizeof(D3DLIGHT9));

    light.Type = D3DLIGHT_DIRECTIONAL;
    light.Ambient= D3DXCOLOR(, , , );
    light.Diffuse= D3DXCOLOR(, , , );
    light.Specular= D3DXCOLOR(,,, );
    light.Direction = D3DXVECTOR3(,, );
    g_pd3dDevice->SetLight(, &light);
    g_pd3dDevice->LightEnable(, true);

    g_pd3dDevice->SetTextureStageState(, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
    g_pd3dDevice->SetTextureStageState(, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);

    g_pd3dDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true);//初始化頂點法向量
    g_pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE, true);

    g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);//開啟背面消隐
    g_pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS);//将深度測試函數設為D3DCMP_LESS

    g_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);//深度測試成功後,更新深度緩存
    //g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(36, 36, 36));//設定環境光

    //開啟Alpha融合
    g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
    //設定融合因子
    g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
    g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

    //設定融合運算方式
    g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
    //各項異性過濾
    g_pd3dDevice->SetSamplerState(, D3DSAMP_MAXANISOTROPY, );
    g_pd3dDevice->SetSamplerState(, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);
    g_pd3dDevice->SetSamplerState(, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);

    return S_OK;
}

VOID Direct3D_Update(HWND hwnd)
{
    g_pDInput->GetInput();

    static FLOAT fPosX = ;
    static FLOAT fPosY = ;
    static FLOAT fPosZ = ;

    if (g_pDInput->GetKeyDown(DIK_1))
    {
        for (DWORD i = ; i < g_dwNumMtrls; i++) {
            g_pMaterials[i].Diffuse.a += ;
        }
    }

    if (g_pDInput->GetKeyDown(DIK_2))
    {
        for (DWORD i = ; i < g_dwNumMtrls; i++)
        {
            g_pMaterials[i].Diffuse.a -= ;
        }
    }

    if (g_pDInput->GetMouseButtonDown())
    {
        fPosX += (g_pDInput->MouseX())*;
        fPosY += (g_pDInput->MouseY())*-;
    }

    //滑鼠滾輪,為觀察點收縮操作
    fPosZ += (g_pDInput->MouseZ())*;

    //鍵盤按鍵控制物體的平移
    if (g_pDInput->GetKeyDown(DIK_A))fPosX -= ;
    if (g_pDInput->GetKeyDown(DIK_D))fPosX += ;
    if (g_pDInput->GetKeyDown(DIK_W))fPosY += ;
    if (g_pDInput->GetKeyDown(DIK_S))fPosY -= ;

    D3DXMatrixTranslation(&g_matWorld, fPosX, fPosY, fPosZ);

    //按住滑鼠右鍵并拖動,為旋轉操作
    static FLOAT fAngleX = D3DX_PI/, fAngleY = D3DX_PI / ;
    if (g_pDInput->GetMouseButtonDown())
    {
        fAngleX += (g_pDInput->MouseX())*-;
        fAngleY += (g_pDInput->MouseY())*;
    }
    //鍵盤按鍵旋轉物體
    if (g_pDInput->GetKeyDown(DIK_UP))fAngleX += ;
    if (g_pDInput->GetKeyDown(DIK_DOWN))fAngleX -= ;
    if (g_pDInput->GetKeyDown(DIK_LEFT))fAngleY += ;
    if (g_pDInput->GetKeyDown(DIK_RIGHT))fAngleY -= ;


    D3DXMATRIX Rx, Ry;
    D3DXMatrixRotationX(&Rx, fAngleX);
    D3DXMatrixRotationY(&Ry, fAngleY);

    g_matWorld = Rx*Ry*g_matWorld;

    g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_matWorld);

    Matrix_Set();

    return VOID();
}

VOID Direct3D_Render(HWND hwnd)
{

    g_pd3dDevice->Clear(, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(,,),,);

    RECT formatrect;
    GetClientRect(hwnd,&formatrect);

    //開始繪制
    g_pd3dDevice->BeginScene();

    for (DWORD i = ; i < g_dwNumMtrls; i++) {
        g_pd3dDevice->SetMaterial(&g_pMaterials[i]);
        g_pd3dDevice->SetTexture(,g_pTextures[i]);
        g_pMesh->DrawSubset(i);
    }


    //在視窗右上角處,顯示每秒幀數
    int charCount = swprintf(g_strFPS, , _T("FPS:%0.3f"), GET_FPS());

    g_pTextFPS->DrawText(NULL, g_strFPS, charCount, &formatrect, DT_TOP | DT_RIGHT, D3DCOLOR_XRGB(, , , ));

    //顯示顯示卡類型名
    g_pTextAdapterName->DrawText(NULL, g_strAdapterName, -, &formatrect, DT_TOP | DT_LEFT, D3DXCOLOR(,,,));

    //輸出繪制資訊
    formatrect.top = ;
    static wchar_t strInfo[] = {  };
    swprintf_s(strInfo, -, L"模型橫坐标:(%.2f,%.2f,%.2f)", g_matWorld._41, g_matWorld._42, g_matWorld._43);

    g_pTextInfo->DrawText(NULL, strInfo, -, &formatrect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DXCOLOR(, , , ));

    formatrect.left = ;
    formatrect.top = ;
    g_pTextHelper->DrawText(NULL, L"控制說明:", -, &formatrect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(, , , ));

    formatrect.top += ;
    g_pTextHelper->DrawText(NULL, L"按住滑鼠左鍵并拖動,平移模型", -, &formatrect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(, , , ));

    formatrect.top += ;
    g_pTextHelper->DrawText(NULL, L"按住滑鼠右鍵并拖動,旋轉模型", -, &formatrect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(, , , ));

    formatrect.top += ;
    g_pTextHelper->DrawText(NULL, L"滑動滑鼠滾輪:拉伸模型", -, &formatrect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(, , , ));

    formatrect.top += ;
    g_pTextHelper->DrawText(NULL, L"W,S,A,D鍵:平移模型", -, &formatrect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(, , , ));

    formatrect.top += ;
    g_pTextHelper->DrawText(NULL, L"上下左右方向鍵:旋轉模型", -, &formatrect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(, , , ));

    formatrect.top += ;
    g_pTextHelper->DrawText(NULL, L"鍵盤上1,2,3,4數字鍵:在四種尋址模式之間切換", -, &formatrect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(, , , ));

    formatrect.top += ;
    g_pTextHelper->DrawText(NULL, L"ESC 鍵盤:退出程式", -, &formatrect, DT_SINGLELINE | DT_NOCLIP | DT_LEFT, D3DCOLOR_RGBA(, , , ));



    g_pd3dDevice->EndScene();
    g_pd3dDevice->Present(NULL, NULL, NULL, NULL);
    return VOID();
}

VOID Direct3D_CleanUp(HWND hwnd)
{
    if (g_pDInput)delete g_pDInput;
    SAFE_RELEASE(g_pIndexBuffer);
    SAFE_RELEASE(g_pVertexBuffer);
    SAFE_RELEASE(g_pTextAdapterName);
    SAFE_RELEASE(g_pTextHelper);
    SAFE_RELEASE(g_pTextInfo);
    SAFE_RELEASE(g_pTextFPS);
    SAFE_RELEASE(g_pd3dDevice);
    return VOID();
}

VOID Matrix_Set()
{
    D3DXMATRIX view;//取景變換矩陣
    D3DXMATRIX projection;//投影變換矩陣
    D3DVIEWPORT9 vp;//視口變換
    D3DXVECTOR3 vEye(, , -);//錄影機的位置
    D3DXVECTOR3 vAt(, , );//觀察點的位置
    D3DXVECTOR3 vUp(, , );//向上的向量
    //取景變換矩陣的設定
    D3DXMatrixLookAtLH(&view,&vEye,&vAt,&vUp);
    g_pd3dDevice->SetTransform(D3DTS_VIEW, &view);

    //投影變換矩陣
    D3DXMatrixPerspectiveFovLH(&projection, D3DX_PI / , (float)((double)WINDOW_WIDTH / WINDOW_HEIGHT), , );
    g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &projection);

    //設定視口變換
    vp.Width = WINDOW_WIDTH;
    vp.Height = WINDOW_HEIGHT;
    vp.X = ;
    vp.Y = ;
    vp.MinZ = ;
    vp.MaxZ = ;
    g_pd3dDevice->SetViewport(&vp);
    return VOID();
}

float GET_FPS()
{
    static float fps = ;   //我們需要計算的FPS值
    static int frameCount = ;//幀數
    static float currentTime = ;//目前時間
    static float lastTime = ;//持續時間
    frameCount++;
    currentTime = timeGetTime()*;
    if (currentTime - lastTime > ) {
        fps = (float)frameCount / (currentTime - lastTime);
        lastTime = currentTime;
        frameCount = ;
    }
    return fps;

    return ;
}


LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
        Direct3D_Render(hwnd);
        ValidateRect(hwnd, NULL);
        break;
    case WM_DESTROY:
        Direct3D_CleanUp(hwnd);
        PostQuitMessage();
        break;
    case WM_KEYDOWN:
        if (wParam == VK_ESCAPE)
            DestroyWindow(hwnd);
        break;
    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    //return LRESULT();
}
           

Copyright© by 寒江雪

Date:2016.12.23

繼續閱讀