DirectX11触ってみた

ふと思い立ってDirectX11を触ってみた。

DirectX自体久方ぶりなもので、しかも9.0cから10をすっ飛ばして11なので、出てくる用語がなんのこっちゃですが、気にせず行こう。

とりあえず、環境設定してウィンドウ作ってデバイス初期化して四角形ポリゴン表示までをざっと。

■環境設定

□1:まずはDirectXSDKをダウンロードしてインストール
   http://www.microsoft.com/download/en/details.aspx?id=6812

□2:次にVisualStudioのプロジェクト設定で以下のパスを追加

 インクルードディレクトリ:$(DXSDK_DIR)Include
 ライブラリディレクトリ:$(DXSDK_DIR)Lib\x86

□3:VisualStudioを再起動

□4:完了!



■ソース

//----------------------------------------------------------------------
//! @file main.cpp
//----------------------------------------------------------------------

//----------------------------------------------
// includes
//----------------------------------------------
#include <stdio.h>
#include <windows.h>
#include <D3DX11async.h>
#include <d3d11.h>
#include <d3dx11.h>
#include <xnamath.h>
#include <d3dcompiler.h>

//----------------------------------------------
// lib
//----------------------------------------------
#pragma comment(lib,"d3d11.lib")

#if defined(DEBUG) || defined(_DEBUG)
#pragma comment(lib,"d3dx11d.lib")
#else
#pragma comment(lib,"d3dx11.lib")
#endif

//----------------------------------------------
// define
//----------------------------------------------
#define SAFE_CLEARSTATE(p)  if (p) { p->ClearState();    p=NULL; }
#define SAFE_RELEASE(p)     if (p) { p->Release();        p=NULL; }
#define ARRAY_SIZE(a)       (sizeof(a) / sizeof(a[0]))

//----------------------------------------------
// global variable
//----------------------------------------------

// アプリケーション
static TCHAR*           APPLICATION_NAME            = L"dx11_initialize_sample";

// シェーダー関係
static TCHAR*           SHADER_FILE_PATH            = L"data/Shader/basic.fx";
static const char*      VERTEX_SHADER_FUNC_NAME     = "VertexShaderMain";
static const char*      VERTEX_SHADER_VERSION       = "vs_4_0";
static const char*      PIXEL_SHADER_FUNC_NAME      = "PixelShaderMain";
static const char*      PIXEL_SHADER_VERSION        = "ps_4_0";
static const float      CLEAR_COLOR[4]              = { 0.5f,0.5f,0.5f,1.0f };

// ポリゴン関係
static const float      POLYGON_EDGE_LENGTH         = 0.1f;
static const float      POLYGON_DEPTH               = 0.5f;
static const float      POLYGON_COLOR[4]            = { 0.8f,0.0f,0.2f,1.0f };

//----------------------------------------------
// struct
//----------------------------------------------
struct CustomVertex
{
    XMFLOAT3 position;
    XMFLOAT4 color;
};

//----------------------------------------------
// function front decralation
//----------------------------------------------
LRESULT CALLBACK WindowProcedure(HWND hWnd,UINT uMsg,WPARAM wp,LPARAM lp);
HRESULT CompileShaderFromFile(TCHAR* filename,LPCSTR entry_point,LPCSTR shader_model,ID3DBlob**    blob_out);

