
Creating a VBO Cube (Vertex and Element Array Buffers)
Posted Wednesday, 9 February, 2011 - 08:35 by openechoI have been messing with C# and OpenTK for a few weeks now and I have spent the last few days attempting to get a cube rendering that is VBO based with elements that are also stored in the graphics card. I quickly got this to work on linux (my development platform) but it was not showing correctly on OSX. I did not see any errors, but my screen contained no geometry.
This version of the code works across Windows, OSX and Linux and is based on the current (opentk-2010-10-06.zip) release of OpenTK where it compiles without obsolete warnings. This code was largely based on and acknowledges http://www.opentk.com/node/425.
Hopefully it is useful for someone else.
using System; using System.Drawing; using System.Runtime.InteropServices; using System.Xml.Serialization; using OpenTK; using OpenTK.Graphics.OpenGL; namespace VBOCube { /// <summary> /// Struct to hold the Vector Data. You can use the standard Vector3 struct /// that comes with OpenTK. /// /// If your app supports other graphics libraries then it may be appropriate to /// have your own vector storage. /// </summary> [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Vector3f : IEquatable<Vector3f> { public float x, y, z; public static readonly int ByteSize = Marshal.SizeOf (new Vector3f ()); public Vector3f (float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public bool Equals (Vector3f other) { return x == other.x && y == other.y && z == other.z; } } class VBOCube : GameWindow { // Variables for the cube data Vector3f[] cubeVertexData = null; Vector3f[] cubeNormalData = null; int[] colorData = null; uint[] indicesVboData = null; // Variables for the graphics card handles int colorBufferID; int normalBufferID; int vertexBufferID; int indiciesBufferID; // Count of tri elements to be drawn int elementCount = 0; // Storage for the simple rotation angle to make it a bit more interesting double rotationAngle = 0; protected override void OnLoad (EventArgs e) { Title = "VBO Cube"; GL.ClearColor (Color.DarkBlue); // Vertex Data cubeVertexData = new Vector3f[] { new Vector3f (-1.0f, -1.0f, 1.0f), new Vector3f (1.0f, -1.0f, 1.0f), new Vector3f (1.0f, 1.0f, 1.0f), new Vector3f (-1.0f, 1.0f, 1.0f), new Vector3f (-1.0f, -1.0f, -1.0f), new Vector3f (1.0f, -1.0f, -1.0f), new Vector3f (1.0f, 1.0f, -1.0f), new Vector3f (-1.0f, 1.0f, -1.0f) }; // Normal Data for the Cube Verticies cubeNormalData = new Vector3f[] { new Vector3f (-1.0f, -1.0f, 1.0f), new Vector3f (1.0f, -1.0f, 1.0f), new Vector3f (1.0f, 1.0f, 1.0f), new Vector3f (-1.0f, 1.0f, 1.0f), new Vector3f (-1.0f, -1.0f, -1.0f), new Vector3f (1.0f, -1.0f, -1.0f), new Vector3f (1.0f, 1.0f, -1.0f), new Vector3f (-1.0f, 1.0f, -1.0f) }; // Color Data for the Cube Verticies colorData = new int[] { ColorToRgba32 (Color.Cyan), ColorToRgba32 (Color.Cyan), ColorToRgba32 (Color.DarkCyan), ColorToRgba32 (Color.DarkCyan), ColorToRgba32 (Color.Cyan), ColorToRgba32 (Color.Cyan), ColorToRgba32 (Color.DarkCyan), ColorToRgba32 (Color.DarkCyan) }; // Element Indicies for the Cube indicesVboData = new uint[] { 0, 1, 2, 2, 3, 0, 3, 2, 6, 6, 7, 3, 7, 6, 5, 5, 4, 7, 4, 0, 3, 3, 7, 4, 0, 1, 5, 5, 4, 0, 1, 5, 6, 6, 2, 1 }; int bufferSize; // Color Array Buffer if (colorData != null) { // Generate Array Buffer Id GL.GenBuffers (1, out colorBufferID); // Bind current context to Array Buffer ID GL.BindBuffer (BufferTarget.ArrayBuffer, colorBufferID); // Send data to buffer GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(colorData.Length * sizeof(int)), colorData, BufferUsageHint.StaticDraw); // Validate that the buffer is the correct size GL.GetBufferParameter (BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize); if (colorData.Length * sizeof(int) != bufferSize) throw new ApplicationException ("Vertex array not uploaded correctly"); // Clear the buffer Binding GL.BindBuffer (BufferTarget.ArrayBuffer, 0); } // Normal Array Buffer if (cubeNormalData != null) { // Generate Array Buffer Id GL.GenBuffers (1, out normalBufferID); // Bind current context to Array Buffer ID GL.BindBuffer (BufferTarget.ArrayBuffer, normalBufferID); // Send data to buffer GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(cubeNormalData.Length * Vector3f.ByteSize), cubeNormalData, BufferUsageHint.StaticDraw); // Validate that the buffer is the correct size GL.GetBufferParameter (BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize); if (cubeNormalData.Length * Vector3f.ByteSize != bufferSize) throw new ApplicationException ("Normal array not uploaded correctly"); // Clear the buffer Binding GL.BindBuffer (BufferTarget.ArrayBuffer, 0); } // Vertex Array Buffer { // Generate Array Buffer Id GL.GenBuffers (1, out vertexBufferID); // Bind current context to Array Buffer ID GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBufferID); // Send data to buffer GL.BufferData (BufferTarget.ArrayBuffer, (IntPtr)(cubeVertexData.Length * Vector3f.ByteSize), cubeVertexData, BufferUsageHint.DynamicDraw); // Validate that the buffer is the correct size GL.GetBufferParameter (BufferTarget.ArrayBuffer, BufferParameterName.BufferSize, out bufferSize); if (cubeVertexData.Length * Vector3f.ByteSize != bufferSize) throw new ApplicationException ("Vertex array not uploaded correctly"); // Clear the buffer Binding GL.BindBuffer (BufferTarget.ArrayBuffer, 0); } // Element Array Buffer { // Generate Array Buffer Id GL.GenBuffers (1, out indiciesBufferID); // Bind current context to Array Buffer ID GL.BindBuffer (BufferTarget.ElementArrayBuffer, indiciesBufferID); // Send data to buffer GL.BufferData (BufferTarget.ElementArrayBuffer, (IntPtr)(indicesVboData.Length * sizeof(int)), indicesVboData, BufferUsageHint.StaticDraw); // Validate that the buffer is the correct size GL.GetBufferParameter (BufferTarget.ElementArrayBuffer, BufferParameterName.BufferSize, out bufferSize); if (indicesVboData.Length * sizeof(int) != bufferSize) throw new ApplicationException ("Element array not uploaded correctly"); // Clear the buffer Binding GL.BindBuffer (BufferTarget.ElementArrayBuffer, 0); } elementCount = indicesVboData.Length; } protected override void OnUpdateFrame (FrameEventArgs e) { base.OnUpdateFrame (e); rotationAngle += System.Math.PI / 16; } protected override void OnRenderFrame (FrameEventArgs e) { base.OnRenderFrame (e); // Clear the buffers GL.Clear (ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); // Enable depth testing so our cube draws correctly GL.Enable (EnableCap.DepthTest); // Set the viewport GL.Viewport (0, 0, Width, Height); // Load a perspective matrix view GL.MatrixMode (MatrixMode.Projection); GL.LoadIdentity (); OpenTK.Matrix4 perspective = OpenTK.Matrix4.CreatePerspectiveFieldOfView ((float)(System.Math.PI / 4f), (float)Width / Height, 1f, 200f); GL.LoadMatrix (ref perspective); // Translate a little into the z-axis so we can see the cube GL.Translate (0, 0, -5f); // Rotate by the current angle GL.Rotate (rotationAngle, Vector3d.UnitY); if (vertexBufferID == 0) return; if (indiciesBufferID == 0) return; // Color Array Buffer (Colors not used when lighting is enabled) if (colorBufferID != 0) { // Bind to the Array Buffer ID GL.BindBuffer (BufferTarget.ArrayBuffer, colorBufferID); // Set the Pointer to the current bound array describing how the data ia stored GL.ColorPointer (4, ColorPointerType.UnsignedByte, sizeof(int), IntPtr.Zero); // Enable the client state so it will use this array buffer pointer GL.EnableClientState (ArrayCap.ColorArray); } // Vertex Array Buffer { // Bind to the Array Buffer ID GL.BindBuffer (BufferTarget.ArrayBuffer, vertexBufferID); // Set the Pointer to the current bound array describing how the data ia stored GL.VertexPointer (3, VertexPointerType.Float, Vector3f.ByteSize, IntPtr.Zero); // Enable the client state so it will use this array buffer pointer GL.EnableClientState (ArrayCap.VertexArray); } // Element Array Buffer { // Bind to the Array Buffer ID GL.BindBuffer (BufferTarget.ElementArrayBuffer, indiciesBufferID); // Draw the elements in the element array buffer // Draws up items in the Color, Vertex, TexCoordinate, and Normal Buffers using indices in the ElementArrayBuffer GL.DrawElements (BeginMode.Triangles, elementCount, DrawElementsType.UnsignedInt, IntPtr.Zero); } SwapBuffers (); } /// <summary> /// Converts a Color instance into an int representation /// </summary> /// <param name="c"> /// A <see cref="Color"/> instance to be converted /// </param> /// <returns> /// A <see cref="System.Int32"/> /// </returns> public static int ColorToRgba32 (Color c) { return (int)((c.A << 24) | (c.B << 16) | (c.G << 8) | c.R); } public static void Main (string[] args) { using (VBOCube p = new VBOCube ()) { p.Run (60); } } } }
If there is anything that is obsolete or should be corrected then I am very interested.
- openecho's blog
- Login or register to post comments


Comments
Re: Creating a VBO Cube (Vertex and Element Array Buffers)
Nice! It's very good for OpenGL/TK new programmers!
Re: Creating a VBO Cube (Vertex and Element Array Buffers)
Yep, it works perfectly in Windows7.
Do you know how can I display 10 cubes instead of only 1?
Re: Creating a VBO Cube (Vertex and Element Array Buffers)
I've just look again at your code, and I have some tips:
About the "Vector3f" struct:
It's good to use the StructLayout attribute, but you should use it that way(yes, it have a different):
[StructLayout(LayoutKind.Sequential, Pack = 1)]About the general code:
Use "class VBOCube" as a separated item, instead of a "GameWindow", so the developer can access many of them as pointed by flopoloco.
Again, it's good you write how to do it.
Re: Creating a VBO Cube (Vertex and Element Array Buffers)
Hey Gents,
Okay, over the past few days I have been moving through teaching myself how to use OpenTK and OpenGL.
The following example is a lot more sophisticated (has a texture thats mapped to each face of the cube). It shows how to make many instances of VBO. It also uses different matrix modes which took me a little while to understand.
I agree the cube object should be on its own.. but these are justs tests for me to understand the API then I move to the next. Anyway, here you go.
John
P.S. Just make a square jpg called texture.jpg and throw it in with the project.
Re: Creating a VBO Cube (Vertex and Element Array Buffers)
Hi there, I packed this functionality into a nice class to make it more reusable. I will work again on it by the 2nd of April, because I have to reach some deadlines in work. In the meantime if you can add texture or shader support for it then it would be great for all OpenTK users want to display graphics.
Re: Creating a VBO Cube (Vertex and Element Array Buffers)
Hi there, I packed this functionality into a nice class to make it more reusable. I will work again on it by the 2nd of April, because I have to reach some deadlines in work. In the meantime if you can add texture or shader support for it then it would be great for all OpenTK users want to display graphics.
Hello,
I did one with multiple textures some time ago.
http://www.opentk.com/node/2318
No shader support or lighting. I am yet to teach myself about either of these things.
Regards,
John
Re: Creating a VBO Cube (Vertex and Element Array Buffers)
I tried to extend flopoloco's code by adding the texture code from openecho's example but now it crashes when GL.DrawElements is called. Can anyone see why? I'm still trying to understand VBOs and the like.
Re: Creating a VBO Cube (Vertex and Element Array Buffers)
I have the problem that it draws me the textures 4 Times on each face:
2D Quad:
Why is that?