天天看點

【寒江雪】四大紋理過濾方式和四大尋址模式四大紋理過濾方式概述設定紋理過濾方式四大紋理尋址方式紋理映射總結示例代碼

四大紋理過濾方式

概述

  在Direct3D中,有四種紋理過濾方式,它們分别是:

* 最近點采樣過濾(nearst-pointsampling)

* 線性紋理過濾(lineartexture filtering)

* 各項異性過濾(anisotropictexture filtering)

* 多級漸進過濾(texturefiltering with mipmaps)

設定紋理過濾方式

  設定紋理過濾方式通常都是用IDirect3DDevice9::SetSamplerState函數,這個函數用于設定紋理的各種采樣屬性,下面我們來看一下這個函數:

HRESULT SetSamplerState(
        [in] DWORD Sampler,
        [in] D3DSAMPLERSTATETYPE Type,
        [in] DWORD Value
    );
           
  • DWORD Sampler:指定為哪一層紋理設定采樣狀态,在Direct3D中支援最多8層紋理,是以這個值取值顯然就是0~7了。如果使用單層紋理進行渲染的話,這個值取0即可
  • D3DSAMPLERSTATETYPE Type:用來指定對哪種紋理采用屬性來進行操作。從參數的類型來看我們就知道需要在枚舉體D3DSAMPLERSTATETYPE中進行取值,下面就是這個枚舉體的定義:
typedef enum D3DSAMPLERSTATETYPE{
    D3DSAMP_ADDRESSU=,
    D3DSAMP_ADDRESSV=,
    D3DSAMP_ADDRESSW=,
    D3DSAMP_BORDERCOLOR=,
    D3DSAMP_MAGFILTER=,
    D3DSAMP_MINFILTER=,
    D3DSAMP_MIPFILTER=,
    D3DSAMP_MIPMAPLODBIAS=,
    D3DSAMP_MAXMIPLEVEL=,
    D3DSAMP_MAXANISOTROPY=,
    D3DSAMP_SRGBTEXTURE=,
    D3DSAMP_ELEMENTINDEX=,
    D3DSAMP_DMAPOFFSET=,
    D3DSAMP_FORCE_DWORD=
}D3DSAMPLERSTATETYPE,*LPD3DSAMPLERSTATETYPE;
           
  • DWORD Value:這個參數和第二個參數Type聯系很緊密,就是對第二個參數指定的屬性進行值的設定的。
    1. 最近點采樣過濾

        Direct3D計算得到的紋理過濾元素通常都是一個浮點值,當使用最近點采樣時,Direct3D會複制與這個浮點值位址最接近的整數位址的紋理元素的顔色。

      • 設定最近點采樣過濾

        設定最近點采樣的具體方法,就是調用我們上面提到的IDirect3DDevice9::SetSamplerState函數,通過第二個參數Type的不同取值,分别設定紋理過濾的放大過濾器和縮小過濾器就可以了。

        g_pd3dDevice->SetSamplerState(,D3DSAMP_MAGFILTER,D3DTEXF_POINT);
        g_pd3dDevice->SetSamplerState(,D3DSAMP_MINFILTER,D3DTEXF_POINT);
                   
        另外需要注意的是,如果紋理大小和螢幕圖元的實際大小相近,那麼采用最近點采樣過濾對圖像品質的影響不大。但是,如果紋理大小和螢幕圖元的實際大小差不多,則圖像的精度就會受到影響,進而圖像品質就會差強人意,出現閃爍或者失真的現象。
    2. 線性紋理過濾

        與最近采樣點過濾方式相比,能有效地提高圖檔的顯示品質,且對系統性能影響不大。線性紋理過濾取得與計算得到的紋理元素的浮點位址最接近的上下左右四個紋理元素,對這四個紋理元素進行權重平均,進而得到最終顯示的顔色值。因為是在單一紋理層上的線性過濾,而且是從x,y方向上的線性過濾,是以我們也稱通常的線性紋理過濾為雙線性紋理過濾。

        設定線性紋理過濾就是使用SetSamplerState函數,第二個參數還是D3DSAMP_MAGFILTER和D3DSAMP_MINFILTER,有不同的地方就是第三個采參數改為線性紋理過濾專屬的D3DTEXF_LINEAR就可以了。下面是一個執行個體:

g_pd3dDevice->SetSamplerState(,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
           

  目前市面上大多數顯示卡都為線性紋理過濾進行了針對性優化,是以使用這種紋理過濾方式能獲得比較好的圖形品質,而且也不吃資源。線性紋理過濾是目前使用最廣泛的紋理過濾方式.

  1. 各向異性紋理過濾

      首先普及一個知識,在很多時候,三維物體表面不可能是完全平面的,當三維物體的表面和投影平面不平行時,它在螢幕上的投影會有拉長和扭曲的現象,這種現象稱為各向異性(Anisotropic)。當一個各項異性的圖元映射到紋理元素上時,它的形狀就會發生扭曲。而Direct3D會根據螢幕像素反向轉換到紋理元素的延長度,來決定各項異性的程度。

      設定各項異性的方法:

    g_pd3dDevice->SetSamplerState(,D3DSAMP_MAXANISOTROPY,);
        g_pd3dDevice->SetSamplerState(,D3DSAMP_MAGFILTER,D3DTEXF_ANISOTROPIC);
        g_pd3dDevice->SetSamplerState(,D3DSAMP_MINFILTER,D3DTEXF_ANISOTROPIC);
               
  2. 多級漸進紋理過濾

      首先需要知道的是,這個多級漸進紋理過濾通常不是獨當一面的紋理過濾方式,他需要找個伴,也就是需要和我們上面講到的四大紋理過濾方式其他三種之一結合使用。

      多級漸進紋理就是由一組分辨率逐漸降低的紋理序列組成的,每一級紋理的寬度和高度都是上一級紋理的寬度和高度的一半。需要注意,紋理的寬度和高度并不一定相等。也就是說,他們不一定是正方形。

      Direct3D在繪圖時,自動選擇一幅與物體大小最接近的紋理進行渲染。當物體離投影面很遠時,Direct3D會選擇一張尺寸較小,分辨率較低的進行渲染;當物體離投影面較近時,Direct3D則會選擇一張尺寸較大,分辨率較高的紋理進行渲染。

    &esmp; Direct3D将多級漸進紋理描繪成一系列互相聯系的表面,當建立一個漸進紋理時,分辨率最高的紋理位于開始處,并與下一級紋理互相聯系,直到達到分辨率最低的一級紋理。

      Direct3D能夠估計出多級漸進紋理鍊中哪幅紋理的分辨率最接近想要的輸出結果,然後能将像素映射到紋理空間。而且當最終顯示的圖形大小介于任意兩級圖形之間時,Direct3D能智能地取得兩級紋理的相應元素進行混合後顯示出來。

      多級漸進紋理過濾能夠有效地提高圖形渲染速度,當物體離投影平面較遠的時候,Direct3D會選擇一張尺寸較小的紋理進行渲染,而無需經過複雜的諸如各項異性紋理過濾。并且由于這時紋理需要的顯存比不适用多級漸進紋理要小,是以還能有效地減小載入顯存的時間。

    • 多級漸進紋理的生成
      • 使用D3DXCreateTextureFromFileEx方法:
        HRESULT D3DXCreateTextureFromFileEx(
                    _In_ LPDIRECT3DDEVICE9 pDevice,//D3D裝置對象接口
                    _In_ LPCTSTR pSrcFile,//紋理貼圖的檔案位址
                    _In_ UINT Width,//紋理寬度,為0的話表示使用紋理貼圖的寬度
                    _In_ UINT Height,//紋理高度,為0的話表示使用紋理貼圖的高度
                    _In_ UINT MipLevels,//生成的漸進紋理的級數
                    _In_ DWORD Usage,//用法的标志,通常設為0
                    _In_ D3DFORMAT Format,//紋理貼圖的方式
                    _In_ D3DPOOL Pool,//儲存紋理的方式
                    _In_ DWORD Filter,//紋理過濾的方式
                    _In_ DWORD MipFilter,//生成紋理序列的過濾方式
                    _In_ D3DCOLOR ColorKey,//替換Alpha值得顔色值
                    _Inout_ D3DXIMAGE_INFO *pSrcInfo,//通常設為NULL就可以了
                    _Out_ PALETTEENTRY *pPalette,//調色闆的位址,通常設為NULL就可以了
                    _Out_ LPDIRECT3DTEXTURE9 *PPTexture//紋理接口對象
                );
                   
      • 這個函數的一個調用執行個體
      D3DXCreateTextureFromFileEx(g_pd3dDevice,L"pal5q.jpg",
          ,,,,
          D3DFMT_X8R8G8B8,D3DPOOL_MANAGED,D3DX_DEFAULT,
          D3DX_DEFAULT,xFF000000,,,&g_pMipTexture);
                 
      • 設定多級漸進紋理過濾
        //首先設定線性紋理過濾
            g_pd3dDevice->SetSamplerState(,D3DSAMP_MIPFILTER,D3DTEXD3DTEXF_LINEAR);
            //然後設定堆積漸進紋理過濾
            g_pd3dDevice->SetSamplerState(,D3DSAMP_MIPMAPLODBIAS,D3DTEXF_LINEAR);
            g_pd3dDevice->SetSamplerState(,D3DSAMP_MAXMIPLEVEL,);
                   

四大紋理尋址方式

  我們定義的紋理坐标u,v範圍在[0,1]之間。為了處理超出[0,1]之外的紋理坐标值,就需要通過紋理尋址模式來控制了。

  這其中有四種尋址模式:

* 重複尋址模式(wrap texture address mode)

* 鏡像紋理尋址模式(mirror texture address mode)

* 夾取紋理尋址模式(clamp texture address mode)

* 邊框顔色紋理尋址(border color texture address mode)

  1. 重複尋址模式

      重複尋址模式是Direct3D中預設的尋址模式,這種尋址模式允許在每個整數連接配接點處重複上一個整數的紋理。

      如果我們要手動啟用這種紋理模式或者是從其他的尋址模式想換回來的話,還是用哪個SetSamplerState。如下兩句連用,分别對U軸和V軸啟用重複紋理尋址模式:

//設定重複紋理尋址模式
g_pd3dDevice->SetSamplerState(,D3DSAMP_ADDRESSU,D3DTADDRESS_WRAP);
g_pd3dDevice->SetSamplerState(,D3DSAMP_ADDRESSV,D3DTADDRESS_WRAP);
           

  為友善了解,貼出一個用于定義四大尋址模式類型中用于SetSamplerState第三個參數取值的枚舉體:

typedef enum D3DTEXTUREADDRESS{
    D3DTADDRESS_WRAP=,//重複尋址模式
    D3DTADDRESS_MIRROR=,//鏡像尋址模式
    D3DTADDRESS_CLAMP=,//夾取尋址模式
    D3DTADDRESS_hljs-number">4,//邊框顔色尋址模式
    D3DTADDRESS_MIRRORONCE=,
    D3DTADDRESS_FORCE_WORD=
}D3DTEXTUREADDRESS,*LPD3DTEXTUREADDRESS;
           
  1. 鏡像尋址模式

      使用鏡像紋理尋址模式時,Direct3D會在每個整數紋理坐标連接配接處自動複制并翻轉紋理,且為兩兩成對翻轉。

    &esmp; 下面給出一個例子:

g_pd3dDevice->SetSamplerState(,D3DSAMP_ADDRESSU,D3DTADDRESS_MIRROR);
g_pd3dDevice->SetSamplerState(,D3DSAMP_ADDRESSV,D3DTADDRESS_MIRROR);
           
  1. 夾取尋址模式

     &ems;夾取紋理尋址模式将紋理坐标夾取在[0,1]之間,也就是說,在[0.0,1.0]之間就是把紋理複制一遍,然後對于[0.0,1.0]之外的内容,将邊緣的内容沿着U軸,V軸進行下一延伸。

      下面是一個例子:

//設定夾取紋理尋址模式
g_pd3dDevice->SetSamplerState(,D3DSAMP_ADDRESSU,D3DTADDRESS_CLAMP);
g_pd3dDevice->SetSamplerState(,D3DSAMP_ADDRESSV,D3DTADDRESS_CLAMP);
           
  1. 邊框顔色尋址模式

    &esmp; 邊框顔色尋址模式就是在[0,1]之間繪制下一紋理,然後[0,1]之外的内容就用邊框顔色填充了。

      下面給出一個執行個體:

//設定邊框紋理尋址模式
g_pd3dDevice->SetSamplerState(,D3DSAMP_ADDRESSU,D3DTADDRESS_BORDER);
g_pd3dDevice->SetSamplerState(,D3DSAMP_ADDRESSV,D3DTADDRESS_BORDER);
           

紋理映射總結

  1. 紋理映射四步曲
    • 頂點的定義
      • FVF頂點格式的定義,最多八層紋理
    • 頂點的通路
      • 通路頂點緩存内容,紋理坐标的填充
    • 紋理的建立
      • IDIRECT3DTEXTURE9 接口對象
      • D3DXCreateTextureFromFile方法
    • 紋理的啟用
      • SetTexture方法
        //頂點的定義
        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)
        
        
        //頂點的通路
        CUSTOMVERTEX *pVertices=NULL;
        if(FAILED(g_pVertexBuffer->Lock(,sizeof(CUSTOMVERTEX),(void**)&pVertices,)))
            return E_FAIL;
        pVertices[]=...;
        pVertices[]=...;
        pVertices[]=...;
        pVertices[]=...;
        g_pVertexBuffer->UnLock();
        
        //紋理的建立
        LPDIRECT3DTEXTURE9 g_pTexture=NULL;//紋理接口對象
        D3DXCreateTextureFromFile(g_pd3dDevice,L"pal5q.jpg",&g_pTexture);//建立紋理
        
        //紋理的啟用
        g_pd3dDevice->BeginScene();
        g_pd3dDevice->SetTexture(,&g_pTexture);
        g_pd3dDevice->EndScene();
                   
  2. 四大紋理過濾模式
    • 最近點采樣模式

      g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_POINT); g_pd3dDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_POINT);

    • 線性紋理過濾

      g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR); g_pd3dDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);

    • 各項異性紋理過濾

      g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAXANISOTROPY,3); g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTED3DTEXF_ANISOTROPIC); g_pd3dDevice->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_ANISOTROPIC);

    • 多級漸進過濾

      g_pd3dDevice->SetSamplerState(0,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR); g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAXMIPLEVEL,16);

  3. 四大紋理尋址模式
    • 重複尋址模式

      g_pd3dDevice->SetSamplerState(0,D3DSAMP_ADDRESSU,D3DTADDRESS_WRAP); g_pd3dDevice->SetSamplerState(0,D3DSAMP_ADDRESSV,D3DTADDRESS_WRAP);

    • 鏡像紋理尋址模式

      g_pd3dDevice->SetSamplerState(0,D3DSAMP_ADDRESSU,D3DTADDRESS_MIRROR); g_pd3dDevice->SetSamplerState(0,D3DSAMP_ADDRESSV,D3DTADDRESS_MIRROR);

    • 夾取尋址模式

      g_pd3dDevice->SetSamplerState(0,D3DSAMP_ADDRESSU,D3DTADDRESS_CLAMP); g_pd3dDevice->SetSamplerState(0,D3DSAMP_ADDRESSV,D3DTADDRESS_CLAMP);

    • 邊框顔色尋址模式

      g_pd3dDevice->SetSamplerState(0,D3DSAMP_ADDRESSU,D3DTADDRESS_BORDER); g_pd3dDevice->SetSamplerState(0,D3DSAMP_ADDRESSV,D3DTADDRESS_BORDER);