//----------------------------------------------------------------------
//! @function    main
//! @brief        メインエントリ
//----------------------------------------------------------------------
int main(int argc,char** argv)
{
    //--------------------------------------------
    // 変数
    //--------------------------------------------
    ID3D11Device*           pDevice;
    ID3D11DeviceContext*    pDeviceContext;
    IDXGISwapChain*         pSwapChain;

    D3D_DRIVER_TYPE         driverType;
    D3D_FEATURE_LEVEL       featureLevel;

    ID3D11RenderTargetView* pRenderTargetView;
    ID3D11VertexShader*     pVertexShader;
    ID3D11PixelShader*      pPixelShader;
    ID3D11InputLayout*      pVertexLayout;
    ID3D11Buffer*           pVertexBuffer;
    ID3DBlob*               pVertexShaderBlob;

    HRESULT hr = S_OK;

    //--------------------------------------------
    // ウィンドウ作成
    //--------------------------------------------

    // インスタンスハンドルを持っておく
    HINSTANCE hInst = GetModuleHandle(NULL);

    // ウィンドウのパラメータ設定
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProcedure;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;
    wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL,IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = APPLICATION_NAME;
    wc.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
    if (!RegisterClassEx(&wc))
    {
        printf("failed register class.\n");
        return 0;
    }

    // ウィンドウサイズを設定
    const unsigned int WINDOW_WIDTH  = 640;
    const unsigned int WINDOW_HEIGHT = 480;

    RECT windowRect =  { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
    AdjustWindowRect(&windowRect,WS_OVERLAPPEDWINDOW,FALSE);

    // ウィンドウ作成
    HWND hWnd = CreateWindow(APPLICATION_NAME,
                             APPLICATION_NAME,
                             WS_OVERLAPPEDWINDOW,
                             CW_USEDEFAULT,
                             CW_USEDEFAULT,
                             WINDOW_WIDTH,
                             WINDOW_HEIGHT,
                             NULL,
                             NULL,
                             hInst,
                             NULL);

    // 作成失敗?
    if (!hWnd)
    {
        printf("failed RegisterClassEx.\n");
        return 0;
    }

    // ウィンドウ表示
    ShowWindow(hWnd,SW_SHOWNORMAL);

    //--------------------------------------------
    // デバイスとスワップチェインを作成
    //--------------------------------------------

#if defined(DEBUG) || defined(_DEBUG)
    const unsigned int createDeviceFlag = D3D11_CREATE_DEVICE_DEBUG;
#else
    const unsigned int createDeviceFlag = 0;
#endif

    // ドライバータイプ
    const D3D_DRIVER_TYPE driverTypes[] = 
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };
    const unsigned int driverTypeNum = ARRAY_SIZE(driverTypes);

    // 機能レベル
    const D3D_FEATURE_LEVEL featureLevels[] = 
    {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };
    const unsigned int featureLevelNum = ARRAY_SIZE(featureLevels);

    // スワップチェインパラメータ設定
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd,sizeof(sd));
    sd.BufferCount= 1;
    sd.BufferDesc.Width = WINDOW_WIDTH;
    sd.BufferDesc.Height = WINDOW_HEIGHT;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow =hWnd;
    sd.SampleDesc.Count= 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed= TRUE;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
    sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

    // デバイスとスワップチェインを作成する
    for (UINT driverTypesIndex=0;driverTypesIndex<driverTypeNum;++driverTypesIndex)
    {
        driverType = driverTypes[driverTypesIndex];

        hr = D3D11CreateDeviceAndSwapChain( NULL,
                                            driverType,
                                            NULL,
                                            createDeviceFlag,
                                            featureLevels,
                                            featureLevelNum,
                                            D3D11_SDK_VERSION,
                                            &sd,
                                            &pSwapChain,
                                            &pDevice,
                                            &featureLevel,
                                            &pDeviceContext);

        if (SUCCEEDED(hr)) break;
    }

    if (FAILED(hr)) 
    {
        printf("failed D3D11CreateDeviceAndSwapChain.\n");
        return 0;
    }

    //------------------------------------------
    // レンダーターゲットを設定
    //------------------------------------------

    // バックバッファ取得
    ID3D11Texture2D* pBackBuffer= NULL;
    hr = pSwapChain->GetBuffer(0,__uuidof(ID3D11Texture2D),(LPVOID*)&pBackBuffer);
    if (FAILED(hr))
    {
        printf("failed GetBuffer\n");
        return hr;
    }

    // レンダーターゲットを作成
    hr = pDevice->CreateRenderTargetView(pBackBuffer,NULL,&pRenderTargetView);
    SAFE_RELEASE(pBackBuffer);

    if (FAILED(hr))
    { 
        printf("failed CreateRenderTargetView.\n");
        return hr;
    }

    // レンダーターゲットを設定する
    pDeviceContext->OMSetRenderTargets(1,&pRenderTargetView,NULL);

    //---------------------------------------------------
    // ビューポートの設定
    //---------------------------------------------------
    D3D11_VIEWPORT viewport;
    viewport.Width = (FLOAT)WINDOW_WIDTH;
    viewport.Height = (FLOAT)WINDOW_HEIGHT;
    viewport.MinDepth = 0.0f;
    viewport.MaxDepth = 1.0f;
    viewport.TopLeftX = 0;
    viewport.TopLeftY = 0;
    pDeviceContext->RSSetViewports(1,&viewport);

    //---------------------------------------------------
    // 頂点シェーダーを設定(blobは後で使うので保存する)
    //---------------------------------------------------
    hr = CompileShaderFromFile(SHADER_FILE_PATH,VERTEX_SHADER_FUNC_NAME,VERTEX_SHADER_VERSION,&pVertexShaderBlob);
    if (FAILED(hr))
    {
        printf("failed CompileShaderFromFile: %s fn: %s version: %s\n",
                SHADER_FILE_PATH,VERTEX_SHADER_FUNC_NAME,VERTEX_SHADER_VERSION);
        return hr;
    }

    pDevice->CreateVertexShader(pVertexShaderBlob->GetBufferPointer(),pVertexShaderBlob->GetBufferSize(),NULL,&pVertexShader);
    if (FAILED(hr))
    {
        pVertexShaderBlob->Release();
        pVertexShaderBlob = NULL;
        return hr;
    }

    //--------------------------------------
    // ピクセルシェーダを設定
    //--------------------------------------
    ID3DBlob* pBlob = NULL;
    hr = CompileShaderFromFile(SHADER_FILE_PATH,PIXEL_SHADER_FUNC_NAME,PIXEL_SHADER_VERSION,&pBlob);
    if (FAILED(hr))
    {
        printf("failed CompileShaderFromFile: %s fn: %s version: %s\n",
                SHADER_FILE_PATH,PIXEL_SHADER_FUNC_NAME,PIXEL_SHADER_VERSION);
        return hr;
    }

    pDevice->CreatePixelShader(pBlob->GetBufferPointer(),pBlob->GetBufferSize(),NULL,&pPixelShader);
    SAFE_RELEASE(pBlob);


    //---------------------------------------------------
    // 頂点入力レイアウトを設定する
    //---------------------------------------------------
    const D3D11_INPUT_ELEMENT_DESC layout[] = 
    {
        {"POSITION", 0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,                    D3D11_INPUT_PER_VERTEX_DATA, 0 },
        {"COLOR",    0,DXGI_FORMAT_R32G32B32_FLOAT,0,sizeof(float) * 3,    D3D11_INPUT_PER_VERTEX_DATA, 0 }
    };
    UINT numElements = ARRAY_SIZE(layout);

    hr = pDevice->CreateInputLayout(layout,
                                    numElements,
                                    pVertexShaderBlob->GetBufferPointer(),
                                    pVertexShaderBlob->GetBufferSize(),
                                    &pVertexLayout);

    SAFE_RELEASE(pVertexShaderBlob);

    if (FAILED(hr)) 
    {
        printf("failed CreateInputLayout.\n");
        return hr;
    }

    pDeviceContext->IASetInputLayout(pVertexLayout);

    //---------------------------------------
    // 頂点バッファの作成
    //---------------------------------------
    static const CustomVertex VERTICES[] = 
    {
        {XMFLOAT3(+POLYGON_EDGE_LENGTH,+POLYGON_EDGE_LENGTH,POLYGON_DEPTH),XMFLOAT4(POLYGON_COLOR)},
        {XMFLOAT3(+POLYGON_EDGE_LENGTH,-POLYGON_EDGE_LENGTH,POLYGON_DEPTH),XMFLOAT4(POLYGON_COLOR)},
        {XMFLOAT3(-POLYGON_EDGE_LENGTH,-POLYGON_EDGE_LENGTH,POLYGON_DEPTH),XMFLOAT4(POLYGON_COLOR)},

        {XMFLOAT3(+POLYGON_EDGE_LENGTH,+POLYGON_EDGE_LENGTH,POLYGON_DEPTH),XMFLOAT4(POLYGON_COLOR)},
        {XMFLOAT3(-POLYGON_EDGE_LENGTH,-POLYGON_EDGE_LENGTH,POLYGON_DEPTH),XMFLOAT4(POLYGON_COLOR)},
        {XMFLOAT3(-POLYGON_EDGE_LENGTH,+POLYGON_EDGE_LENGTH,POLYGON_DEPTH),XMFLOAT4(POLYGON_COLOR)},

    };
    static const unsigned int VERTICES_NUM = ARRAY_SIZE(VERTICES);

    // 頂点バッファパラメータ設定
    D3D11_BUFFER_DESC bd;
    ZeroMemory(&bd,sizeof(bd));
    bd.Usage = D3D11_USAGE_DEFAULT;
    bd.ByteWidth= sizeof(CustomVertex) * VERTICES_NUM;
    bd.BindFlags= D3D11_BIND_VERTEX_BUFFER;
    bd.CPUAccessFlags = 0;

    // サブリソース設定
    D3D11_SUBRESOURCE_DATA initData;
    ZeroMemory(&initData,sizeof(initData));
    initData.pSysMem = VERTICES;

    // 頂点バッファ生成
    hr = pDevice->CreateBuffer(&bd,&initData,&pVertexBuffer);
    if (FAILED(hr))
    {
        printf("failed CreateBuffer\n");
        return hr;
    }

    // 入力アセンブラに頂点バッファを設定
    UINT stride = sizeof(CustomVertex);
    UINT offset = 0;
    pDeviceContext->IASetVertexBuffers(0,1,&pVertexBuffer,&stride,&offset);
    pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    //---------------------------------------
    // メインループ
    //---------------------------------------
    MSG msg = { 0 };

    while ( WM_QUIT != msg.message )
    {
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);

        }else
        {
            pDeviceContext->ClearRenderTargetView(pRenderTargetView,CLEAR_COLOR);
            pDeviceContext->VSSetShader(pVertexShader,NULL,0);
            pDeviceContext->PSSetShader(pPixelShader,NULL,0);
            pDeviceContext->Draw(VERTICES_NUM,0);
            pSwapChain->Present(0,0);
        }
    
    }

    //---------------------------------------
    // 後片付け
    //---------------------------------------
    SAFE_CLEARSTATE(pDeviceContext);
    SAFE_RELEASE(pVertexBuffer);
    SAFE_RELEASE(pVertexLayout);
    SAFE_RELEASE(pVertexShader);
    SAFE_RELEASE(pPixelShader);
    SAFE_RELEASE(pRenderTargetView);
    SAFE_RELEASE(pSwapChain);
    SAFE_RELEASE(pDeviceContext);
    SAFE_RELEASE(pDevice);

    return (int)msg.wParam;
}

