5
\$\begingroup\$

I created a simple Model framework in C++ 11 so that the same model can be rendered in different parts of a scene.

What I am trying to figure out is how to reference VertexBuffer data already "set" in the GPU for reuse as I draw different instances(100x) of different geometries, (i.e., square, triangle, etc).

I apologize that I had not understood the complexity of this particular question and have not been very good at making my question clear. As I am researching, I am clarifying the detail of the question.

Given: 1. Model Vertex Data is Stored in a Vertex Buffer, (that I refer to as the static/default Vertex Buffer). 2. Instance Position, (Vertex) data is stored in an instance buffer. 3. Constant Data, (World, View(to be changed), Projection) is stored in the Constant Buffer.

How Do I: 1. Update the entire Instance Buffer without having to re-bind the Static Buffer, and/or 2. Update only one instance in the Instance Buffer without having to re-bind the entire Instance Buffer

In the following code: I am defining both the Instance and Static Vertex Buffers' Element Description at the same time... Want to break this up, so that I can call VertexBuffer's initialization methods once, and then Instance Buffer's stuff over and over again:

void ModelRenderer::InitializeInputElementDescription()
        {
            // InputElementDescription = new D3D11_INPUT_ELEMENT_DESC[2]();

            InputElementDescription[0] = D3D11_INPUT_ELEMENT_DESC();
            InputElementDescription[0].SemanticName = "POSITION";
            InputElementDescription[0].SemanticIndex = 0;



        InputElementDescription[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
            InputElementDescription[0].InputSlot = 0;

            InputElementDescription[0].AlignedByteOffset = 0;
            InputElementDescription[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
            InputElementDescription[0].InstanceDataStepRate = 0;

            InputElementDescription[1] = D3D11_INPUT_ELEMENT_DESC();
            InputElementDescription[1].SemanticName = "TEXCOORD";
            InputElementDescription[1].SemanticIndex = 0;

            InputElementDescription[1].Format = DXGI_FORMAT_R32G32B32_FLOAT;
            InputElementDescription[1].InputSlot = 1;

            InputElementDescription[1].AlignedByteOffset = 0;
            InputElementDescription[1].InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA;
            InputElementDescription[1].InstanceDataStepRate = 1;
        }

Shader File: My Semantic isn't being recognized somehow here, but I can manually set a shape's position.

cbuffer ModelViewProjectionConstantBuffer : register(b0)
{
    matrix model;
    matrix view;
    matrix projection;
};

struct VertexShaderInput
{
    float3 position : POSITION;
    float3 instance_position : TEXCOORD;
    // float3 color : COLOR0;
};

struct VertexShaderOutput
{
    float4 pos : SV_POSITION;
    // float3 color : COLOR0;
};

VertexShaderOutput main(VertexShaderInput input)
{
    VertexShaderOutput output;

    // Move the Instance someplace into the world.
    float3 translatedPosition = input.position + input.instance_position;


    float4 pos = float4(translatedPosition, 1.0f);


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

    // Pass through the color without modification.
    // output.color = input.color;

    return output;
}

Initializing InstanceBuffer: Something like this would be called every frame, or so. But right now, I am sending this info along with the static vertex buffer info:

/**************************************/ void ModelRenderer::InitializeInstanceBuffer() { HRESULT result;

instances = new Position[2]();  
instances[0].X = 0.0f;
instances[0].Y = 0.0f;
instances[0].Z = 0.0f;

instances[1].X = 0.5f;
instances[1].Y = 0.5f;
instances[1].Z = 0.5f;


// Set up the description of the instance buffer.
instanceBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
instanceBufferDesc.ByteWidth = sizeof(Position) * 2;
instanceBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
instanceBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
instanceBufferDesc.MiscFlags = 0;
instanceBufferDesc.StructureByteStride = 0;


instanceSubResourceData.SysMemPitch = 0;
instanceSubResourceData.SysMemSlicePitch = 0;

instanceSubResourceData.pSysMem = instances; 

result = DeviceManager::Device->CreateBuffer(&instanceBufferDesc, &instanceSubResourceData, &instanceBuffer);


delete [] instances;

}

Here, I am setting my VertexBuffer and InstanceBuffer into the InputAssembly (IA), both at the same time. I would only want to do this once on initialization, and then call a similar method that doesn't reference the initial static vertex buffer data again:

/******************************************************************/
    void ModelRenderer::Stage()
    {
        DeviceManager::DeviceContext->OMSetRenderTargets(
            1,
            DeviceManager::RenderTargetView.GetAddressOf(),
            DeviceManager::DepthStencilView.Get()
            );

        DeviceManager::DeviceContext->UpdateSubresource(
            ConstantBuffer.Get(),
            0,
            NULL,
            &ConstantBufferData,
            0,
            0
            );

        DeviceManager::DeviceContext->UpdateSubresource(
            instanceBuffer,
            0,
            NULL,
            &instanceSubResourceData,
            0,
            0
            );

        unsigned int offsets[2] = {0,0};
        unsigned int strides[2];

        ID3D11Buffer* bufferPointers[2];

        strides[0] = sizeof(NS::Vertex);
        strides[1] = sizeof(Position); 

        bufferPointers[0] = ModelRenderer::VertexBuffer.Get();  
        bufferPointers[1] = ModelRenderer::instanceBuffer;

        DeviceManager::DeviceContext->IASetInputLayout(ModelRenderer::InputLayout.Get());

        DeviceManager::DeviceContext->IASetVertexBuffers(
            0,
            2,
            bufferPointers,
            strides,
            offsets
            );

        DeviceManager::DeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);


        DeviceManager::DeviceContext->VSSetConstantBuffers(
            0,
            1,
            ModelRenderer::ConstantBuffer.GetAddressOf()
            );
    }

Thanks for the help!

\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

You could go about this in different ways.

Usually, you have a model's geometry centered around the origin (0,0,0), and to place it somewhere in the world you would use a world matrix, which offsets the entire model to a desired new position.

This world matrix can reside in a constant buffer accessible to the vertex shader, where it would be combined with the view and projection matrices, and then used to transform your vertex data.

Constant buffers allow you to provide variables to your shader code, however they remain constant while a shader runs. An example in shader code:

cbuffer perObject 
{
    float4x4 World; //world matrix carrying position information
    float4 objColor;
};

cbuffer perScene
{
    float4x4 ViewProjection; //view & projection matrix
};

Here is a tutorial that covers constant buffers, among other things: http://www.rastertek.com/dx11tut04.html

And an article on MSDN: http://msdn.microsoft.com/en-us/library/windows/desktop/ff476898(v=vs.85).aspx#Shader_Constant_Buffer

With this approach you can move your model to different positions by calculating a new world matrix and updating the constant buffer containing the world matrix, no need to update the whole vertex buffer.

If you are drawing many instances though, you could use an instance buffer in which you store all the different positions your object should be drawn at.

An instance buffer is not much different from a vertex buffer, the elements from the instance buffer will be received as parameters in your vertex shader.

You would call ID3D11DeviceContext::DrawInstanced(numVertices, numInstances, vertOffset, instOffset) or DrawInstancedIndexed(..), and your vertices will be drawn numinstances times, each time supplying a different element from your instance buffer to the vertex shader.

Here is a tutorial that covers instancing in D3D11: http://www.rastertek.com/dx11tut37.html

\$\endgroup\$
10
  • \$\begingroup\$ Thanks! This is exactly what I am looking for. But, is there any way you could clarify the associations you mentioned regarding the constantbuffer and instantbuffer? What data would go in which? Would the "static" vertexbuffer go into the constantbuffer? The shaders too? I am working through your link, so understanding the basic associations would help me understand this page. (There is a LOT here). Thanks! \$\endgroup\$ Commented Jan 26, 2013 at 21:58
  • \$\begingroup\$ I can't link your name for some reason, but if you would like, I linked the stackoverflow version of my question, if you want to answer it there. \$\endgroup\$ Commented Jan 26, 2013 at 22:14
  • \$\begingroup\$ @WindAndFlame You can't link my name, because I am already notified when you comment on my answer :) I've edited the answer, added some explanations, and another link \$\endgroup\$
    – user13213
    Commented Jan 26, 2013 at 22:19
  • \$\begingroup\$ melak47: ID3D11DeviceContext::DrawInstanced(numVertices, numInstances, vertOffset, instOffset) .... If I have two model definitions buffered in the vertexbuffer, how does DirectX know which model an instance refers to? Is that why the vertOffset is mentioned? I just need to keep track of that somewhere? \$\endgroup\$ Commented Jan 26, 2013 at 22:32
  • \$\begingroup\$ @WindAndFlame yes, if you have to models in one vertex buffer (say, first one has 30, second one 50 vertices) you can draw them by Draw(30, 0) and Draw(50,30). This extends to DrawInstanced etc, and works the same with the instance buffers \$\endgroup\$
    – user13213
    Commented Jan 26, 2013 at 22:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .