Nico
Nico

Reputation: 1191

Why is the transitive closure of matrix multiplication not working in this vertex shader?

This can probably be filed under premature optimization, but since the vertex shader executes on each vertex for each frame, it seems like something worth doing (I have a lot of vars I need to multiply before going to the pixel shader).

Essentially, the vertex shader performs this operation to convert a vector to projected space, like so:

// Transform the vertex position into projected space.
pos = mul(pos, model);
pos = mul(pos, view);
pos = mul(pos, projection);
output.pos = pos;

Since I'm doing this operation to multiple vectors in the shader, it made sense to combine those matrices into a cumulative matrix on the CPU and then flush that to the GPU for calculation, like so:

// VertexShader.hlsl
cbuffer ModelViewProjectionConstantBuffer : register (b0)
{
    matrix model;
    matrix view;
    matrix projection;
    matrix cummulative;
    float3 eyePosition;
};

...

// Transform the vertex position into projected space.
pos = mul(pos, cummulative);
output.pos = pos;

And on the CPU:

// Renderer.cpp
// now is also the time to update the cummulative matrix
m_constantMatrixBufferData->cummulative = 
    m_constantMatrixBufferData->model * 
    m_constantMatrixBufferData->view *
    m_constantMatrixBufferData->projection;
// NOTE: each of the above vars is an XMMATRIX

My intuition was that there was some mismatch of row-major/column-major, but XMMATRIX is a row-major struct (and all of its operators treat it as such) and mul(...) interprets its matrix parameter as row-major. So that doesn't seem to be the problem but perhaps it still is in a way that I don't understand.

I've also checked the contents of the cumulative matrix and they appear correct, further adding to the confusion.

Thanks for reading, I'll really appreciate any hints you can give me.

EDIT (additional information requested in comments): This is the struct that I am using as my matrix constant buffer:

// a constant buffer that contains the 3 matrices needed to
// transform points so that they're rendered correctly
struct ModelViewProjectionConstantBuffer
{
    DirectX::XMMATRIX model;
    DirectX::XMMATRIX view; 
    DirectX::XMMATRIX projection;
    DirectX::XMMATRIX cummulative;
    DirectX::XMFLOAT3 eyePosition;

    // and padding to make the size divisible by 16
    float padding;
};

I create the matrix stack in CreateDeviceResources (along with my other constant buffers), like so:

void ModelRenderer::CreateDeviceResources()
{
    Direct3DBase::CreateDeviceResources();

    // Let's take this moment to create some constant buffers

    ... // creation of other constant buffers

    // and lastly, the all mighty matrix buffer
    CD3D11_BUFFER_DESC constantMatrixBufferDesc(sizeof(ModelViewProjectionConstantBuffer), D3D11_BIND_CONSTANT_BUFFER);
    DX::ThrowIfFailed(
        m_d3dDevice->CreateBuffer(
        &constantMatrixBufferDesc,
        nullptr,
        &m_constantMatrixBuffer
        )
        );

    ... // and the rest of the initialization (reading in the shaders, loading assets, etc)

}

I write into the matrix buffer inside a matrix stack class I created. The client of the class calls Update() once they are done modifying the matrices:

void MatrixStack::Update()
{
    // then update the buffers
    m_constantMatrixBufferData->model = model.front();
    m_constantMatrixBufferData->view = view.front();
    m_constantMatrixBufferData->projection = projection.front();
    // NOTE: the eye position has no stack, as it's kept updated by the trackball

    // now is also the time to update the cummulative matrix
    m_constantMatrixBufferData->cummulative = 
        m_constantMatrixBufferData->model * 
        m_constantMatrixBufferData->view *
        m_constantMatrixBufferData->projection;

    // and flush
    m_d3dContext->UpdateSubresource(
        m_constantMatrixBuffer.Get(),
        0,
        NULL,
        m_constantMatrixBufferData,
        0,
        0
        );
}

Upvotes: 0

Views: 177

Answers (1)

Cédric Bignon
Cédric Bignon

Reputation: 13022

Given your code snippets, it should work.

Possible causes of your problem:

  • Have you tried the inverted multiplication: projection * view * model?
  • Are you sure you set correctly the cummulative (register index = 12, offset = 192) in your constant buffer
  • Same for eyePosition (register index = 12, offset = 256)?

Upvotes: 1

Related Questions