DirectX 9 Tutorials – #02 – Vertex Buffers

Basically, a vertex buffer is a piece of memory that is either located in system memory or video memory, and as the name may suggest you, it holds the vertex informations for the object you want to draw. Starting from the first DirectX 9 release, it is possible to store more than one entity in the same vertex buffer, because the support functions accepts an offset parameters that let you choose from which vertex you want to start to operate.

To create a vertex buffer, the CreateVertexBuffer method is used. Here’s its prototype:

HRESULT IDirect3DDevice9::CreateVertexBuffer
(
	// Length of the vertex buffer
	UINT Length,

	// Usage flags
	DWORD Usage,

	// Vertex format
	DWORD FVF,

	// Memory pool
	D3DPOOL Pool,

	// Pointer to a vertex buffer interface object pointer
	IDirect3DVertexBuffer9** ppVertexBuffer,

	// Reserved, must be null
	HANDLE* pSharedHandle
);

The Usage parameter is used to tell DirectX what we’re going to do with the object we want to create. If i had created a software vertex processing device, i would have to add the D3DUSAGE_SOFTWAREPROCESSING flag; since i’ve specified hardware processing instead, this flag is not set, and the default approach (hardware) will be used. The next flag i’ve specified into the source code (D3DUSAGE_WRITEONLY) will be explained later when i speak about the memory pools.

The FVF (Flexible Vertex Format) parameter can be set to a valid FVF identifier in case you want to use a standard vertex format. Here’s the one i used in the source code:

DWORD g_dwVertexFormat = D3DFVF_XYZ | D3DFVF_DIFFUSE;

It tells DirectX that for each vertex stored in the buffer, 3 float data types will be reserved for the XYZ coordinates, and a double-word (4 bytes) will be used to define the color.  I will not explain here what happen when you set it to zero; refer to the documentation or wait for the next tutorials.

Now that we have decided the format we want to use, we must create a vertex structure.

// Structure of a D3DFVF_XYZ | D3DFVF_DIFFUSE vertex
typedef struct __VERTEX
{
	// Position
	FLOAT fX, fY, fZ;

	// Color
	DWORD dwDiffuseColor;
} VERTEX, *LPVERTEX;

Note that DirectX expects to find the structure fields in a specific order; if you swap two variables, the vertex buffer will not be read in the correct way, and the geometry will appear corrupted. This is the order you must use:

  1. Position
  2. RHW value (only for already transformed vertices)
  3. Blending values (up to 5)
  4. Vertex normal
  5. Vertex point size
  6. Diffuse color
  7. Specular color
  8. Texture coordinates (up to 8 pairs of UV coordinates)

The memory pool field defines where the created object will be stored; valid values are:

  • D3DPOOL_DEFAULT: DirectX will automatically store the resource in the best position available according to the specified usage flags (usually, it is placed into video memory). When using this pool for vertex buffer, the D3DUSAGE_WRITEONLY must be specified to improve performances. Default memory pool resources must be released everytime a lost device status is detected; if you do not release them before calling the Reset method, you will deadlock the application (TestCooperativeLevel will return a D3DERR_DEVICENOTRESET status but the Reset method will always fail).
  • D3DPOOL_MANAGED: The resources will be stored in system memory and copied into video memory as needed. Managed resources does not need to be released and recreated when the device is reset.
  • D3DPOOL_SYSTEMMEM: This memory pool is located into the system memory. The resources will not be copied into video memory and does not need to be created after a lost device. The pourpose of this memory pool is to store data used to update surfaces or textures.
  • D3DPOOL_SCRATCH: It’s nearly the same as the previous one; the contents of the resources created here are indipendent from the DirectX device and can only be created, copied and locked. They can’t be set as textures or render targets.

The vertex buffer has been created but it doesn’t contains anything yet; in order to fill it with our vertices, we first need to define them in some way. Here’s where the vertex structure we created before comes in handy:

VERTEX g_a_vxVertices[] =
{
	// First vertex (red) of the first triangle
	-5.0f, 5.0f, 0.0f, 0xFFFF0000,

	// Second vertex (blue) of the first triangle
	5.0f, 5.0f, 0.0f, 0xFF0000FF,

	// Third vertex (green) of the first triangle
	-5.0f, -5.0f, 0.0f, 0xFF00FF00,

	// First vertex (yellow) of the second triangle
	5.0f, 5.0f, 0.0f, 0xFFFFF200,

	// Second vertex (purple) of the second triangle
	5.0f, -5.0f, 0.0f, 0xFF8F00FF,

	// Third vertex (orange) of the second triangle
	-5.0f, -5.0f, 0.0f, 0xFFFF8F00
};

Now that we have defined the geometry for our object, we need to store it inside the vertex buffer. The vertices have to be copied to the buffer returned from the Lock method; here’s the prototype:

HRESULT IDirect3DVertexBuffer9::Lock
(
	// Offset into the vertex data
	UINT OffsetToLock,

	// Size of the vertex data to lock
	UINT SizeToLock,

	// Pointer to a memory buffer containing the returned vertex data
	VOID **ppbData,

	// Flags
	DWORD Flags
);

A simple memcpy call will do the job:

memcpy(ReturnedPointer, ArrayOfVertices, sizeof(ArrayOfVertices))

The vertex buffer is ready to be drawn, we just need to call the Unlock() method; this will update the vertex data with the user-supplied buffer and release the lock. Everything i’ve explained now takes place inside the vInitializeDeviceObjects function.

Before we can render the vertices, we need to call BeginScene and tell DirectX the format we used to declare the vertices:

DirectX9Device->SetFVF(g_D3DFVF_XYZ | D3DFVF_DIFFUSE)

The last step is to set the stream source and call the draw method:

DirectX9Device->SetStreamSource
(
	// Stream number
	0,

	// Vertex buffer
	VertexBuffer,

	// Offset into the vertex buffer
	0,

	// Can be used for instancing, we will set this to the vertex size
	sizeof(VERTEX)
)

DirectX9Device->DrawPrimitive
(
	// Type of primitive to render
	D3DPT_TRIANGLELIST,

	// First vertex to draw
	0,

	// Primitive count
	sizeof(g_a_vxVertices) / sizeof(VERTEX)
)

The scene can now be drawn to screen calling EndScene and Present.

Source code: http://insanedevelopers.net/downloads/Sources/DirectX9Tutorials/02 – Vertex Buffers.zip

Comments are closed.