読者です 読者をやめる 読者になる 読者になる

WPFとDirect3D9Exの連携 --- テクスチャ貼り付け

下記記事にて三角形を描画するところまで実装しました。
WPFとDirect3D9Exの連携 --- 三角形描画 - 何でもプログラミング

今回はさらにテクスチャを貼り付けを実装してみたいと思います。

本記事で利用している、CreateVertexShader、CreatePixelShader、CreateVertexBufferは上記記事を、
CliComPtr、AssertHR、CreateDevice、CreateRenderTargetは下記記事を参照してください。
WPFとDirect3D9Exの連携 --- 導入 - 何でもプログラミング

Texture作成

X8R8G8B8フォーマットで作成しています。(byte列ではBGRA)

CPU側から書き込むため、D3DPOOL_SYSTEMMEMを指定しています。

CComPtr<IDirect3DTexture9> CreateTexture(IDirect3DDevice9Ex* device, int width, int height, const byte* pixels)
{
    CComPtr<IDirect3DTexture9> texture;
    AssertHR(device->CreateTexture(
        width,
        height,
        1,
        0,
        D3DFMT_X8R8G8B8,
        D3DPOOL_SYSTEMMEM,
        &texture.p,
        NULL));

    D3DLOCKED_RECT lockedRect;
    AssertHR(texture->LockRect(0, &lockedRect, NULL, D3DLOCK_DISCARD));
    for (int y = 0; y < height; ++y)
        memcpy((byte*)lockedRect.pBits + y * lockedRect.Pitch, pixels + y * 4 * width, 4 * width);
    AssertHR(texture->UnlockRect(0));

    return texture;
}


Renderer作成

入力データからテクスチャを作成し、ターゲット全画面にレンダリングしています。

struct Vertex
{
    static const DWORD FVF = D3DFVF_XYZ | D3DFVF_TEX1;
    FLOAT X, Y, Z;
    FLOAT U, V;
};
public ref class Renderer
{
public:
    property IntPtr Surface { IntPtr get() { return IntPtr(_surface); } }
    Renderer(int width, int height, array<byte>^ pixels)
    {
        _device = CreateDevice();
        _surface = CreateRenderTarget(_device, width, height);

        // Shader作成
        std::string vertexCode =
            "struct VS_OUTPUT {                     \n"
            "    float4 Position : POSITION;        \n"
            "    float2 Texcoord : TEXCOORD0;       \n"
            "};                                     \n"
            "VS_OUTPUT main(                        \n"
            "    float4 position : POSITION,        \n"
            "    float2 texcoord : TEXCOORD0) {     \n"
            "    VS_OUTPUT output = (VS_OUTPUT)0;   \n"
            "    output.Position = position;        \n"
            "    output.Texcoord = texcoord;        \n"
            "    return output;                     \n"
            "}                                      \n";
        _vertexShader = CreateVertexShader(_device, vertexCode);

        std::string pixelCode =
            "Texture2D texture0 : register(t0);                      \n"
            "SamplerState texture0Sampler {                          \n"
            "   Filter = MIN_MAG_MIP_POINT;                          \n"
            "   AddressU = Clamp;                                    \n"
            "   AddressV = Clamp;                                    \n"
            "};                                                      \n"
            "float4 main(float2 texcoord : TEXCOORD0) : COLOR {      \n"
            "   return texture0.Sample(texture0Sampler, texcoord);   \n"
            "}                                                       \n";
        _pixelShader - CreatePixelShader(_device, pixelCode);

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

        // Texture作成
        pin_ptr<byte> pixelsPtr = &pixels[0];
        _texture = CreateTexture(_device, width, height, pixelsPtr);

        // 描画準備
        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->SetTexture(0, _texture));

        // 描画
        AssertHR(_device->BeginScene());            
        AssertHR(_device->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2));
        AssertHR(_device->EndScene());
    }
private:
    CliComPtr<IDirect3DDevice9Ex> _device;
    CliComPtr<IDirect3DSurface9> _surface;
    CliComPtr<IDirect3DVertexShader9> _vertexShader;
    CliComPtr<IDirect3DPixelShader9> _pixelShader;
    CliComPtr<IDirect3DVertexBuffer9> _vertexBuffer;
    CliComPtr<IDirect3DTexture9> _texture;
};


WPF

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

C#側に、画像ファイルを読み込んでRendererを作成する部分を追加します。

var src = new BitmapImage(new Uri("Parrots.bmp", UriKind.Relative));
int width = src.PixelWidth;
int height = src.PixelHeight;
byte[] pixels = new byte[4 * width * height];
src.CopyPixels(pixels, 4 * width, 0);

var renderer = new Renderer(width, height, pixels);

image.Source = CreateD3DImage(width, height, renderer.Surface);

f:id:any-programming:20170502180307p:plain