[TOC]
With all the objects from the previous chapters bound to the pipeline we are almost ready to draw an object. When drawing you must ensure a pipeline state of GraphicsPipelineState type is bound, instead of ComputePipelineState.
Next you need to specify what type of primitives you wish to draw by calling @ref bs::ct::RenderAPI::setDrawOperation "ct::RenderAPI::setDrawOperation()", which accepts any of the types defined in @ref bs::DrawOperationType "DrawOperationType". This determines how are contents of the index buffer interpreted (or vertex buffer if index isn't available). The available draw types are:
@ref bs::DOT_TRIANGLE_FAN "DOT_TRIANGLE_FAN" - Each vertex (except the first two) forms a triangle with the first vertex and previous vertex.
// We're drawing a triangle list
RenderAPI& rapi = RenderAPI::instance();
rapi.setDrawOperation(DOT_TRIANGLE_LIST);
Finally you can now draw the object by calling @ref bs::ct::RenderAPI::drawIndexed() "ct::RenderAPI::drawIndexed()". It requires the following parameters:
@p vertexCount - Number of vertices to draw. Since the actual number of primitives drawn is determined by the index count, this value is internally used just for tracking purposes and wont affect your rendering. In most cases you can specify the number of vertices in the vertex buffer(s).
SPtr<IndexBuffer> ib = ...;
SPtr<VertexBuffer> vb = ...;
UINT32 numIndices = ib->getProperties()->getNumIndices();
UINT32 numVertices = vb->getProperties()->getNumVertices();
RenderAPI& rapi = RenderAPI::instance();
rapi.drawIndexed(0, numIndices, 0, numVertices);
If drawing without an index buffer you can call @ref bs::ct::RenderAPI::draw() "ct::RenderAPI::draw()" instead. It requires only the @p vertexOffset and @p vertexCount parameters, with same meaning as above (except @p vertexCount in this case does affect the rendering).
SPtr<VertexBuffer> vb = ...;
UINT32 numVertices = vb->getProperties()->getNumVertices();
RenderAPI& rapi = RenderAPI::instance();
rapi.draw(0, numVertices);
Both ct::RenderAPI::draw() and ct::RenderAPI::drawIndexed() support drawing multiple instances of the same object using the @p instanceCount parameter. This can be used as an alternative for issuing multiple draw calls, as they may have a significant CPU overhead. Using instanced drawing you can draw the same geometry multiple times with almost no additional CPU overhead.
// Draw 5 instances of the currently bound geometry
rapi.drawIndexed(0, numIndices, 0, numVertices, 5);
In order for instanced drawing to actually be useful, in most cases we need a way to differentiate the instances. At the very least we usually want to draw the instances at different positions in the world.
This is done by creating a separate VertexBuffer that contains per-instance properties (like position). This buffer is created same as a normal vertex buffer, except it doesn't contain per-vertex data, and instead contains per-instance data.
In order to use such a buffer we need to let the pipeline know by creating an appropriate VertexDeclaration. We need to define a VertexDataDesc that contains per-instance data. This is done by specifying the @p instanceStepRate parameter when calling VertexDataDesc::addVertElem().
SPtr<VertexDataDesc> vertexDesc = VertexDataDesc::create();
// Per vertex data (same as for non-instanced drawing)
vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
vertexDesc->addVertElem(VET_FLOAT3, VES_NORMAL);
vertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
// Per instance data
vertexDesc->addVertElem(
VET_FLOAT3, // Each entry in the instance vertex buffer is a 3D float
VES_POSITION, // We map it to the position semantic as vertex shader input
1, // We use the semantic index 1, as 0th index is taken by per-vertex VES_POSITION semantic
1, // We use the second bound vertex buffer for instance data
1 // Instance step rate of 1 means the new element will be fetched from the vertex buffer for each drawn instance
);
// Create a vertex declaration
SPtr<VertexDeclaration> vertexDecl = VertexDeclaration::create(vertexDesc);
RenderAPI& rapi = RenderAPI::instance();
// Bind vertex declaration
rapi.setVertexDeclaration(vertexDecl);
// Bind per-vertex and per-instance vertex buffers
rapi.setVertexBuffers(0, { perVertexVB, perInstanceVB });
// Draw ...
As a way of making drawing easier you can also use @ref bs::ct::RendererUtility::draw "ct::RendererUtility::draw()" helper method, accessible globally through @ref bs::ct::gRendererUtility() "ct::gRendererUtility()". This method accepts a ct::Mesh as input and will automatically:
Execute a draw call
SPtr<Mesh> mesh = ...;
gRendererUtility().draw(mesh);