One Or More VertexBuffers in DirectX

Started by
12 comments, last by exscreen 9 years, 6 months ago

Hi Everyone,

I have a question regarding vertex buffers and their transformation.I want to render objects of different shapes(vertices) and have them move in separate directions.For example if i have a triangle that's rotating in one direction and a square which is moving or rotating in another direction should the vertices of these two objects be placed in the same Vertex buffer or should they be placed in separate buffers.

Both solutions don't make sense to me:

1) I've tried placing them in one buffer but then they rotate/move in the same direction.

2) Having a VertexBuffer for every object that's moving differently will requier me having a huge number of buffers.

So my question is how should i go about placing vertices of objects,in one or more buffers, and how should i move(transform) them?

Advertisement

- option 1 is the right way - pack objects in few vertex buffers

You are doing something wrong, can you show your draw code?

it is perfectly okay to draw just a portion of a vertex buffer, with separate transforms, in order to make the objects move in the unique directions.

Cheers!

It seems that the error you're making with option 1 is that you're assuming that you must draw the entire vertex buffer with each draw call. That's not necessary.

Let's assume that you have a single vertex buffer with a triangle and a square in it; that's 7 vertices, 3 for the triangle and 4 for the square.

You want to draw them and have them move separately.

First of all you set up to draw. This means your IASetVertexBuffers or SetStreamSource calls.

Then you send the transform (i.e. the matrix) you're going to use for the triangle.

Then you draw the triangle vertices in the first draw call. In D3D11 it would be:

context->Draw (3, 0);

In D3D9:

device->DrawPrimitive (D3DPT_TRIANGLELIST, 0, 1);

Now we come to draw the square, so keeping the same vertex buffer set, we send the transform (matrix) and issue our draw calls:

context->Draw (4, 3);

Or:

device->DrawPrimitive (D3DPT_TRIANGLESTRIP, 3, 2);

And that's all there is to it. One buffer, two draw calls, adjusting the parameters of the calls to select the portion of the buffer to use in each.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

I guess option 1 then. So what i got from this is that we put all vertices(square,triangle) in a single vertex buffer, we update every vertex so that the object that we want to draw(square) sits in its designated position and then just draw only the vertices of that object using

context->Draw and ignore all other vertices. We do the same for the triangle except we update it differently.

Is this how it's done and if so doesn't updating every vertex in the buffer just for drawing one object impact performance?

I know this is not the most technical explanation so here is some code. i managed to update the square and the triangle separatelly :

The D3DGraphics.cpp


void D3DGraphics::InitGraphics()
{ VERTEX OurVertices[] =
    {
        {-0.5f, -0.5f, 0.5f, DirectX::PackedVector::XMCOLOR(1.0f, 0.0f, 0.0f, 1.0f)},
        {-0.5f,  0.5f, 0.5f, DirectX::PackedVector::XMCOLOR(0.0f, 1.0f, 0.0f, 1.0f)},
        { 0.5f,  0.5f, 0.5f, DirectX::PackedVector::XMCOLOR(0.0f, 0.0f, 1.0f, 1.0f)},
        { 0.8f, -0.5f, 0.5f, DirectX::PackedVector::XMCOLOR(0.0f, 0.0f, 1.0f, 1.0f)},
        {-0.4f, -0.3f, 0.4f, DirectX::PackedVector::XMCOLOR(1.0f, 0.0f, 0.0f, 1.0f)},
	{-0.4f,  0.3f, 0.4f, DirectX::PackedVector::XMCOLOR(0.0f, 1.0f, 0.0f, 1.0f)},
	{ 0.3f,  0.3f, 0.4f, DirectX::PackedVector::XMCOLOR(0.0f, 0.0f, 1.0f, 1.0f)}
    };
DWORD indices[] = 
	{0,2,3,
         0,1,2,
	 4,5,6};
//create and fill the index buffer
D3D11_BUFFER_DESC indexBufferDesc;
//...

//create and fill the vertex buffer
D3D11_BUFFER_DESC bd;
//...

//Create the buffer to send to the cbuffer in effect file
D3D11_BUFFER_DESC cbbd;	
//...
//Camera information
//Set the View matrix
//Set the Projection matrix

void D3DGraphics::DrawVertices()
{
        // select which vertex buffer to display
        UINT stride = sizeof(VERTEX);
        UINT offset = 0;

	m_pImmediateContext->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
        m_pImmediateContext->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);
	// select which primtive type we are using
        m_pImmediateContext->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        //Refresh the Depth/Stencil view
	m_pImmediateContext->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0);
        
        //we update and draw the square
	WVP = squareWorld * camView * camProjection;
	cbPerObj.WVP = XMMatrixTranspose(WVP);	
	m_pImmediateContext->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
        m_pImmediateContext->DrawIndexed(6, 0, 0 );
        
        //we update and draw the triangle
        WVP = triangleWorld * camView * camProjection;
	cbPerObj.WVP = XMMatrixTranspose(WVP);	
	m_pImmediateContext->UpdateSubresource( cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0 );
	m_pImmediateContext->VSSetConstantBuffers( 0, 1, &cbPerObjectBuffer );
	m_pImmediateContext->DrawIndexed( 3, 4, 4 );
}

