Microsoft Developer Network - Windows Graphics
This sample demonstrates how to implement instancing using DirectX 11. Instancing is a technique that allows you to draw multiple copies of the same mesh with a single draw call, significantly improving performance for scenes with many similar objects.
Instancing in DirectX 11 is achieved through the use of Input Layouts and Vertex Buffers that contain per-instance data. This per-instance data is typically sent to the vertex shader and used to transform, color, or otherwise modify each instance of the object.
DrawInstanced
or DrawIndexedInstanced
: The primary API calls used to render multiple instances.This sample typically includes the following components:
struct VS_INPUT
{
float4 Pos : POSITION;
float2 Tex : TEXCOORD;
};
struct VS_OUTPUT
{
float4 Pos : SV_POSITION;
float2 Tex : TEXCOORD;
};
// Per-instance data structure
struct INSTANCE_DATA
{
float4x4 World : WORLDMATRIX;
float4 Color : COLOR;
};
// Buffer holding per-instance data
cbuffer cbInstanceData : register(b0)
{
INSTANCE_DATA instances[MAX_INSTANCES];
};
VS_OUTPUT Main(VS_INPUT input, uint instanceID : SV_InstanceID)
{
VS_OUTPUT output;
float4 worldPos = mul(input.Pos, instances[instanceID].World);
output.Pos = mul(worldPos, ViewProjection); // ViewProjection matrix would be a constant buffer
output.Tex = input.Tex;
return output;
}
The C++ code would involve creating two vertex buffers: one for the mesh geometry and one for the instance data. The input layout would be configured to read from both buffers, with the instance data buffer marked for per-instance data access.
Here's a conceptual snippet of setting up the input layout and buffers:
// Device and DeviceContext objects are assumed to be initialized
// Vertex Buffer for mesh geometry
D3D11_BUFFER_DESC vbDesc;
vbDesc.ByteWidth = sizeof(MeshVertex) * numVertices;
vbDesc.Usage = D3D11_USAGE_DEFAULT;
vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbDesc.CPUAccessFlags = 0;
vbDesc.MiscFlags = 0;
// Create and fill vbDesc.pSysMem with vertex data
// Instance Data Buffer
D3D11_BUFFER_DESC instanceBufferDesc;
instanceBufferDesc.ByteWidth = sizeof(InstanceData) * numInstances;
instanceBufferDesc.Usage = D3D11_USAGE_DEFAULT;
instanceBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
instanceBufferDesc.CPUAccessFlags = 0;
instanceBufferDesc.MiscFlags = 0;
// Create and fill instanceBufferDesc.pSysMem with instance data (world matrices, colors, etc.)
// Input Layout Description
std::vector<D3D11_INPUT_ELEMENT_DESC> layout;
// Add elements for mesh geometry (POSITION, TEXCOORD, etc.)
// Add elements for instance data (WORLDMATRIX, COLOR, etc.) with InputSlotClass = D3D11_INPUT_PER_INSTANCE_DATA
// and InstanceDataStepRate set appropriately (e.g., 1 for world matrix)
// Create Input Layout
// Compile vertex shader, get shader signature
// device->CreateInputLayout(&layout[0], layout.size(), shaderBlob->GetBufferPointer(), shaderBlob->GetBufferSize(), &inputLayout);
// Set Vertex Buffers for rendering
UINT strides[2];
UINT offsets[2];
ID3D11Buffer* vertexBuffers[2];
// Mesh vertex buffer
vertexBuffers[0] = meshVertexBuffer;
strides[0] = sizeof(MeshVertex);
offsets[0] = 0;
// Instance data buffer
vertexBuffers[1] = instanceBuffer;
strides[1] = sizeof(InstanceData);
offsets[1] = 0;
context->IASetVertexBuffers(0, 2, vertexBuffers, strides, offsets);
context->IASetInputLayout(inputLayout);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Draw call
context->DrawInstanced(numVertices, numInstances, 0, 0); // Or DrawIndexedInstanced
You can download the complete Visual Studio project for this DirectX 11 instancing sample to explore the code in detail.
Download Sample (ZIP)