Search code examples
c++directxsprite2d-gamesdirect3d11

How do you update the vertex buffer or constant buffer attached to a sprite to move it smoothly across the screen in Direct3D 11?


I have attached a texture to a set of 4 indexed vertices which are stored in a dynamic vertex buffer. I have also added a translation matrix to the constant buffer of the vertex shader. However, when I try updating the constant buffer to alter the translation matrix so the I can move the sprite, the sprite does not move smoothly. It stops randomly for short amounts of time before moving a short distance again.

Below are the render functions, the main loop and the shaders being used:

    void Sprite::Render(ID3D11DeviceContext* devcon, float dt) {
    // 2D rendering on backbuffer here
    UINT stride = sizeof(VERTEX);
    UINT offset = 0;

    spr_const_data.translateMatrix.r[3].m128_f32[0] += 60.0f*dt;

    devcon->IASetInputLayout(m_data_layout);
    devcon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    devcon->VSSetShader(m_spr_vert_shader, 0, 0);
    devcon->PSSetShader(m_spr_pixel_shader, 0, 0);

    devcon->VSSetConstantBuffers(0, 1, &m_spr_const_buffer);
    devcon->PSSetSamplers(0, 1, &m_tex_sampler_state);
    devcon->PSSetShaderResources(0, 1, &m_shader_resource_view);

    // select vertex and index buffers
    devcon->IASetIndexBuffer(m_sprite_index_buffer, DXGI_FORMAT_R32_UINT, offset);
    devcon->IASetVertexBuffers(0, 1, &m_sprite_vertex_buffer, &stride, &offset);

    D3D11_MAPPED_SUBRESOURCE ms;
    ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE));
    devcon->Map(m_spr_const_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
    memcpy(ms.pData, &spr_const_data, sizeof(spr_const_data));
    devcon->Unmap(m_spr_const_buffer, 0);

    // select which primitive type to use
    // draw vertex buffer to backbuffer
    devcon->DrawIndexed(6, 0, 0);
}

void RenderFrame(float dt) {
        float background_color[] = { 1.0f, 1.0f, 1.0f, 1.0f };

        // clear backbuffer
        devcon->ClearRenderTargetView(backbuffer, background_color);

        knight->Render(devcon, dt);

        // switch back and front buffer
        swapchain->Present(0, 0);
    }

void MainLoop() {
        MSG msg;
        auto tp1 = std::chrono::system_clock::now();
        auto tp2 = std::chrono::system_clock::now();
        while (GetMessage(&msg, nullptr, 0, 0) > 0) {
            tp2 = std::chrono::system_clock::now();
            std::chrono::duration<float> dt = tp2 - tp1;
            tp1 = tp2;
            TranslateMessage(&msg);
            DispatchMessage(&msg);

            RenderFrame(dt.count());
        }
    }
cbuffer CONST_BUFFER_DATA : register(b0)
{
    matrix orthoMatrix;
    matrix translateMatrix;
};

struct VOut {
    float4 position : SV_POSITION;
    float2 tex : TEXCOORD0;
};

VOut VShader(float4 position : POSITION, float2 tex : TEXCOORD0) {
    VOut output;

    position = mul(translateMatrix, position);
    output.position = mul(orthoMatrix, position);
    output.tex = tex;

    return output;
}
Texture2D square_tex;
SamplerState tex_sampler;

float4 PShader(float4 position : SV_POSITION, float2 tex : TEXCOORD0) : SV_TARGET
{
    float4 tex_col = square_tex.Sample(tex_sampler, tex);
    return tex_col;
}

I have also included the initialization of the swapchain and backbuffer in case there is a mistake owing to it.

DXGI_SWAP_CHAIN_DESC scd; // hold swap chain information
        ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));

        // fill swap chian description struct
        scd.BufferCount = 1; // one back buffer
        scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // use 32-bit color
        scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used (draw into back buffer)
        scd.OutputWindow = hWnd; // window to be used
        scd.SampleDesc.Count = 1; // how many multisamples
        scd.Windowed = true; // windowed/full screen

        // create device, device context and swap chain using scd
        D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_DEBUG, nullptr, NULL, D3D11_SDK_VERSION, &scd, &swapchain, &dev, nullptr, &devcon);

        // get address of back buffer
        ID3D11Texture2D* pBackBuffer;
        swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);

        // use back buffer address to create render target
        dev->CreateRenderTargetView(pBackBuffer, nullptr, &backbuffer);
        pBackBuffer->Release();

        // set the render target as the backbuffer
        devcon->OMSetRenderTargets(1, &backbuffer, nullptr);

        // Set the viewport
        D3D11_VIEWPORT viewport;
        ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));

        viewport.TopLeftX = 0;
        viewport.TopLeftY = 0;
        viewport.Width = WINDOW_WIDTH;
        viewport.Height = WINDOW_HEIGHT;

        devcon->RSSetViewports(1, &viewport); // activates viewport

I have also tried getting a pointer to the vertex data via the pData member of the D3D11_MAPPED_SUBRESOURCE object and then casting it to a VERTEX* to manipulate the data, but the problem persists. I would like to know how to move the sprite smoothly across the window.


Solution

  • I solved the problem by writing a fixed FPS game loop and giving the interpolated values between the previous and current states to the render function. I was also updating the constant buffer in an incorrect manner.