天天看点

【寒江雪】四大纹理过滤方式和四大寻址模式四大纹理过滤方式概述设置纹理过滤方式四大纹理寻址方式纹理映射总结示例代码

四大纹理过滤方式

概述

  在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

继续阅读