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

下記記事にてWPFとDirect3D11を連携してサーフェスを表示しました。
WPFとDirect3D11の連携 --- 導入 - 何でもプログラミング

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

Shader作成(csoから)

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

VertexShaderではInputLayoutも併せて作成しています。

#define AssertHR(expr) { HRESULT hr = expr; if (FAILED(hr)) throw gcnew ComponentModel::Win32Exception(hr, #expr); }

std::tuple<CComPtr<ID3D11VertexShader>, CComPtr<ID3D11InputLayout>> CreateVertexShader(
    ID3D11Device* device, const std::vector<byte>& cso, const std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc)
{
    CComPtr<ID3D11VertexShader> shader;        
    AssertHR(device->CreateVertexShader(cso.data(), cso.size(), NULL, &shader.p));

    CComPtr<ID3D11InputLayout> inputLayout;
    AssertHR(device->CreateInputLayout(inputDesc.data(), inputDesc.size(), cso.data(), cso.size(), &inputLayout.p));

    return { shader, inputLayout };
}
CComPtr<ID3D11PixelShader> CreatePixelShader(ID3D11Device* device, const std::vector<byte>& cso)
{
    CComPtr<ID3D11PixelShader> shader;
    AssertHR(device->CreatePixelShader(cso.data(), cso.size(), NULL, &shader.p));
    return shader;
}


Shader作成(コードから)

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

ターゲット(vs_4_0_level_9_1、ps_4_0_level_9_1)は適宜変更してください。

Compile関数は下記記事を参照してください。
WPFとDirect3D9Exの連携 --- 三角形描画 - 何でもプログラミング

std::tuple<CComPtr<ID3D11VertexShader>, CComPtr<ID3D11InputLayout>> CreateVertexShader(
    ID3D11Device* device, std::string code, const std::vector<D3D11_INPUT_ELEMENT_DESC>& inputDesc)
{
    auto blob = Compile(code, "vs_4_0_level_9_1");
    auto p = (byte*)blob->GetBufferPointer();
    std::vector<byte> cso(p, p + blob->GetBufferSize());
    return CreateVertexShader(device, cso, inputDesc);
}
CComPtr<ID3D11PixelShader> CreatePixelShader(ID3D11Device* device, std::string code)
{
    auto blob = Compile(code, "ps_4_0_level_9_1");
    auto p = (byte*)blob->GetBufferPointer();
    std::vector<byte> cso(p, p + blob->GetBufferSize());
    return CreatePixelShader(device, cso);
}


VertexBuffer作成

template <class T>
CComPtr<ID3D11Buffer> CreateVertexBuffer(ID3D11Device* device, const std::vector<T>& vertices)
{
    D3D11_BUFFER_DESC desc;
    ZeroMemory(&desc, sizeof(desc));
    desc.Usage = D3D11_USAGE_DEFAULT;
    desc.ByteWidth = vertices.size() * sizeof(T);
    desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    desc.CPUAccessFlags = 0;

    D3D11_SUBRESOURCE_DATA data;
    ZeroMemory(&data, sizeof(data));
    data.pSysMem = vertices.data();

    CComPtr<ID3D11Buffer> buffer;
    AssertHR(device->CreateBuffer(&desc, &data, &buffer));
  
    return buffer;
}


Renderer作成

CliComPtr、D3D9Ex::CreateDeviceは下記記事を参照してください。
WPFとDirect3D9Exの連携 --- 導入 - 何でもプログラミング

CreateSharedRenderTarget、CreateDevice、OpenRenderTarget、Tie2は下記記事を参照してください。
WPFとDirect3D11の連携 --- 導入 - 何でもプログラミング

struct Vertex
{
    float X, Y, Z;
};
public ref class Renderer
{
public:
    property IntPtr Surface { IntPtr get() { return IntPtr(_surface9); } }
    Renderer(int width, int height)
    {
        _device9 = D3D9Ex::CreateDevice();
        HANDLE handle;
        Tie2(_surface9, handle, CreateSharedRenderTarget(_device9, width, height));

        Tie2(_device, _context, CreateDevice());
        _renderTarget = OpenRenderTarget(_device, handle);
            
        // Shader作成
        std::string vertexCode =
            "float4 main(float4 position : POSITION) : SV_POSITION {  \n"
            "    return position;                                     \n"
            "}                                                        \n";
        std::vector<D3D11_INPUT_ELEMENT_DESC> inputDesc =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
        };
        Tie2(_vertexShader, _inputLayout, CreateVertexShader(_device, vertexCode, inputDesc));

        std::string pixelCode =
            "float4 main() : SV_TARGET {                 \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);

        // 描画準備
        ID3D11RenderTargetView* renderTargets[1] = { _renderTarget };
        _context->OMSetRenderTargets(1, renderTargets, NULL);

        _context->VSSetShader(_vertexShader, NULL, 0);
        _context->PSSetShader(_pixelShader, NULL, 0);
        _context->IASetInputLayout(_inputLayout);

        UINT stride = sizeof(Vertex);
        UINT offset = 0;
        ID3D11Buffer* vertexBuffers[1] = { _vertexBuffer };
        _context->IASetVertexBuffers(0, 1, vertexBuffers, &stride, &offset);

        D3D11_VIEWPORT viewport;
        viewport.Width = (float)width;
        viewport.Height = (float)height;
        viewport.MinDepth = 0.0f;
        viewport.MaxDepth = 1.0f;
        viewport.TopLeftX = 0.0f;
        viewport.TopLeftY = 0.0f;
        _context->RSSetViewports(1, &viewport);

        // 描画
        float clearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
        _context->ClearRenderTargetView(_renderTarget, clearColor);

        _context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        _context->Draw(3, 0);

        _context->Flush();
    };
private:
    CliComPtr<IDirect3DDevice9Ex> _device9;
    CliComPtr<IDirect3DSurface9> _surface9;
    CliComPtr<ID3D11Device> _device;
    CliComPtr<ID3D11DeviceContext> _context;
    CliComPtr<ID3D11RenderTargetView> _renderTarget;
    CliComPtr<ID3D11VertexShader> _vertexShader;
    CliComPtr<ID3D11InputLayout> _inputLayout;
    CliComPtr<ID3D11PixelShader> _pixelShader;
    CliComPtr<ID3D11Buffer> _vertexBuffer;
};


WPF

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

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