SUU-Lab

プログラムに関するメモとかが多いです。

Direct3D11初期化&画面クリア

Direct3D11 を使用して初期化&画面クリアするアプリケーションを作成しました。

サンプルアプリケーションクラス

初期化&画面クリアに必要な最小クラスです。

// SampleApp.hpp

#pragma once

#include "IApp.hpp"
#include <dxgi.h>
#include <d3d11.h>
#include <wrl.h>

template<class T>
using ComPtr = Microsoft::WRL::ComPtr<T>;

class SampleApp
{
public:
    SampleApp(IApp* pApp);

    ~SampleApp();

    // 初期化
    bool Init();

    // 描画処理
    void Render();

    // リサイズ
    void OnResize(const Size2D& newSize);

private:
    // バックバッファを作成
    bool CreateBackBuffer(const Size2D& newSize);

private:
    IApp * m_pApp;
    UINT m_BufferCount;

    DXGI_FORMAT       m_BufferFormat;
    D3D_FEATURE_LEVEL m_FeatureLevel;

    ComPtr<IDXGIFactory>   m_Factory;
    ComPtr<IDXGISwapChain> m_SwapChain;

    ComPtr<ID3D11Device>           m_Device;
    ComPtr<ID3D11DeviceContext>    m_Context;
    ComPtr<ID3D11RenderTargetView> m_RenderTargetView;
};

 

Direct3D11 初期化

初期化の手順を見ていきましょう。

1.ライブラリのリンク

Direct3D11 を利用するのに必要なライブラリをリンクしています。

// SampleApp.cpp

// DXGI & D3D11 のライブラリをリンク
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")

 

2.IDXGIFactory の作成

IDXGISwapChain を作成するのに必要なので、あらかじめ作成しておきます。

CreateDXGIFactory 関数で作成します。

// SampleApp::Init()
HRESULT hr = CreateDXGIFactory(IID_PPV_ARGS(&m_Factory));
if(FAILED(hr))
{
    return false;
}

 

3.ID3D11Device & ID3D11DeviceContext の作成

D3D11CreateDevice 関数で作成しています。

IDXGISwapChain と同時に作成も可能なのですが、今回は別々に作成しています。

// SampleApp::Init()

#if defined(DEBUG) || defined(_DEBUG)
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_FEATURE_LEVEL featureLevels[] = {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
    };

    // デバイス&コンテキストを生成
    hr = D3D11CreateDevice(
        nullptr,
        D3D_DRIVER_TYPE_HARDWARE, // ハードウェア ドライバー を使用
        nullptr,
        createDeviceFlags,
        featureLevels,
        _countof(featureLevels),
        D3D11_SDK_VERSION,
        &m_Device,
        &m_FeatureLevel,
        &m_Context
    );
    if (FAILED(hr))
    {
        return false;
    }

 

D3D11CreateDevice 関数

HRESULT D3D11CreateDevice(
IDXGIAdapter *pAdapter,
D3D_DRIVER_TYPE DriverType,
HMODULE Software,
UINT Flags,
CONST D3D_FEATURE_LEVEL *pFeatureLevels,
UINT FeatureLevels,
UINT SDKVersion,
ID3D11Device **ppDevice,
D3D_FEATURE_LEVEL *pFeatureLevel,
ID3D11DeviceContext **ppImmediateContext
);

pAdapter
 デバイスの作成時に使用するビデオ アダプターへのポインターです。

 通常は既定のアダプターで良いので、NULL で問題ないです。

DriverType
 作成するデバイスの種類です。

 D3D_DRIVER_TYPE のいずれかを指定します。

 pAdapter に NULL 以外の値を指定する場合は、 D3D_DRIVER_TYPE_UNKNOWN を指定する必要があります。

 通常は D3D_DRIVER_TYPE_HARDWARE 問題ないでしょう。

Software
 ソフトウェア ラスタライザーを実装する DLL のハンドルです。

 DriverType に D3D_DRIVER_TYPE_SOFTWARE を指定した場合、NULL に設定することはできません。

 D3D_DRIVER_TYPE_SOFTWARE を指定することはほぼないので、NULL で問題ないでしょう。

Flags
 有効にするランタイム レイヤーです。

 D3D11_CREATE_DEVICE_FLAG の値を OR 演算で指定できます。

 デバッグレイヤーを有効にするには、D3D11_CREATE_DEVICE_DEBUG を指定します。

 デフォルトで良い場合は 0 で構いません。

pFeatureLevels
 作成を試みる機能レベルの順序を指定する配列へのポインターです。

 機能レベルが高いものから順に並べておくと、最大の機能レベルを取得できます。

FeatureLevels
 pFeatureLevels の要素数です。

SDKVersion
 SDK のバージョンです。D3D11_SDK_VERSION を指定します。

ppDevice
 作成されたデバイスを表す ID3D11Device オブジェクトへのポインターのアドレスを返します。

pFeatureLevel
 成功した場合は、成功した pFeatureLevels 配列の最初の D3D_FEATURE_LEVEL を返します。

 失敗した場合は 0 を返します。

ppImmediateContext
 デバイス コンテキストを表す ID3D11DeviceContext オブジェクトへのポインターのアドレスを返します。

4.MSAA (Multi-Sample Anti-Aliasing) の設定

今回は画面クリアまでなので必要ないかもしれませんが、一応設定だけしておきます。

MSAA については、ここでは説明を省きます。

