Jump to content
  • Advertisement
Sign in to follow this  
SteveHatcher

How to draw multiple objects to screen reusing draw commands?

This topic is 2102 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,

 

I am working on a very simple game in which the player controls a quad and there are some triangles and other shapes on the screen. Right now there is a separate class for quad and triangle as I want to be able to specify different parameters and 'behaviors' for them.

 

The problem is I am a bit confused in the cleanest way to draw multiple vertexBuffers to screen.

 

Right now my quad's initialization is as follows:

bool Quad::initialize(Graphics *g)
{
	graphics = g;

	Vertex vtx[] =
	{
		Vertex(-0.2f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
		Vertex(0.2f, 0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
		Vertex(-0.2f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
		Vertex(0.2f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f),
	};

	graphics->createVertexBuffer(ARRAYSIZE(vtx), vtx, &vertexBuffer);
	graphics->createShaders();

	return true;
}

and my triangles initialize is exactly the same just with different vertex points.

 

The problem is when it comes to the draw command in my code eg quad.draw() and then triangle.draw(), they get sent to:

bool Graphics::draw(ID3D11Buffer *vertexBuffer, const ShapeData &shapeData)
{
	float bgColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; //Clear our backbuffer
	d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor);

	//Refresh the Depth/Stencil view
	d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

	UINT stride = sizeof(Vertex);
	UINT offset = 0;

	d3d11DevCon->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);

	camPosition = XMVectorSet(0.0f, 0.0f, -3.0f, 0.0f);
	camTarget = XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
	camUp = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);

	//Set the View matrix
	camView = XMMatrixLookAtLH(camPosition, camTarget, camUp);

	//Set the Projection matrix
	camProjection = XMMatrixPerspectiveFovLH(0.4f*3.14f, (float)GAME_WIDTH / GAME_HEIGHT, 1.0f, 1000.0f);

	XMMATRIX WVP = XMMatrixIdentity();

	Translation = XMMatrixTranslation((float)shapeData.x, (float)shapeData.y, 0.0f);

	XMMATRIX World = Translation;

	WVP = World;

	cbPerObj.WVP = XMMatrixTranspose(WVP);
	d3d11DevCon->UpdateSubresource(cbPerObjectBuffer, 0, NULL, &cbPerObj, 0, 0);
	d3d11DevCon->VSSetConstantBuffers(0, 1, &cbPerObjectBuffer);

	d3d11DevCon->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
	d3d11DevCon->Draw(4, 0);

	return true;
}

So as you can see the vertex buffer just gets overwritten with whatever the last .draw command contains. It works currently but I can only ever have one thing on screen at once (whatever is the last shape.draw() I have specified).

 

How would I go about reusing this draw command but allowing me to draw multiple vertexBuffers?

 

Thanks

Edited by SteveHatcher

Share this post


Link to post
Share on other sites
Advertisement

Don't clear the render target and depth stencil buffer each time you draw a single object. Do that once per frame only in a separate method. High-level code structure should be something like: 

initialize()

while running:
    clear back buffer
    
    for each object:
        draw object

    flip back buffer 

You'll want to eventually start batching or even instancing your object.  Each call to draw and state change has a substantial cost; you want to get as much geometry on screen as possible with each individual draw call.  For such simple geometry as rectangles and single triangles, you could fill in a single vertex buffer with all objects and then draw it with a single call.  More complicated setups can often be handled with instancing.  When you really do need multiple draw calls you'll at least want to batch the state together (keeping with this current exmample: draw all rectangles and then draw all triangles so you only need to change vertex buffer twice per frame instead of once per object).

Share this post


Link to post
Share on other sites

Thank you very much. I changed the position of clear render target and depth stencil buffer to where you say and it works perfectly now.

 

So just to make sure I understand what you mean, right now my program fills vertex buffer -> calculates translations -> draws new x-y location onto screen each frame update.

Later down the track when I add some other shapes eg n cubes and n triangles, I can load the vertex buffer up with the vertices for those n number of cubes, and then plonk them all down on the screen at once with Draw. Same thing with the triangles... So right now am I basically wasting all of the 'space' available in the verex buffer by just loading it with one geometry?

 

So in terms of game objects, how do I decide which ones I can concatenate into one vertex buffer? For instance if two cubes are player controlled, 3 triangles just bounce around the screen, and there are some static walls just made out of rectangles. Is it just based on which objects need to update eg their position at certain times?

 

Thanks for your time and clear explanation.

Edited by SteveHatcher

Share this post


Link to post
Share on other sites


So right now am I basically wasting all of the 'space' available in the verex buffer by just loading it with one geometry?

 

No. What you may be wasting is bandwidth. The concept to keep in mind is this: Passing information to and from the graphics card is slow, therefore you should do it as few times as possible.

You should also know that it only really matters when your game is running in interactive mode -> you should do slow stuff at Load Time, instead of Run Time because slow is only slow for real time stuff, but it's still fast enough to do in a Loading screen.

 

Now, every time you tell your GPU to render something, that's information passing to and from the GPU*, so if you could manage to render your whole scene with just 1 draw call, it would be awesome, but you're probably going to need many more. However you group (as much as you can) static geometry in big buffers, instead of having all static objects in their separate buffers, you will be able to render much more objects with just 1 draw call! Example: Calling Draw() 1000 times, each time rendering just 1 polygon is much slower than rendering 10000 polygons with just 1 Draw() call. This is called geometry batching in case you want to research further.

 


So in terms of game objects, how do I decide which ones I can concatenate into one vertex buffer? For instance if two cubes are player controlled, 3 triangles just bounce around the screen, and there are some static walls just made out of rectangles. Is it just based on which objects need to update eg their position at certain times?

That's a tough question engine developers have to fight with every day! Indeed dividing you geometry in 2 groups (static geometry and non-static geometry) is a good start! Basically you should find the way that allows you to render more stuff with less Draw calls and state changes.

 

 

* Driver details apply here, but ignore these for now.

 

P.S: Geometry Instancing also allows you to render the same (small?) vertex buffer multiple times with different properties per "object" with just 1 draw call. This is useful to render stuff like a bunch of trees or rocks without actually duplicating the objects in the vertex buffer (wasting memory).

Share this post


Link to post
Share on other sites

Thank you very much for that excellent explanation.

 

I guess right now because I am only talking about a few objects on the screen at once it wont be much of a problem to lazily use draw(), but I am keen on learning good habits first so I will try and batch some of these draw calls together.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!