3
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ What, specifically, are you having trouble with? This is pretty close to a "where to get started" type question. You need to try to implement this and tell us where it's going wrong. \$\endgroup\$ – MichaelHouse Jan 8 '14 at 1:29
  • \$\begingroup\$ @Byte56 After reading What topics can I ask about here? and What types of questions should I avoid asking? it seems to be fine on this webiste. The latter article also states that questions like “I would like others to explain ______ to me” are OK. I'm sorry if I misinterpreted something. If I indeed am wrong, do you know where I could ask a question like this? \$\endgroup\$ – davidwroxy Jan 8 '14 at 2:02
  • \$\begingroup\$ Yep, there's also a scope limitation. It's totally fine to have people explain things to you, but the scope needs to be limited. Large topics should be broken up into smaller parts. Then those smaller parts can be explained separately. This is why I ask for a more specific problem you're having. Generally the best way to find out that specific problem is to try implementing it and see where you get stuck. \$\endgroup\$ – MichaelHouse Jan 8 '14 at 2:12
  • \$\begingroup\$ @Byte56 I edited my question, explained where I got stuck and for what I exactly need geometry instancing. Does that help? \$\endgroup\$ – davidwroxy Jan 8 '14 at 2:27
  • \$\begingroup\$ Much better, thanks. I did a little editing to cut down on the intro. \$\endgroup\$ – MichaelHouse Jan 8 '14 at 2:35
2
\$\begingroup\$

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...

\$\endgroup\$
  • \$\begingroup\$ also...see the D3D9 SDK docs because they have a great example on using fewer variables to define the rotation of an object... \$\endgroup\$ – P. Avery Mar 22 '14 at 19:59
  • \$\begingroup\$ +1 for posting the vertex declaration. Using the ones directly from the MSDN article doesn't work, it appears you need to combine the vertex information and instance information(transform matrices, etc) into a single vertex declaration. I read somewhere that MSDN separates them into two vertex declaration for demonstration purpose. \$\endgroup\$ – JamesAMD Sep 2 '15 at 13:30
2
\$\begingroup\$

The DirectX SDK has samples on how to do this, and there are resources online you can find on how to do this. Look in C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Samples\C++\Direct3D\Instancing\Instancing.cpp.

Here's a Nvidia GPU Gem article on Geometry Instancing.

And here's an article on MSDN. This article shows you the two vertex buffers in a diagram, shows you the format they use, then immediately show you how to setup the stream source using the D3D device. Setting up instance data requires using the D3DSTREAMSOURCE_INSTANCEDATA flag.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.