WPFとDirect3D9Exの連携 --- 三角形描画

下記記事にてIDirect3DSurface9をWPFで表示しました。
WPFとDirect3D9Exの連携 --- 導入 - 何でもプログラミング

今回は単純な三角形を描画してWPFで表示してみたいと思います。

本記事で利用しているAssertHR、CliComPtr、CreateDevice、CreateRenderTargetに関しましては、上記記事を参照してください.

Shader作成(csoから)

csoデータが存在する場合は、下記関数のみで作成できます。

CComPtr<IDirect3DVertexShader9> CreateVertexShader(IDirect3DDevice9Ex* device, const DWORD* cso)
{
    CComPtr<IDirect3DVertexShader9> shader;
    AssertHR(device->CreateVertexShader(cso, &shader.p));
    return shader;
}
CComPtr<IDirect3DPixelShader9> CreatePixelShader(IDirect3DDevice9Ex* device, const DWORD* cso)
{
    CComPtr<IDirect3DPixelShader9> shader;
    AssertHR(device->CreatePixelShader(cso, &shader.p));
    return shader;
}


Shader作成(コードから)

実行時にコンパイルする場合は、下記関数を利用します。

Direct3D9Exを利用しているため、vs_3_0、ps_3_0より後のバージョンは利用できません。

コンパイルオプション(flags1)は適宜変更してください。

CComPtr<IDirect3DVertexShader9> CreateVertexShader(IDirect3DDevice9Ex* device, std::string code)
{
    return CreateVertexShader(device, (const DWORD*)Compile(code, "vs_3_0")->GetBufferPointer());
}
CComPtr<IDirect3DPixelShader9> CreatePixelShader(IDirect3DDevice9Ex* device, std::string code)
{
    return CreatePixelShader(device, (const DWORD*)Compile(code, "ps_3_0")->GetBufferPointer());
}
#include <d3dcompiler.h>
#pragma comment(lib, "d3dcompiler.lib")

CComPtr<ID3DBlob> Compile(std::string code, std::string target)
{
#ifdef _DEBUG
    UINT flags1 = D3DCOMPILE_DEBUG | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS;
#else
    UINT flags1 = D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS;
#endif
    CComPtr<ID3DBlob> compiled, errorMessage;
    D3DCompile(
        code.c_str(), 
        code.size(), 
        NULL, 
        NULL, 
        D3D_COMPILE_STANDARD_FILE_INCLUDE, 
        "main", 
        target.c_str(), 
        flags1, 
        0, 
        &compiled.p, 
        &errorMessage.p);
    if (errorMessage)
        throw gcnew Exception(gcnew String((const char*)errorMessage->GetBufferPointer()));
    
    return compiled;
}


VertexBuffer作成

対象のVertexクラスには、FVF staticメンバが定義されていることを想定しています。

template <class T>
CComPtr<IDirect3DVertexBuffer9> CreateVertexBuffer(IDirect3DDevice9Ex* device, const std::vector<T>& vertices)
{
    CComPtr<IDirect3DVertexBuffer9> buffer;
    AssertHR(device->CreateVertexBuffer(
        vertices.size() * sizeof(T),
        0,
        T::FVF,
        D3DPOOL_DEFAULT,
        &buffer.p,
        NULL));
    
    void* p;
    AssertHR(buffer->Lock(0, 0, &p, 0));
    memcpy(p, vertices.data(), vertices.size() * sizeof(T));
    AssertHR(buffer->Unlock());

    return buffer;
}


Renderer作成

Shader、VertexBufferを作成して、三角形を描画しています。

struct Vertex
{
    static const DWORD FVF = D3DFVF_XYZ;
    FLOAT X, Y, Z;
};
public ref class Renderer
{
public:
    property IntPtr Surface { IntPtr get() { return IntPtr(_surface); } }
    Renderer(int width, int height)
    {
        _device = CreateDevice();
        _surface = CreateRenderTarget(_device, width, height);

        // Shader作成
        std::string vertexCode =
            "float4 main(float4 position : POSITION) : POSITION {  \n"
            "    return position;                                  \n"
            "}                                                     \n";
        _vertexShader = CreateVertexShader(_device, vertexCode);

        std::string pixelCode =
            "float4 main() : COLOR {                     \n"
            "    return float4(1.0f, 0.0f, 0.0f, 1.0f);  \n"
            "}                                           \n";
        _pixelShader = CreatePixelShader(_device, pixelCode);

        // VertexBuffer作成
        std::vector<Vertex> vertices = 
        {
            {  0.0f,  1.0f, 0.5f },
            {  1.0f, -1.0f, 0.5f },
            { -1.0f, -1.0f, 0.5f },
        };
        _vertexBuffer = CreateVertexBuffer(_device, vertices);

        // 描画準備
        AssertHR(_device->SetRenderTarget(0, _surface));
        AssertHR(_device->SetFVF(Vertex::FVF));
        AssertHR(_device->SetStreamSource(0, _vertexBuffer, 0, sizeof(Vertex)));
        AssertHR(_device->SetVertexShader(_vertexShader));
        AssertHR(_device->SetPixelShader(_pixelShader));

        // 描画
        AssertHR(_device->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0));
        AssertHR(_device->BeginScene());            
        AssertHR(_device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1));            
        AssertHR(_device->EndScene());
    }
private:
    CliComPtr<IDirect3DDevice9Ex> _device;
    CliComPtr<IDirect3DSurface9> _surface;
    CliComPtr<IDirect3DVertexShader9> _vertexShader;
    CliComPtr<IDirect3DPixelShader9> _pixelShader;
    CliComPtr<IDirect3DVertexBuffer9> _vertexBuffer;
};


WPF

WPF側は下記記事と同じものを利用します。
WPFとDirect3D9Exの連携 --- 導入 - 何でもプログラミング

実行すると下記のようなウィンドウが表示されます。
f:id:any-programming:20170502173131p:plain