void D3DGraphics::UpdateScene()
{
	//Keep the cubes rotating
	rot += .0001f;
	if(rot > 6.2831f)
		rot = 0.0f;

	//Reset squareWorld
	squareWorld = DirectX::XMMatrixIdentity();
        //Define square's world space matrix
	DirectX::XMVECTOR rotaxis = DirectX::XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
	Rotation = DirectX::XMMatrixRotationAxis( rotaxis, rot);
	Translation = DirectX::XMMatrixTranslation( 0.0f, 0.0f, 0.0f );

	//Set square's world space using the transformations
	squareWorld = Translation * Rotation;

	//Reset triangleWorld
	triangleWorld = DirectX::XMMatrixIdentity();

	//Define triangle's world space matrix
	Rotation = DirectX::XMMatrixRotationAxis( rotaxis, -rot);
	Scale = DirectX::XMMatrixScaling( 1.3f, 1.3f, 1.3f );

	//Set triangle's world space matrix
	triangleWorld = Rotation * Scale;
}


The shaders.fx code:


cbuffer cbPerObject
{
	float4x4 WVP;
};
struct VOut
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

VOut VShader(float4 position : POSITION, float4 color : COLOR)
{
    VOut output;

    output.position = mul(position, WVP);
    output.color = color;

    return output;
}


