2D graphics with OpenGL Posted on 28 July 2008 | Tagged , | Comments (7)

Just a quick tech note here - I've been playing round with OpenGL a little bit recently, and managed to figure out how to do some basic 2D graphics with it. Chucking my code here in case it helps anyone - this is just for basic drawing image to the screen.

First things first, you need to create your window and OpenGL context. I use SDL to do most of the hard work for me.

// Initialise SDL & DevIL
SDL_Init(SDL_INIT_VIDEO);
ilInit();
// Enable double buffering
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// Initialise video surface
SDL_SetVideoMode(xRes, yRes, bpp, SDL_OPENGL);
// Set up OpenGL for 2d rendering
gluOrtho2D(0.0, xRes, yRes, 0.0);
// Enable textures
glEnable(GL_TEXTURE_2D);
// Enable blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

We use SDL to set up OpenGL for us, being careful to enable double buffering. The gluOrtho2D sets up a 2D coordinate system for us. Here I've made (0,0) the top left of the screen. Then I enabled textures (which we use to draw images) and alpha blending.

Before we can draw an image, we need to load it into a texture. I used the DevIL library to do this, as it is written specifically to work well with OpenGL. It provides some utility functions to do things quicker than I did it here, but I found the textures lost of a lot quality if I used them, so I stuck to the basics.

// Generate DevIL image and make it current context
ILuint image;
ilGenImages(1, &image);
ilBindImage(image);
// Load the image
if(!ilLoadImage(const_cast<char*>(filename.c_str()))) {
   throw runtime_error("Unable to load image " + filename);
}
width = ilGetInteger(IL_IMAGE_WIDTH);
height = ilGetInteger(IL_IMAGE_HEIGHT);
// Copy to OpenGL texture
if(!ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE)) {
   throw runtime_error("Unable to convert image " + filename + " to display friendly format.");
}
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Use nice (linear) scaling
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Use nice (linear) scaling
glTexImage2D(GL_TEXTURE_2D, 0, ilGetInteger(IL_IMAGE_BPP), width, height, 0, ilGetInteger(IL_IMAGE_FORMAT), GL_UNSIGNED_BYTE, ilGetData());
// Free DevIL image since we have it in a texture now
ilDeleteImages(1, &image);

Pretty straight forward - use DevIL to load the image, and then convert the image to a display friendly format and copy it into a texture. The final step is to actually draw the image on the screen. We do this by drawing a four sided polygon and binding the texture to it.

// Bind texture to current context
glBindTexture(GL_TEXTURE_2D, texture);
// Set the alpha
glColor4f(1.0, 1.0, 1.0, alpha);
// Draw texture using a quad
glBegin(GL_POLYGON);
// Top left
glTexCoord2f(0.0, 0.0);
glVertex2i(x, y);
// Top right
glTexCoord2f(1.0, 0.0);
glVertex2i(x + width, y);
// Bottom right
glTexCoord2f(1.0, 1.0);
glVertex2i(x + width, y + height);
// Bottom left
glTexCoord2f(0.0, 1.0);
glVertex2i(x, y + height);
// Finish quad drawing
glEnd();

Care must be taken here to make sure you draw your points clockwise, to make sure you get a front facing polygon. Texture coordinates are always from (0.0, 0.0) to (1.0, 1.0), regardless of the size of the image.

That's it! Tuck that away behind some abstraction so you don't have to deal with it, and you have nice hardware accelerated 2D drawing. I've been playing round with creating a 2D graphics library/engine with OpenGL, so I'll probably chuck that online in a little while. :)

Edit: Sorry about the lack of spacing in the code - SilverStripe appears to have a bug with line breaks in code blocks with bbcode. I'll fix this at some point - syntax highlighting might also be nice! :)

Post your comment

Comments

  • Hey, came here with a google search, i've found this really useful right now because i was wrestling with perspective matrices and stupid translations. Thank you! And write more!

    Posted by Can Mert, 16/07/2010 2:31am (2 months ago)

  • You dont have to draw coordinates in clockwise order, if you call the gl_frontFace(CCW) command (think thats it, i probably mispelt it) it sets the front face as if the coordinates as bing counter-clockwise to render. You can also repeat the command to keep switching modes (CCW and CW) so that you can easily use other peoples work where the vertices are in different orders.

    Posted by Stuart Page, 25/01/2010 10:10am (7 months ago)

  • Thanks dude, took ages to find this info ... Works a treat :)

    Posted by TruthSeeker, 05/06/2009 1:42am (1 year ago)

  • I should put a blog on my site.

    Posted by Jeremy, 28/07/2008 3:32am (2 years ago)

  • Cool man. I'm keen to see what you come up with. By the way, I saw a better molecule renderer than your OpenGL one...well better in the sense that it was ray-traced, but not real-time.

    Show us some lightning fast 2D stuff!

    Posted by Jeremy, 28/07/2008 3:31am (2 years ago)

  • wow andy 2 blog posts in less then a week. You must be bored!. Why dont you just use the java DrawingCanvas class? That has everything you would ever need in there!!!!! :P

    Posted by Will, 28/07/2008 3:16am (2 years ago)

  • yeah use this php library called GeShi filter it has a syntax highlighting for so many languages!

    Drupal wins! lol you can just go <code language="ruby"> </code> and voila!

    Although nice work on the OpenGL can't help to tease you OpenGL is best for 3D :P But we had that discussion I want full source code that I can compile tbh

    Posted by shoaib, 28/07/2008 3:03am (2 years ago)

RSS feed for comments on this page | RSS feed for all comments