//----------------------------------------------
//! @brief ウィンドウプロシージャ
//----------------------------------------------
LRESULT CALLBACK WindowProcedure(HWND hWnd,UINT uMsg,WPARAM wp,LPARAM lp)
{
    switch (uMsg)
    {
    case WM_DESTROY:
        {
            PostQuitMessage( 0 );
        }
        break;
    default:
        break;
    }

    return DefWindowProc(hWnd,uMsg,wp,lp);
}

//----------------------------------------------------
//! @brief        FXファイルからシェーダーをコンパイル
//! @param[in]    filename:ファイル名
//! @param[in]    entry_point:エントリポイント名
//! @param[in]    shader_model:シェーダーモデル
//! @param[in]  blob_out:シェーダーブロブ
//----------------------------------------------------
HRESULT CompileShaderFromFile(  TCHAR*        filename,
                                LPCSTR        entry_point,
                                LPCSTR        shader_model,
                                ID3DBlob**    blob_out)
{
    HRESULT hr = S_OK;

    DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;

#if defined(DEBUG) || defined(_DEBUG)
    dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

    ID3DBlob* errorBlob;

    hr = D3DX11CompileFromFile( filename,NULL,NULL,entry_point,
                                shader_model,dwShaderFlags,0,NULL,
                                blob_out,&errorBlob,NULL );

    if ( FAILED( hr ) )
    {
        if ( errorBlob != NULL )
        {
            OutputDebugStringA( (char*)errorBlob->GetBufferPointer() );
        }
    }

    if ( errorBlob )
    {
        errorBlob->Release();
        errorBlob = NULL;
    }

    return hr;
}


