A vertex buffer is at its
most basic, an array of vertices. This array is contained within a
single chunk of memory ready to be passed to the video card for drawing.
The entire job of the vertex buffer is to hold a collection of vertices
that Direct3D will use to create 3D objects with. All vertices for an
object must live in a vertex buffer so they can be drawn.
Creating a Vertex Buffer
Vertex buffers are based on the ID3D10Buffer interface and are created using the function CreateBuffer. The CreateBuffer
function has multiple uses; it can create both vertex and index
buffers. Because the function can be used in multiple ways, you have to
define what you want it to do by filling out a D3D10_BUFFER_DESC structure. This structure describes not only the type of buffer you want, but the size and type of the data it will contain.
typedef struct D3D10_BUFFER_DESC {
UINT ByteWidth;
D3D10_USAGE Usage;
UINT BindFlags;
UINT CPUAccessFlags;
UINT MiscFlags;
} D3D10_BUFFER_DESC;
The D3D10_BUFFER_DESC structure encompasses five variables.
The first variable, ByteWidth, is the
number of bytes required for all the vertices the buffer will contain.
This can be calculated by multiplying the number of vertices by the size
of the custom vertex structure.
sizeof(VertexPosStruct) * numVertices;
The Usage variable details how the buffer will be used by the system. In most cases using a value of D3D10_USAGE_DEFAULT
is acceptable, letting the system know the buffer will be updated only
occasionally. If you absolutely will not be changing the data contained
in the buffer, use the value D3D10_USAGE_IMMUTABLE. This will
allow the system to optimize the access of the vertex buffer. If you
will be changing the information in the buffer regularly, the value D3D10_USAGE_DYNAMIC is for you. The system will make sure the buffer is accessible to you for updates.
The BindFlags variable dictates the type of buffer being created. In the case of vertex buffers, a value of D3D10_BIND_VERTEX_BUFFER should be used.
CPUAccessFlags specifies whether the CPU
should have access to the data in the buffer. If you want to be able to
update a buffer quite often, use the value D3D10_CPU_ACCESS_WRITE. Otherwise, a value of 0 can be used.
The final variable, MiscFlags, should have a value of 0 if you have a single Direct3D device. Setting a value of D3D10_RESOURCE_MISC_SHARED allows the buffer to be shared across multiple devices.
Now that you’ve described what type of buffer you
want to create, you have the opportunity to fill that buffer with some
data during the buffer creation process using the D3D10_SUBRESOURCE_DATA structure.
The D3D10_SUBRESOURCE_DATA structure builds a subresource filled with your vertex data that is then assigned to the buffer.
Since you’re creating a vertex buffer, the initial data will be filled with vertices. The D3D10_SUBRESOURCE_DATA structure contains a variable called pSysMem whose purpose is to point to an array of data; in this instance, an array of vertices.
D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices;
You now know the type of buffer you’re creating
and have a subresource created to pass into; those were the two items
you needed before creating the buffer. You can now call the CreateBuffer function, passing in the buffer description and subresource structures. The CreateBuffer function fills in the pVertexBuffer variable with the newly created vertex buffer.
ID3D10Buffer* pVertexBuffer;
// Do the creation of the actual vertex buffer
hr = pD3DDevice->CreateBuffer(&bufferDescription, &InitData, &pVertexBuffer);
The following code sample shows the steps necessary to create a vertex buffer.
ID3D10Buffer* pVertexBuffer;
// Create vertex buffer
VertexPosStruct vertices[] =
{
D3DXVECTOR3(0.0f, 0.5f, 0.5f),
D3DXVECTOR3(0.5f, -0.5f, 0.5f),
D3DXVECTOR3(-0.5f, -0.5f, 0.5f),
};
// Calculate the number of vertices in the array
int numVertices = sizeof(vertices) / sizeof(VertexPosStruct);
D3D10_BUFFER_DESC bufferDescription;
bufferDescription.Usage = D3D10_USAGE_DEFAULT;
bufferDescription.ByteWidth = sizeof(VertexPosStruct) * numVertices;
bufferDescription.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bufferDescription.CPUAccessFlags = 0;
bufferDescription.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices;
// Do the creation of the actual vertex buffer
hr = pD3DDevice->CreateBuffer(&bufferDescription, &InitData, &pVertexBuffer);
if(FAILED(hr))
{
return false;
}
Before drawing can take place, the vertex buffer must be bound to the Direct3D pipeline using the IASetVertexBuffers function. This function allows the vertices in the buffer to be accessed and used for drawing. The IASetVertexBuffers function requires five parameters.
The first parameter is the starting slot. Since
this function allows for more than one vertex buffer to be bound at a
time, this parameter lets Direct3D know which vertex buffer you want to
start with. Multiple vertex buffers can be passed into this function as
part of an array.
The second parameter is the number of vertex
buffers in the array. Most of the time, only a single buffer is being
bound so you’ll commonly see 1 being used.
The third parameter is a pointer to the array of
vertex buffers. If only one buffer is being used, this will point to the
single buffer.
The next parameter is called the stride. The
stride is similar to pitch and means the number of bytes that each
vertex in the buffer requires. The stride can be determined by using the sizeof
function on the custom vertex structure. This parameter allows for an
array of stride values to be passed in if more than one vertex buffer is
being bound.
The final parameter is the offset value. The
offset value defines the first vertex in each buffer to be used. If you
intend to use all the vertices in a buffer, then this value should be 0.
The following example call shows how to use the IASetVertexBuffers function.
// Set vertex buffer
UINT stride = sizeof(VertexPosStruct);
UINT offset = 0;
pD3DDevice->IASetVertexBuffers(0, 1, &modelObject->pVertexBuffer, &stride,
&offset);