スワップチェインの作成時に MSAA を有効化できるのですが、その設定に利用する値を調べます。

ID3D11::CheckMultisampleQualityLevels 関数を使って、利用可能なサンプリングカウントと品質の最大値を調べます。

// SampleApp::Init()
    // 使用可能なMSAAを取得
    DXGI_SAMPLE_DESC sampleDesc;
    ZeroMemory(&sampleDesc, sizeof(sampleDesc));
    for (int i = 1; i <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; i <<= 1)
    {
        UINT Quality;
        if (SUCCEEDED(m_Device->CheckMultisampleQualityLevels(DXGI_FORMAT_D24_UNORM_S8_UINT, i, &Quality)))
        {
            if (0 < Quality)
            {
                sampleDesc.Count = i;
                sampleDesc.Quality = Quality - 1;
            }
        }
    }

 

5.DXGI_SWAP_CHAIN_DESC の設定

スワップチェイン作成に必要な設定をします。

下のような感じです。

// SampleApp::Init()
    // DXGI_SWAP_CHAIN_DESC の設定
    DXGI_SWAP_CHAIN_DESC swapChainDesc;
    ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));

    swapChainDesc.BufferDesc.Width = static_cast<UINT>(clientSize.width);
    swapChainDesc.BufferDesc.Height = static_cast<UINT>(clientSize.height);
    swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
    swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
    swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_CENTERED;
    swapChainDesc.BufferDesc.Format = m_BufferFormat;
    swapChainDesc.SampleDesc = sampleDesc;
    swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
    swapChainDesc.BufferCount = m_BufferCount;
    swapChainDesc.Windowed = TRUE;
    swapChainDesc.OutputWindow = hWnd;
    swapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

 

DXGI_SWAP_CHAIN_DESC 構造体

typedef struct DXGI_SWAP_CHAIN_DESC {
DXGI_MODE_DESC BufferDesc;
DXGI_SAMPLE_DESC SampleDesc;
DXGI_USAGE BufferUsage;
UINT BufferCount;
HWND OutputWindow;
BOOL Windowed;
DXGI_SWAP_EFFECT SwapEffect;
UINT Flags;
} DXGI_SWAP_CHAIN_DESC;

BufferDesc.Width
 バッファの横幅です。

BufferDesc.Height
 バッファの縦幅です。

BufferDesc.RefreshRate.Numerator
 リフレッシュレートの分子です。

BufferDesc.RefreshRate.Denominator
 リフレッシュレートの分母です。

BufferDesc.ScanlineOrdering
 スキャンラインの方法です。

 バックバッファをフリップした時にハードウェアがパソコンのモニターに点をどう描くかを指定します。

 プログレッシブやインターレスなどが選択可能ですが、
 特段理由が無ければデフォルト値(DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED)でOKです。

BufferDesc.Scaling
 バッファのスケーリングの設定です。

 ウィンドウのサイズに応じてスケーリングするかどうかの設定を行えます。

 スケーリングする場合は DXGI_MODE_SCALING_STRETCHED 、
 スケーリングしない場合は DXGI_MODE_SCALING_CENTERED を指定します。

BufferDesc.Format
 バッファのフォーマットです。

 フォーマットの設定についてはかなりの種類があるので MSDN を参考にしてください。

SampleDesc
 MSAA の設定です。
 
 「4.MSAA (Multi-Sample Anti-Aliasing) の設定」で取得した値を設定します。

 何も指定しなかった場合は MSAA が無効になります。

BufferUsage
 バッファの使用方法です。

 レンダーターゲットとして使用する場合は DXGI_USAGE_RENDER_TARGET_OUTPUT 、
 シェーダー入力用(レンダリングテクスチャ)として使用する場合は DXGI_USAGE_SHADER_INPUT を指定します。

BufferCount
 バッファの数です。

 2ならダブルバッファ、3ならトリプルバッファとなります。

 特別こだわりがなければ2で問題ないと思います。

Windowed
 ウィンドウモードで作成するかどうかです。

 TRUE ならウィンドウモード、FALSE ならフルスクリーンモードです。

OutputWindow
 出力ウィンドウです。ウィンドウハンドルを指定します。

Flags
 スワップチェインの動作オプションです。

 DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH を指定した場合、
 フルスクリーンモードとウィンドウモードの切り替えが可能になります。

6.IDXGISwapChain の作成

やっとスワップチェインの作成です。

IDXGIFactory::CreateSwapChain 関数で作成します。

// SampleApp::Init()
    // スワップチェインの生成
    hr = m_Factory->CreateSwapChain(
            m_Device.Get(),
            &swapChainDesc,
            &m_SwapChain
        );
    if (FAILED(hr))
    {
        return false;
    }

 

IDXGIFactory::CreateSwapChain 関数

HRESULT CreateSwapChain(
IUnknown *pDevice,
DXGI_SWAP_CHAIN_DESC *pDesc,
IDXGISwapChain **ppSwapChain
);

pDevice
 スワップ チェーンに 2D イメージを書き込むデバイスへのポインター
 D3D11CreateDevice で作成したデバイスを指定します。

pDesc
 DXGI_SWAP_CHAIN_DESC へのポインターです。
 NULL は指定できません。

ppSwapChain
 作成されたスワップ チェーンへのポインターです。



ここまで記事を書いてきたのですが、
長くなったので続きは次回にしようと思います。

次回はレンダーターゲットの作成と画面クリアの処理についてです。