float4 PShader(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET
{
    return color;
}

Sorry if there's some unnecessary stuff here it's my first time posting on gamedev smile.png and Thanks

The general indexed approach you've coded can be made to work, or, alternatively, as mhagain suggests, you don't need to use an index buffer if you put the vertices in trianglestrip order and set primitive topology to D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP.

Note: Take a look at the docs for context->DrawIndexed. Your indices already point to the correct position in the vertex buffer. As a result, your code for drawing the triangle:


m_pImmediateContext->DrawIndexed( 3, 4, 4 );

should be:


m_pImmediateContext->DrawIndexed( 3, 6, 0 );
// 3 vertices, start at index buffer position 6, add 0 to each index to get the vertex buffer position.

Also, perhaps just semantics, but your comments: "we update every vertex so that the object that we want to draw(square) sits in its designated position" and "doesn't updating every vertex in the buffer just for drawing one object impact performance?" might be better phrased: "using the same vertices, the objects are transformed to their correct positions in the shader by setting the appropriate world matrix for each object."

With regard to updating the constant buffer with each object's world matrix prior to rendering - that's pretty much how it's done. smile.png EDIT: The alternative of actually updating the values in the vertex buffer itself is inefficient (having to do with caching, moving memory from GPU to CPU and back to GPU, etc.) As a result, being able to set a matrix to transform vertices in the GPU enhances performance!

EDIT: Just FYI, your line of code Translation = DirectX::XMMatrixTranslation( 0.0f, 0.0f, 0.0f ); doesn't serve any purpose, unless you anticipate changing the object position.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Yeah that worked, now there separate and drawn properly. I just have one more thing i want to ask.

How can you add objects to a vertex buffer at runrime?

For example if i had an empty vertexbuffer initialized at the start of my program could i:

-have the user make an object using vertices that they've defined by clicking on a position on the screen and then somehow add those vertices to the vertexbuffer to be drawn.I guess this was done by making the buffer dynamic in DX9 but there's nothing on how it's done in DX11.

OR

-have the vertexbuffer be filled with vertices and then transform each individual vertex(not all, just the number of vertices in the object) using XMMatrixTranslation to the position of vertices provided by the user so that they make the object.

Both seem like they could be made to work but i'm not sure unsure.png

You might want to start by reading this Q&A.

If the vertex buffer is to be updated infrequently, but the addition(s) will be permanent, create the vertex buffer as described in that link. You can take a look at the docs for CreateBuffer (ID3D11Device), D3D11_USAGE, and Map (ID3D11DeviceContext). Pay attention to the various flags used to create and map the buffer. If you KNOW there are going to be more additions to the vertex buffer, you can initially create the buffer larger than initially needed, and keep track of (all) object indices elsewhere. It will be faster to add new info to an existing buffer, than create a new buffer each time you want to add something. Then use DrawIndexed to draw only what's needed.

EDIT: That being said, to answer your question regarding adding vertices at runtime (if you must):

To add new vertices to your scene:

1. If the current vertex buffer doesn't have room for the new vertices, get a copy of the old data from that buffer. Use context->Map(...) to get a pointer to the old data (similar to d3dx9 Lock); Copy the data to a CPU memory in a data array large enough to hold old + new data. Unmap.

2. Release the vertex buffer.

3. Add your new vertex data to the CPU data array.

4. Create a new vertex buffer (at least) large enough to hold all the data. Create it with appropriate flags if you anticipate adding more data later. Initialize the new vertex buffer from the data array (old+new verex data).

5. Update your index buffer similarly. Be sure to keep track of which objects are at which index positions, etc.

If your current vertex buffer is large enough to add new data to the end of the buffer, Map the buffer, copy new data to the mapped subresource, and Unmap the buffer. That process is similar to D3DX9 Lock, copy, Unlock.

EDIT: As you seem familiar with D3DX9, note that creating a D3D11 vertex buffer with initialization data is similar to the D3DX9 process of:

a. create the buffer.

b. Lock() the buffer.

c. copy data to the buffer.

d. Unlock() the buffer.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

I suggest modifying the above advice. If your buffer isn't big enough for the data you need to draw in a frame, then you should have created a bigger buffer to begin with. On PC there is, in the general case, absolutely no win to be had from micromanaging tiny amounts of memory. If for example you find that one time out of 10 you need a 512k buffer, but the other 9 times 64k is enough, then just create the 512k buffer. "Wasting" the extra memory each frame is far preferable to having lots of little hitches and stalls incurred by destroying and creating resources at runtime.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.


I suggest modifying the above advice. If your buffer isn't big enough for the data you need to draw in a frame, then you should have created a bigger buffer to begin with.


If you KNOW there are going to be more additions to the vertex buffer, you can initially create the buffer larger than initially needed, and keep track of (all) object indices elsewhere. It will be faster to add new info to an existing buffer, than create a new buffer each time you want to add something.

I'm willing to learn - but I'm not sure I understand the difference between what you're suggesting and my suggestion. Can you clarify?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.


I suggest modifying the above advice. If your buffer isn't big enough for the data you need to draw in a frame, then you should have created a bigger buffer to begin with.


If you KNOW there are going to be more additions to the vertex buffer, you can initially create the buffer larger than initially needed, and keep track of (all) object indices elsewhere. It will be faster to add new info to an existing buffer, than create a new buffer each time you want to add something.

I'm willing to learn - but I'm not sure I understand the difference between what you're suggesting and my suggestion. Can you clarify?

What I'm talking about is the 5 points under your "to add new vertices to your scene" heading; maybe you need to clarify that you don't mean "do this at runtime"?

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This topic is closed to new replies.

Advertisement