OpenGL 2D Independent Resolution Rendering

Around two years ago I made a tutorial for XNA in which you could render 2D games scaled to the current window resolution with proper letter-boxes or pillar-boxes.

As many know since then I moved to C++ and OpenGL, and ocasionally people ask me “Can you still do that independent resolution thing?”, and yes it’s perfectly possible. I’ve used this on all latest Windows, Mac and iOS, in case you are wondering.

The code is quite straight forward actually. In case you are not familiar with what we are trying to achieve here I recommend my other tutorial first, where I explain this is more detail.

So first we need to set our viewport with proper letterbox or pillar box, if required.

 
// Let's start by clearing the whole screen with black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);	
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
// Both these values must be your real window size, so of course these values can't be static
int screen_width = 1024;  
int screen_height = 728; 
 
// This is your target virtual resolution for the game, the size you built your game to
int virtual_width=1280;
int virtual_height=720;
 
float targetAspectRatio = virtual_width/virtual_height;
 
// figure out the largest area that fits in this resolution at the desired aspect ratio
int width = screen_width ;
int height = (int)(width / targetAspectRatio + 0.5f);
 
if (height > screen_height )
{
   //It doesn't fit our height, we must switch to pillarbox then
    height = screen_height ;
    width = (int)(height * targetAspectRatio + 0.5f);
}
 
// set up the new viewport centered in the backbuffer
int vp_x = (screen_width  / 2) - (width / 2);
int vp_y = (screen_height / 2) - (height/ 2);
 
glViewport(vp_x,vp_y,width,height);

Now that our viewport is set we should set our 2d perspective

 
// Now we use glOrtho
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
// This function is for Mac and Windows only, if you are using
// iOS you should use glOrthof instead
glOrtho(0, screen_width, screen_height, 0, -1, 1);
/*if on iOS*/ //glOrthof(0, screen_width, screen_height, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();

So now we should push the transformations before actually drawing anything

// Push in scale transformations
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
 
//Now to calculate the scale considering the screen size and virtual size
float scale_x = (float)((float)(screen_width)) / (float)virtual_width);
float scale_y = (float)((float)(screen_height) / (float)virtual_height);
glScalef(scale_x, scale_y, 1.0f);

We can now proceed to drawing everything we want, that’s is really up to you now.

// Place your sprites drawing code here
// Example
glBegin(GL_TRIANGLES);
glColor3f(0.1, 0.2, 0.3);
glVertex3f(0,  0, 0);
glVertex3f(50, 0, 0);
glVertex3f(0, 50, 0);
glEnd();

I really don’t recommend using glBegin() and glEnd(), this was just for simplicity, you should use glDrawElements or glDrawArrays

After you finish you drawing code we can proceed to the rest

// This pops those matrices for the scale transformations.
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPopMatrix();

//Now to finish we should end our 2D perspective

glMatrixMode(GL_PROJECTION);
glPopMatrix();   
glMatrixMode(GL_MODELVIEW);
glPopMatrix();

And that’s pretty much it. I have this code on my games and it works fine, at least for what I usually need. Feel free to tweak it around for your needs. Hope this helps to get a picture on how to achieve this effect. Let me know if you find any bug.

Here’s an example of what you might achieve with this

Base_1280x720_Res_1280x800Base_1280x720_Res_800x600Base_1024x768_Res_800x600Base_1280x720_Res_480x640Base_1024x768_Res_1280x720

6 Comments

  1. Are you sure it is:
    glOrtho(0, screen_width, screen_height, 0, -1, 1);

    Not:
    glOrtho(0, screen_width, 0, screen_height, -1, 1);

    Juris.

  2. Hey!

    Just writing to say thanks for this post. I have been able to make it work for me but with a little tweak.
    I have looked at games like StarCraft, and some other strategy games and it seems that they always keep the same hight for the virtual viewport and only allow you to see more map on the sides like here:

    http://www.progamingtours.net/wp-content/uploads/2013/03/sc2_fov.gif

    So I have implemented it the same way.

    I wanted to ask, how do you deal with sprite corruption after you scale matrix with glScalef()?
    Here is my image Not Scaled: http://s21.postimg.org/tq7qr8ld3/no_scaling.png
    Here is my image scaled: http://s21.postimg.org/zfnzbjrjb/scaling.png

    Sprite quality suffers after any scaling with glScaleF();

    Any tips?

    Regards. Juris.

Leave a Reply