Sunday, May 24, 2015

3d - Implementing Geometry Instancing in DirectX


I have a project that I'm doing in Managed DirectX (DX9) where I want to render lots of grass. My current method (many draw calls) is slow, but I've heard Geometry Instancing can fix that.


I know that I have to have one vertex buffer to store the vertices that will drawn multiple times. I know that I need another vertex buffer to store the transformations (from what I've seen you can only have one additional vertex buffer). So should the second vertex buffer store transformation matrices? I tried coding this, but when I want to create a new vertex buffer one of the required parameters is vertexFormat, but I can't supply a vertex format since this is a matrix.



I also wanted to try to have the second vertex buffer store the translation values as Vector3, but how do I tell DirectX that the values in the second vertex buffer are to be used as translation values? Secondly, I also need rotation values. Do I store these in a third vertex buffer?


Basically, I want to use geometry instancing to draw multiple instances of a quad that are rotated and translated.



Answer



What you'll wind up doing David is creating a vertex buffer to hold the different transformation matrices for each instance of grass. You will need a vertex declaration that has your standard 'grass' vertex components( pos, norm, uv...etc ), and in that same declaration 4 additional 4-component floats to hold the 4x4 transformation matrix...the declaration will look like this:


IDirect3DVertexDeclaration9 *pVDInstances = NULL;

// create instance vertex declaration
D3DVERTEXELEMENT9 pElements[] = {
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, // position
{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 }, // normal

{ 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, // uv
{ 1, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 }, // 1st row transformation matrix
{ 1, 16, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 2 }, // 2nd row transformation matrix
{ 1, 32, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 3 }, // 3rd row transformation matrix
{ 1, 48, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 4 }, // 4th row transformation matrix
D3DDECL_END()// add additional data declarations before this line if you require( be sure to change the 'stride' when setting the 2nd stream source below... )
};

hr = pd3dDevice->CreateVertexDeclaration( ( D3DVERTEXELEMENT9* )&pElements, &pVDInstances );
if( FAILED( hr ) )

{
// handle failure
}

Take note of the first parameter of the D3DVERTEXELEMENT9 constructor...it is '0' for the mesh object and '1' for the transformation matrix. This is because you are going to instruct the graphics device to use 2 different vertex buffers. Also take note of the 'offset' parameters...they begin again at 0 for the 2nd vertex buffer.


Before you can render though, you must also set the frequency of transformation matrices per instance...such that for every instance of grass the graphics device will use 1 transformation matrix.


Before the draw call you will instruct the graphics device like this:


if( FAILED( hr = pd3dDevice->SetStreamSourceFreq( 1, ( D3DSTREAMSOURCE_INSTANCEDATA | 1ul ) ) ) )
{
// handle error

}


// the instance buffer holds the transformation matrices for each instance of grass
IDirect3DVertexBuffer9 *pVBInstances = pXGrassModel->GetInstanceBuffer();

// Set up the instance data stream
if( FAILED( hr = pd3dDevice->SetStreamSource( 1, pVBInstances, 0, sizeof( D3DXMATRIX ) ) ) ) // you can change this if your instance buffer holds more than a single transformation matrix
{
// handle error

}
if( FAILED( hr = pd3dDevice->SetVertexDeclaration( pVDInstances ) ) )
{
// handle error
}

// then set materials and textures as usual and draw


Here is a vertex shader for instances:


struct VS_OUTPUT_Inst
{

float4 Position : POSITION0;
float3 Normal : TEXCOORD0;
float2 UV : TEXCOORD1;
};

VS_OUTPUT_Inst InstancesPass_Vertex_Shader( float4 vPos : POSITION,
float3 vNormal : NORMAL,
float2 vUV : TEXCOORD0,
float4 vRowX : TEXCOORD1,
float4 vRowY : TEXCOORD2,

float4 vRowZ : TEXCOORD3,
float4 vRowW : TEXCOORD4 )
{
VS_OUTPUT_Inst Output = ( VS_OUTPUT_Inst )0;

float4x4 mWorld;
mWorld._m00_m01_m02_m03 = vRowX.xyzw;
mWorld._m10_m11_m12_m13 = vRowY.xyzw;
mWorld._m20_m21_m22_m23 = vRowZ.xyzw;
mWorld._m30_m31_m32_m33 = vRowW.xyzw;


float4x4 mWorldView = mul( mView, mWorld );

float4 Pos = mul( vPos, mWorld );
Pos = mul( Pos, mView );
Pos = mul( Pos, mProjection );

Output.Position = Pos;

Output.Normal = normalize( mul( vNormal, ( float3x3 )mWorldView ) );


Output.UV = vUV;

return( Output );
}

Note the Semantics used here can be traced to the semantics used in the vertex declaration above...


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...