1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | //A simple 3D vertex public struct Vertex { public float X, Y, Z; public Vertex(float x, float y, float z) { X = x; Y = y; Z = z; } //The stride of this vertex //3 floats 4 bytes each //if you dont like counting bytes you can get this value a few different ways //System.Runtime.InteropServices.Marshal.SizeOf(new Vertex()); //OpenTK.BlittableValueType.StrideOf(new Vertex()); public const int Stride = 12; } //ID of the vertex buffer we will use private int ID_VBO; //ID of the element buffer private int ID_EBO; //You are not limited to using a struct //you can use (float[], byte[], int[]) really anything that can hold data //then you could stride the array with //Vertices.Length * sizeof(float) private Vertex[] Vertices; //I use ushort, for the simple reasons of //i know im never going to index a negative value //and 65,535 is a good vertex limit //but you can use whatever just remember the max_value is your limit private ushort[] Indices; //Keep in mind that you are using memory for everything //the best way to think about any GL buffer is as if its byte[] or ByteArray/ByteBuffer //Only need to call this once, place it in your programs initialize/load method private void InitializeVBO() { //Initialize vertex data and index data //Simple triangle in CW winding Vertices = new Vertex[3] { new Vertex( 0.0f, 0.0f, 0.0f), new Vertex(100.0f, 0.0f, 0.0f), //gl y coords are reversed depending on what direction you consider is down new Vertex( 0.0f, -100.0f, 0.0f) }; //Index data for what order to draw vertices Indices = new ushort[3] { //This is also something to keep in mind //reversing your winding has no performance advantage, //dont let someone tell you otherwise, all data gets read the same way //its always easier to change a few indices than edit vertices //since you are most likely in CCW winding since that is GL's default //i reversed it since the vertices are in CW :D //you can change GL's default winding by GL.FrontFace 0, 1, 2 }; //Setting up the vertex buffer //data has to be initialize before this part. //Generate a single buffer GL.GenBuffers(1, out ID_VBO); //Tell gl we are going to be using this buffer as an Array_Buffer for vertex data GL.BindBuffer(BufferTarget.ArrayBuffer, ID_VBO); //Add our vertex data to it, essentially the same thing as Array.Copy if it were a byte[] //1: targeting the Array_Buffer //2: for this we will need the full length in bytes of our vertex data //3: the data we want in this new buffer //4: we are only going to set this once so our usage will be static and draw/write //basically in full we're are telling gl that we want a write-only buffer that is locked :P GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(Vertices.Length * Vertex.Stride), Vertices, BufferUsageHint.StaticDraw); //This is just good practice GL.BindBuffer(BufferTarget.ArrayBuffer, 0); //Generate another single buffer GL.GenBuffers(1, out ID_EBO); //Tell gl we are going to be using this buffer as an Element_Buffer for indexing data already bound to an Array_Buffer GL.BindBuffer(BufferTarget.ElementArrayBuffer, ID_EBO); //the same as above only changing the data GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(Indices.Length * sizeof(ushort)), Indices, BufferUsageHint.StaticDraw); GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0); } //This will need to be called everyframe you want to see the buffer data so use in OnRenderFrame //or any method with a GL render context. public void RenderVBO() { GL.PushMatrix(); //So you can see it onscreen :P GL.Translate(-50, 50, -100); //We dont have color data so it will resort to the last use GL_Color GL.Color3(1.0f, 1.0f, 1.0f); //since we only have position data thats all that needs to be writen to gl //if you enable another as in ArrayCap.ColorArray and have no color data //you will get an outofbounds/outofmemory exception because there is not data to read //so good practice to enable/disable so you dont run into that problem when using //different types of vertices :D //Although if you know what type you are going to use throughout the whole process //you can remove enable/disable from this method and add enable to your init function GL.EnableClientState(ArrayCap.VertexArray); //Bind our vertex data GL.BindBuffer(BufferTarget.ArrayBuffer, ID_VBO); //Tell gl where to start reading our position data in the length of out Vertex.Stride //so we will begin reading 3 floats with a length of 12 starting at 0 GL.VertexPointer(3, VertexPointerType.Float, Vertex.Stride, 0); GL.BindBuffer(BufferTarget.ElementArrayBuffer, ID_EBO); //tell gl to draw from the bound Array_Buffer in the form of triangles with a length of indices of type ushort starting at 0 GL.DrawElements(BeginMode.Triangles, Indices.Length, DrawElementsType.UnsignedShort, 0); //unlike above you will have to unbind after the data is indexed else the Element_Buffer would have nothing to index GL.BindBuffer(BufferTarget.ArrayBuffer, 0); GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0); //Remember to disable GL.DisableClientState(ArrayCap.VertexArray); GL.PopMatrix(); } |
Also some good references
http://www.opengl.org/wiki/Vertex_Specification
http://www.opengl.org/wiki/Buffer_Object
http://www.opentk.com/doc/graphics/geometry/vertex-buffer-objects