固定機能パイプラインが廃止になったとかで、シェーダー強制使用です。
ということでファイル↓

■シェーダーソース

//----------------------------------------------------------------------
//! @file    basic.fx
//! @brief   超単純シェーダー!
//----------------------------------------------------------------------

// 頂点シェーダー入力
struct VertexShaderInput
{
  float4 position : POSITION0;
  float4 color    : COLOR0;
};

// 頂点シェーダー出力
struct VertexShaderOutput
{
  float4 position : SV_POSITION;
  float4 color    : COLOR;
};

// ピクセルシェーダー入力
struct PixelShaderInput
{
  float4 position : SV_POSITION;
  float4 color    : COLOR;
};

// 頂点シェーダー
VertexShaderOutput VertexShaderMain(VertexShaderInput vs_input)
{
    VertexShaderOutput vs_output = (VertexShaderOutput)0;
    
    // いっちょ代入
    vs_output.position = vs_input.position;
    vs_output.color    = vs_input.color;
    
    return vs_output;
}

// ピクセルシェーダー
float4 PixelShaderMain(PixelShaderInput ps_input) : SV_Target0
{
    // 頂点シェーダーから渡された色をそのままリターン
    return ps_input.color;
}


■結果

ワインレッドなかわいい四角形ちゃんが表示されました。

■感想

長い。長いよ初期化が・・・やること多すぎ。さすがDirectX大先生。
こういった伝統だけは今後も守ってくれそうですね。あはは。orz

各種機能に関しては後日暇があるときにまとめます。

今日は眠いから寝る。