示例代碼

#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;  //索引緩存數組


LPDIRECT3DTEXTURE9 g_pTexture = 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);

    //建立頂點緩存
    if (FAILED(g_pd3dDevice->CreateVertexBuffer( * sizeof(CUSTOMVERTEX), , D3DFVF_CUSTOMVERTEX,
        D3DPOOL_DEFAULT, &g_pVertexBuffer, NULL)))
    {
        return E_FAIL;
    }

    //建立索引緩存
    if (FAILED(g_pd3dDevice->CreateIndexBuffer( * sizeof(WORD), , D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIndexBuffer, NULL)))
    {
        return E_FAIL;
    }

    //頂點的通路
    //填充頂點緩存
    CUSTOMVERTEX *pVertices=NULL;
    if (FAILED(g_pVertexBuffer->Lock(,  * sizeof(CUSTOMVERTEX), (void**)&pVertices, )))
    {
        return E_FAIL;
    }
    // 正面頂點資料
    pVertices[] = CUSTOMVERTEX(-, , -, , );
    pVertices[] = CUSTOMVERTEX(, , -, , );
    pVertices[] = CUSTOMVERTEX(, -, -, , );
    pVertices[] = CUSTOMVERTEX(-, -, -, , );

    // 背面頂點資料
    pVertices[] = CUSTOMVERTEX(, , , , );
    pVertices[] = CUSTOMVERTEX(-, , , , );
    pVertices[] = CUSTOMVERTEX(-, -, , , );
    pVertices[] = CUSTOMVERTEX(, -, , , );

    // 頂面頂點資料
    pVertices[] = CUSTOMVERTEX(-, , , , );
    pVertices[] = CUSTOMVERTEX(, , , , );
    pVertices[] = CUSTOMVERTEX(, , -, , );
    pVertices[] = CUSTOMVERTEX(-, , -, , );

    // 底面頂點資料
    pVertices[] = CUSTOMVERTEX(-, -, -, , );
    pVertices[] = CUSTOMVERTEX(, -, -, , );
    pVertices[] = CUSTOMVERTEX(, -, , , );
    pVertices[] = CUSTOMVERTEX(-, -, , , );

    // 左側面頂點資料
    pVertices[] = CUSTOMVERTEX(-, , , , );
    pVertices[] = CUSTOMVERTEX(-, , -, , );
    pVertices[] = CUSTOMVERTEX(-, -, -, , );
    pVertices[] = CUSTOMVERTEX(-, -, , , );

    // 右側面頂點資料
    pVertices[] = CUSTOMVERTEX(, , -, , );
    pVertices[] = CUSTOMVERTEX(, , , , );
    pVertices[] = CUSTOMVERTEX(, -, , , );
    pVertices[] = CUSTOMVERTEX(, -, -, , );

    g_pVertexBuffer->Unlock();

    //填充索引緩存
    WORD *pIndices = NULL;
    g_pIndexBuffer->Lock(, , (void**)&pIndices, );

    // 正面索引資料
    pIndices[] = ; pIndices[] = ; pIndices[] = ;
    pIndices[] = ; pIndices[] = ; pIndices[] = ;

    // 背面索引資料
    pIndices[] = ; pIndices[] = ; pIndices[] = ;
    pIndices[] = ; pIndices[] = ; pIndices[] = ;

    // 頂面索引資料
    pIndices[] = ; pIndices[] = ; pIndices[] = ;
    pIndices[] = ; pIndices[] = ; pIndices[] = ;

    // 底面索引資料
    pIndices[] = ; pIndices[] = ; pIndices[] = ;
    pIndices[] = ; pIndices[] = ; pIndices[] = ;

    // 左側面索引資料
    pIndices[] = ; pIndices[] = ; pIndices[] = ;
    pIndices[] = ; pIndices[] = ; pIndices[] = ;

    // 右側面索引資料
    pIndices[] = ; pIndices[] = ; pIndices[] = ;
    pIndices[] = ; pIndices[] = ; pIndices[] = ;

    g_pIndexBuffer->Unlock();

    //紋理的建立
    D3DXCreateTextureFromFile(g_pd3dDevice, L"pal5q.jpg", &g_pTexture);

    // 設定材質
    D3DMATERIAL9 mtrl;
    ::ZeroMemory(&mtrl, sizeof(mtrl));
    mtrl.Ambient = D3DXCOLOR(, , , );
    mtrl.Diffuse= D3DXCOLOR(, , , );
    mtrl.Specular= D3DXCOLOR(, , , );
    //mtrl.Power = 5.0f;
    g_pd3dDevice->SetMaterial(&mtrl);//設定材質 待會兒全是用這個材質畫圖
    g_pd3dDevice->SetSamplerState(, D3DSAMP_MAXANISOTROPY, );
    g_pd3dDevice->SetSamplerState(, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);
    g_pd3dDevice->SetSamplerState(, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
    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->SetRenderState(D3DRS_NORMALIZENORMALS, true);//初始化頂點法向量
    g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);//開啟背面消隐
    g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(, , ));//設定環境光


    return S_OK;
}

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

    if (g_pDInput->GetKeyDown(DIK_1))//若數字1被按下,進行實體填充
        g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
    if (g_pDInput->GetKeyDown(DIK_2))//若數字2被按下,進行線框填充
        g_pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
    if (g_pDInput->GetKeyDown(DIK_3))
    {
        g_pd3dDevice->SetSamplerState(, D3DSAMP_MAXANISOTROPY, );
        g_pd3dDevice->SetSamplerState(, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);
        g_pd3dDevice->SetSamplerState(, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
    }
    if (g_pDInput->GetKeyDown(DIK_4))
    {
        g_pd3dDevice->SetSamplerState(, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
        g_pd3dDevice->SetSamplerState(, D3DSAMP_MINFILTER, D3DTEXF_POINT);
    }

    if (g_pDInput->GetKeyDown(DIK_5))
    {
        g_pd3dDevice->SetSamplerState(, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
        g_pd3dDevice->SetSamplerState(, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
    }

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

    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();
    g_pd3dDevice->SetStreamSource(, g_pVertexBuffer, , sizeof(CUSTOMVERTEX));//将包含頂點資訊的緩存與渲染流水線相關聯
    g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);  //指定我們使用的靈活頂點格式宏名
    g_pd3dDevice->SetIndices(g_pIndexBuffer);   //設定索引緩存
    g_pd3dDevice->SetTexture(, g_pTexture);//設定紋理
    g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, , , , , );//利用索引緩存配合頂點緩存繪制圖形

    //在視窗右上角處,顯示每秒幀數
    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

繼續閱讀