Wednesday, April 13, 2011

Creating a basic VertexBuffer

When rendering on a graphics card you specify a geometry for it to draw. This geometry is usually in a VertexBuffer and then processed by the rendering pipeline to create the image we see on screen. So, if the VertexBuffer is so important, how do we create one?

To have a very simple example I'm going to create the vertex buffer for a square. It will have 4 vertices and will contain some relevant information. To create our vertex buffer we will have to follow the following series of steps:
  1. Decide what vertex structure we are going to use
  2. Create a array of that type and fill it with data
  3. Create a VertexBuffer object
  4. Load the data from the array to the VertexBuffer
1. Decide what vertex structure we are going to use

In computer graphics 3D models are created using a mesh of primitives. These primitives describe the shape and position of the object and also contain additional information like normals and texture coordinates.These primitives can be points, lines, triangles or other polygons. All these primitives are represented using vertices. Mathematically the vertices are the defining point of the item. In the case of the point it itself is a vertex, for a line the start and end point represent the required vertices while a polygon while have a vertex wherever 2 edges meet. From this it becomes very clear that vertices will have to contain the position of point. Other than that they can also hold the color, normal, tangent, texture coordinate etc. required to render that object.
In XNA we already have 4 different vertex structures that we can use:
  • VertexPositionColor
  • VertexPositionColorTexture
  • VertexPositionNormalTexture
  • VertexPositionTexture
As you can see the name of the structure tells you what it contains, but what if I need a structure like VertexPositionNormalColor? Well then you will have to write your own vertex structure. You should select a vertex structures that can hold all the information you need. If you are wondering what information you need well... that depends on the application and type of rendering. To use a tint for each vertex you will need a color component, a texture component to texture the model or a normal for lighting calculations.
In this example I'll consider I just want to render a square with colors so I'll select VertexPositionColor.

Rendering of a simple square
How the rendered square will look

2. Create a array of that type and fill it with data

This part is really straightforward, you just write a line of code to declare the array:


    VertexPositionColor[] vertices;

After that we have to allocate the memory space for these items (4 because we want to create a square) and fill them with data:


    vertices = new VertexPositionColor[4];
 
    vertices[0].Color = Color.Azure;
    vertices[0].Position = new Vector3(-1, -1, 0);
    vertices[1].Color = Color.DarkGray;
    vertices[1].Position = new Vector3(-1, 1, 0);
    vertices[2].Color = Color.DeepSkyBlue;
    vertices[2].Position = new Vector3(1, 1, 0);
    vertices[3].Color = Color.Turquoise;
    vertices[3].Position = new Vector3(1, -1, 0);

As you can see this structure has 2 components: Color and Position. I set each of these components manually with built in types in the XNA framework. Color is a structure that has 4 components used to represent colors. It also has a lot of static members to represent different colors, as you can see in the example above I'm using some to make my work more simple. The Vector3 structure is used to represent vectors in 3D space, it has everything you need to work with vectors (including static members for some frequent used vectors). As you can see I'm creating new instances and setting them to the vertices.

3. Create a VertexBuffer object

You may be wondering, if I already have all the geometry data, why do I need to create another object? Well you might have all the data, but when you declare an array, where is the data stored? It is stored in the local memory (RAM) so if we want to draw it from there we have to send it to the graphics card each frame, that doesn't sound too optimal now does it? Considering the graphics card has it's own memory (and a very fast one too) why not store them in that memory? Well that's why we use a VertexBuffer. The buffer itself  represents the place in (graphics) memory and the amount of data to be stored. To declare and instance this a VertexBuffer we need to write the following lines of code:


    VertexBuffer buff;
    buff = new VertexBuffer(GraphicsDevice, VertexPositionColor.VertexDeclaration, 4, BufferUsage.WriteOnly);

So we can see that we have 4 parameters when we make this call. The first of them is a GraphicsDevice object called GraphicsDevice, this object is a our way to talk to the video card on board your computer, it is pre-declared by XNA for us and it is just like an interface to the object. The second parameter is the vertex declaration of our selected vertex type. The constructor requires this parameter so it will know how to interpret the data we are giving it, this is actually where we are telling the size of one element and what it contains (in our case a position and a color). Of course the next logical thing to come after this is how many elements of that type it will read (4 for a square). The last element is an enumeration called BufferUsage. This enumeration has 2 named constants called None and WriteOnly. WriteOnly means that data can't be read back from this buffer (write only) while None means that we can read it back to RAM. Because we are just going to draw these primitives we are going to select WriteOnly.

4. Load the data from the array to the VertexBuffer

Now that we have created our VertexBuffer object we only have to copy the data to the graphics memory. We do this by calling the SetData method of this object like so:


    buff.SetData<VertexPositionColor>(vertices);

As you can see the method has a generic (what is between the <>) where we have to tell it what type of structure it should expect to read.

Now that we have done all these steps all out 4 vertices are in graphics memory ready to be drawn over and over again.

4 comments:

  1. Hi,

    Nice tutorial, but how can I draw the box?

    I cant find a buff.Draw() funct...

    ReplyDelete
  2. Hi,

    There is no such function in this example, to draw this box please go to http://iloveshaders.blogspot.com/2011/04/drawing-simple-cube.html and read the tutorial from there. Also it has back references, other tutorials you have to read before drawing.

    ReplyDelete
  3. Yes, I've noticed later.

    Thanks for the response.

    ReplyDelete
  4. Great Tutorial so far! hoping to learn something ;)

    ReplyDelete