Search code examples
directxdirectx-11direct3ddirect3d11

Draw RGB pixel array to DirectX-11 render view


Given an array of RBG pixels that updates every frame (e.g. 1024x1024), a ID3D11RenderTargetView, ID3D11Device and ID3D11DeviceContext, what's the easiest way to draw these pixels to the render view?

I've been working the angle of creating a vertex buffer for a square (two triangles), trying to make pixels be a proper texture, and figuring out how to make a shader reference the texture sampler. I've been following this tutorial https://learn.microsoft.com/en-us/windows/uwp/gaming/applying-textures-to-primitives .... But to be honest, I don't see how this tutorial has shaders that even reference the texture data (shaders defined on the proceeding tutorial, here).

I am a total DirectX novice, but I am writing a plugin for an application where I am given a directx11 device/view/context, and need to fill it with my pixel data. Many thanks!


Solution

  • IF you can make sure your staging resource matches the exact resolution and format of the render target you are given:

    1. Create a staging resource

    2. Map the staging resource, and copy your data into it.

    3. Unmap the staging resource

    4. UseGetResource on the RTV to get the resource

    5. CopyResource from your staging to that resource.

    Otherwise, IF you can count on Direct3D Hardware Feature level 10.0 or better, the easiest way would be:

    1. Create a texture with USAGE_DYNAMIC.

    2. Map it and copy your data into the texture.

    3. Unmap the resource

    4. Render the dynamic texture as a 'full-screen' quad using the 'big-triangle' self-generation trick in the vertex shader:

    SamplerState PointSampler : register(s0);
    Texture2D<float4> Texture : register(t0);
    
    struct Interpolators
    {
        float4 Position : SV_Position;
        float2 TexCoord : TEXCOORD0;
    };
    
    Interpolators main(uint vI : SV_VertexId)
    {
        Interpolators output;
    
        // We use the 'big triangle' optimization so you only Draw 3 verticies instead of 4.
        float2 texcoord = float2((vI << 1) & 2, vI & 2);
        output.TexCoord = texcoord;
        output.Position = float4(texcoord.x * 2 - 1, -texcoord.y * 2 + 1, 0, 1);
    
        return output;
    }
    

    and a pixel shader of:

    float4 main(Interpolators In) : SV_Target0
    {
        return Texture.Sample(PointSampler, In.TexCoord);
    }
    

    Then draw with:

    ID3D11ShaderResourceView* textures[1] = { texture };
    context->PSSetShaderResources(0, 1, textures);
    
    // You need a sampler object.
    context->PSSetSamplers(0, 1, &sampler);
    
    // Depending on your desired result, you may need state objects here
    context->OMSetBlendState(nullptr, nullptr, 0xffffffff);
    context->OMSetDepthStencilState(nullptr, 0);
    context->RSSetState(nullptr);
    
    context->IASetInputLayout(nullptr);
    contet->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    Draw(3, 0);
    

    For full source for the "Full Screen Quad" drawing, see GitHub.