0

I need to draw million cubes. Cubes has the same vertex array, but may have different scale/position/rotation. Important: they may change their position dynamically, after vertex buffer creation. Some of them should be connected by the line. So I also need to draw lines.

Currently I deal with the instancing using constant buffer:

VertexShader.hlsl

struct VS_INPUT
{
float4 pos: POSITION;
float4 colour: COLOUR;
uint InstanceID: SV_InstanceID;
}
cbuffer ConstantBuffer : register(b0)
{
float4x4 view;
float4x4 proj;
float4x4 world[4000];
}

In D3D initialization I create one constant buffer resource, get the GPU address and copy there structure object:

struct ConstantBufferObject
{
XMFLOAT4X4 view;
XMFLOAT4X4 proj;
XMFLOAT4X4 world[4000];
}cbNPerObject;
...
memcpy(cbvGPUAddress[i],& cbNPerObject,sizeof(cbNPerObject));

Then I fill cbNPerObject.world the way I need and in the UpdatePipeline() make one call DrawIndexedInstanced() with number of cubes I added.

All works good, except one thing - constant buffer size restriction. float4x4 world[4000] can be maxim 4096 sized, but I need millions. Create chunks in form of constant buffers with 4096 size - seems not cool. So I decided to use another method of instancing - use vertex buffer.

I don’t understand how can I dynamically transform my instances when I use vertex buffer, because to change vertex buffer I have to change vertexBufferView which also seems incorrect. As far as I understand for instancing by this way I need to create instance buffer and store it together with vertex buffer or what, Im confused

Artur
  • 325
  • 2
  • 16

1 Answers1

0

You need to use an instance buffer; effectively storing the positions in a new vertex buffer which you map into memory and modify before submitting. Inside the vertex shader you then have access to the instance position for the thing you are currently drawing.

There's a good explanation of it here: https://www.braynzarsoft.net/viewtutorial/q16390-33-instancing-with-indexed-primitives

This example is based on DX11, but the principle is the same.

Somebody converted the approach to DX12 here: https://gamedev.stackexchange.com/questions/163077/dx12-passing-an-instance-buffer

cmaughan
  • 2,596
  • 5
  • 30
  • 35
  • I have read this article. Author also use constant buffer and can’t handle, for example 5000 leaves per tree(not more then 4096) – Artur Apr 02 '20 at 16:34
  • I actually totally confused, and tired reading tons of articles. Currently I don’t use instancing at all... – Artur Apr 02 '20 at 16:35
  • The point is to not use a constant buffer. You can't for that many instances. But you can use a Vertex Buffer which contains the position of each 'instance' of your geometry. Then in the vertex shader you can apply the position to your object. Think of it like this: you have 2 vertex buffers. One has the geometry for your shape in it, the other contains a list of Vec3 positions for all your instances. The key is that the DrawIndexedInstance API call has an IndexCountPerInstance value which means that you only sample the positions every 'n' vertices. – cmaughan Apr 03 '20 at 09:32
  • Obviously you still need a constant buffer for your global matrices, etc. – cmaughan Apr 03 '20 at 09:32
  • I understand what You are talking about - put proj and view matrixes in constant buffer and instancing in instance buffer. But instance buffer is situated in gpu memory as a vertex buffer. To change it I have to reupload it every frame. Is it correct? – Artur Apr 03 '20 at 09:39
  • Yup, that's correct :) But in DX12 you can persistently map the buffer into CPU memory and modify it directly. You'll have to use fences to stop synchronise access. You should be able to find many examples of this. The DX12 MiniEngine might be worth a look: https://github.com/microsoft/DirectX-Graphics-Samples/tree/master/MiniEngine – cmaughan Apr 05 '20 at 13:28