I’ve added some example code (written using the GLUT library) to the VBO Tutorial down below. This has actually been quite a popular tutorial (well, at least for a tiny site like mine).
I might look into doing some more. Possibly starting with an FBO tutorial and from there moving into some basic GLSL.
Just installed Snow Leopard on my Macbook Pro, and I’ve been wondering for ages what version of OpenGL it would come with (I want to play around with OpenGL 3). Here’s the result….
*sigh*
2.1 NVIDIA-1.6.0
With OpenCL being included, I was hoping a lot of the subsystems would be upgraded. Alas, it hasn’t happened. It’s not like OpenGL 3 is new either, it’s been out for over a year (current release is 3.2).
Initial setup:-
Struct for holding vertex data and some #define’s for accessing it
// Location/Normals #define X_POS 0 #define Y_POS 1 #define Z_POS 2 // Texture Coordinates #define U_POS 0 #define V_POS 1 // Colours #define R_POS 0 #define G_POS 1 #define B_POS 2 #define A_POS 3 typedef struct { GLFloat location[3]; GLFloat tex[2]; GLFloat normal[3]; GLFloat colour[4]; GLubyte padding[16]; // Pads the struct out to 64 bytes for performance increase } Vertex;
So now basically we need to have a place to store all of this data. Now while it would be nice to only store 8 verticies (one for each corner of the cube), we are unable to due to the normals pointing in different directions per face. The best way to remember this is a vertex is a combination of position, normal, texture coordinate and colour, as soon as one of these is different, it’s a different vertex. A cube is an extreme example, in most models, we would be able to share verticies (this is important for the next step).
Vertex verts[24]; // We're making a cube, 6 faces * 4 verticies per face
Now we need to have a place store each index to a face. Basically what this is, is an array that defines each triangle. I’ll go into this more later
GLubyte index[36]; // 2 Triangles per face (possible to use quads, but they're being phased out of OpenGL3, so we're using triangles instead)
So now we initialise our vertex array and index array (only showing 1 face in here to save space, the rest are up left as an exercise to the reader)
void buildCube() { verts[0].location[X_POS] = -1; verts[0].location[Y_POS] = -1; verts[0].location[Z_POS] = 1; verts[0].normal[X_POS] = 0; verts[0].normal[Y_POS] = 0; verts[0].normal[Z_POS] = 1; verts[0].tex[U_POS] = 0; verts[0].tex[V_POS] = 0; verts[1].location[X_POS] = -1; verts[1].location[Y_POS] = 1; verts[1].location[Z_POS] = 1; verts[1].normal[X_POS] = 0; verts[1].normal[Y_POS] = 0; verts[1].normal[Z_POS] = 1; verts[1].tex[U_POS] = 0; verts[1].tex[V_POS] = 1; verts[2].location[X_POS] = 1; verts[2].location[Y_POS] = 1; verts[2].location[Z_POS] = 1; verts[2].normal[X_POS] = 0; verts[2].normal[Y_POS] = 0; verts[2].normal[Z_POS] = 1; verts[2].tex[U_POS] = 1; verts[2].tex[V_POS] = 1; verts[3].location[X_POS] = 1; verts[3].location[Y_POS] = -1; verts[3].location[Z_POS] = 1; verts[3].normal[X_POS] = 0; verts[3].normal[Y_POS] = 0; verts[3].normal[Z_POS] = 1; verts[0].tex[U_POS] = 1; verts[0].tex[V_POS] = 0; // ********* SNIP (I'll let you fill in the rest of the cube here) ********* // Colors for (int i = 0; i < 24; i++) { verts[i].colour[R_POS] = 1.0; verts[i].colour[G_POS] = 1.0; verts[i].colour[B_POS] = 1.0; verts[i].colour[A_POS] = 1.0; } // Index Array (define our triangles) // A Face looks like (numbers are the array index number of the vertex) // 1 2 // +------+ // | | // | | // +------+ // 0 3 index[0] = 0; index[1] = 1; index[2] = 2; index[3] = 2; index[4] = 3; index[5] = 0; // Repeated number 2 & 0 as they're shared // ********* SNIP (I'll let you fill in the rest of the cube here) ********* }
So as you can see above, in the index array, we’ve defined 2 triangles. (0, 1, 2) and (2, 3, 0). The diagram in the comments show the vertex indicies that map into our vertex array. So from that, you should be able to visualise the triangles in your head. As we’re drawing triangles directly (not triangle strips/fans or quads), we have to repeat some of the index numbers. This is a good thing as it means that we have some shared verticies, and have to upload less data to the graphics card.
So now we start the actual OpenGL code. Firstly we set up the vertex buffer. There are two buffers that get uploaded to the graphics card. One contains the verticies, and the other is the index array. This only needs to be done once (even if the model changes, I’ll explain how to change individual vertices at the end).
// A helper macro to get a position #define BUFFER_OFFSET(i) ((char *)NULL + (i)) GLuint vboID; // Vertex Buffer, this needs to be accessable wherever we draw from, so in C++, this would be a class member, in regular C, it would probably be a global variable; glGenBuffers(1, &vboID); // Create the buffer ID, this is basically the same as generating texture ID's glBindBuffer(GL_ARRAY_BUFFER, vboID); // Bind the buffer (vertex array data) // Allocate space. We could pass the mesh in here (where the NULL is), but it's actually faster to do it as a // seperate step. We also define it as GL_STATIC_DRAW which means we set the data once, and never // update it. This is not a strict rule code wise, but gives hints to the driver as to where to store the data glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * 24, NULL, GL_STATIC_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertex) * 24, verts); // Actually upload the data // Set the pointers to our data. Except for the normal value (which always has a size of 3), we must pass // the size of the individual component. ie. A vertex has 3 points (x, y, z), texture coordinates have 2 (u, v) etc. // Basically the arguments are (ignore the first one for the normal pointer), Size (many components to // read), Type (what data type is it), Stride (how far to move forward - in bytes - per vertex) and Offset // (where in the buffer to start reading the data - in bytes) // Make sure you put glVertexPointer at the end as there is a lot of work that goes on behind the scenes // with it, and if it's set at the start, it has to do all that work for each gl*Pointer call, rather than once at // the end. glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(12)); glNormalPointer(GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(20)); glColorPointer(4, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(32)); glVertexPointer(3, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(0)); // When we get here, all the vertex data is effectively on the card // Our Index Buffer, same as above, the variable needs to be accessible wherever we draw GLuint indexVBOID; glGenBuffers(1, &indexVBOID); // Generate buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBOID); // Bind the element array buffer // Upload the index array, this can be done the same way as above (with NULL as the data, then a // glBufferSubData call, but doing it all at once for convenience) glBufferData(GL_ELEMENT_ARRAY_BUFFER, 36 * sizeof(GLubyte), index, GL_STATIC_DRAW);
And that’s all there is to getting the data onto the card itself. The only gotcha is to put the glVertexPointer last when you’re setting up your pointers.
Now to paint the code. This will need to go in your render loop after you’ve done your camera transformations
// Bind our buffers much like we would for texturing glBindBuffer(GL_ARRAY_BUFFER, vboID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexVBOID); // Set the state of what we are drawing (I don't think order matters here, but I like to do it in the same // order I set the pointers glEnableClientState(GL_TEXTURE_COORD_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); // Resetup our pointers. This doesn't reinitialise any data, only how we walk through it glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(12)); glNormalPointer(GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(20)); glColorPointer(4, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(32)); glVertexPointer(3, GL_FLOAT, sizeof(Vertex), BUFFER_OFFSET(0)); // Actually do our drawing, parameters are Primative (Triangles, Quads, Triangle Fans etc), Elements to // draw, Type of each element, Start Offset glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0)); // Disable our client state back to normal drawing glDisableClientState(GL_TEXTURE_COORD_ARRAY); glDisableClientState(GL_COLOR_ARRAY); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY);
And that’s all there is to drawing (and VBO’s pretty much). An interesting side note. Before VBO’s, it was more efficient to use GL_TRIANGLE_FAN and GL_TRIANGLE_STRIP for drawing, but now it’s much better to just use GL_TRAINGLES due to the way the card does caching. Using strips and fans can actually cause cache misses which is much worse on modern cards than having to reference more verticies.
If you want to dynamically update your mesh per frame, that’s quite easy too, although be warned, there is quite a performance hit by doing it (although, not as bad as recreating a display list). All you need to do is rather than using GL_STATIC_DRAW, use GL_STREAM_DRAW (if it’s changing per frame), or GL_DYNAMIC_DRAW (if it changes a bit, but not per frame). Then you can use glBufferSubData (shown where we set up the VBO initially) on either your vertex data or your index data to update a single value (or range of values).
VBO Example Code (requires GLUT)
As for my stuff, screenshot below, is reflecting a nice cubemap, mixed in with some colour, a dynamically moving light (doing per pixel lighting), all at a nice 40FPS (which makes this picture around 9 million triangles per second).
I’ve been working on a new OpenGL program. Didn’t finish the last one as I decided I wanted to make a portable application (ie, so I can port it to Windows, and possibly even DirectX one day). Only a touch of Objective-C/Cocoa to set up the windowing and use some nice OS specific functions (such as Cocoa automatically being able to decode PNG files), most of it is written in C++
So I’ve been playing around with VBO’s (Vertex Buffer Objects) and FBO’s (Frame Buffer Objects) which I had been doing in the previous program, but it turns out I was doing it slightly wrong. So now that it’s all working correctly I have a bit of an app going.
Currently it pretty much only loads Obj models (a simple ASCII based format), but most 3D applications will export this.
Underneath there is a lot going on. It has:-
Next things I’d like to do are is have a model manager to allocate one (or more) large VBO on the card to hold lots of smaller models and index them within the VBO, more shaders – I’d really love to try some SSAO (Screen Space Ambient Occlusion – basically simulating the way that indirect light will still light and shadow things) and deferred lighting/shadowing and write a full GUI for it for things like menus, combo boxes, buttons, etc.
I’m sure I’ll come up with a lot more, possibly turning it into some type of game engine one day.
Here’s a screenshot of what I have so far.
It doesn’t look like much, but there’s a lot of work going on there.
The colours you can see on the model (which I actually made myself) are what’s known as a normal map. It’s the way lighting is calculated on 3D models.
Unfortunately there’s a couple of problems with this model.
One thing that may stick out in that screenshot too (at least if you click it and view it in full size) is that the frame rate is only 20FPS. Basically there are three reasons for this:-
So next post, I’ll hopefully show a proper render of the model. Maybe even throw in a quick tutorial on VBO’s (which are really quite easy when you get your head around them).
Ok, so I figured I’d jot down my thoughts on Apple’s announcements at WWDC09. Normally I’d just chuck this on Twitter, but I don’t think 140 characters isn’t enough
iPhone 3G S
I like that it’s got a speed upgrade and a dedicated 3D processor (I’m actually a little jealous of this one), but that’s about it really (for me anyway).
I’m a bit meh about the Magnometer (compass). I’m sure I’ll eat my words here when a killer app requires it.
The camera doesn’t really bother me. 3MP with crappy optics isn’t going to do much. I hardly use mine anyway, I’d much prefer to use a dedicated camera. Video could be nice, but once again, if I wanted video, I’d use a dedicated one.
Voice control, while a very nice technology, it’s not really practical. You can’t really use it in public without annoying people, and I don’t know about you, but when I’m by myself, I feel a bit stupid talking to nothingness.
All of the rest of the features are included in the 3.0 software update (which I’m actually quite excited about) that is coming out worldwide on the 17th of June (Australia included, their press release was actually incorrect).
MacBook Pro
This one is easy. I am so glad that I bought mine last year! At the same price point that I paid (bottom level 15″ MacBook Pro), you now get a downgraded machine
Pros:-
Cons:-
Snow Leopard
Out in September. This is going to be a fantastic upgrade. Full 64bit operating system. The current OS is 32bit with 64bit extensions (basically to allow addressing of over 4Gb of system memory – ie. This doesn’t just include installed RAM). Going to full 64bit should speed things up quite a bit.
Installation time has gone from 1 hour down to 15min which is a nice change, it’s not a big change in the scheme of things (you only do this once every now and again – much less on the Mac than Windows).
It’s smaller. This one surprised me. I’m currently hearing that the installed size is about 6gb smaller. I guess this is due to not having to have the PowerPC binaries in their apps anymore (Snow Leopard is Intel only). This is a very nice change that a newer operating system is faster and smaller than the one it’s upgrading.
OpenCL. Most people really don’t care about this one, but this is going to give some major performance increases for anyone with a GeForce 8600 or better. Basically your Dual Core system just got a whole lot more cores added to it (think hundreds of slower cores). Now while this won’t double performance in most cases, you will see some major speed-ups.
One that I don’t know which way it’s going (if anyone knows, let me know). OpenGL. Is Apple going to have OpenGL 3.1 (and GLSL 1.3) on this, or will it still be stuck back at OpenGL 2? I’d love to start playing around with 3.1 (some quite major changes) and I’m sure more apps/games would use this if available.
Safari 4
Meh.
Ok, so this is what I’ve been working on recently. I know there’s a few of these out there, but I’m more doing this to learn shaders for myself (along with VBO’s – Vertex Buffer Objects – and FBO’s – Frame Buffer Objects) along with Objective-C/Mac programming.
So what you’re seeing there is a slight Gaussian blur on the texture itself and a radial blur on the whole scene itself (using an FBO mapped onto a Quad that fills the screen).
As you can see on the right, it has a built in editor. It will eventually have syntax highlighting, but I had to disable that temporarily as it was causing some issues.