Home Blog



2D OpenGL Tutorial, Part 1

First off, you'll see that I failed to be creative with a title for an OpenGL tutorial, but I guess I already failed with the title of the blog itself. Also, I decided I don't really like it. Anyway, on to the contents of this post.

Essentially what I'm going to do with this series is making a simple tutorial on 2D OpenGL graphics. It'll be multi-part, and probably differently pased then most other OpenGL tutorials. It's going to be intended for C++ on Linux, with VSCode. I know, very specific combination but you don't neccesarily need exactly this setup. If you know how to do C++ debugging with something besides VSCode, then use something else. If you know how to setup OpenGL on Windows, which I for the life of me couldn't figure out then feel free to use Windows. Otherwise, just use WSL.

Okay, here's a general outline of the tutorial (subject to change (for sure (like really this is not going to be the final outline))):

The first thing we need to do is install OpenGL. The first commands we'll run will be to install dependencies:

sudo apt-get update
sudo apt-get install cmake pkg-config build-essential g++ gdb
sudo apt-get install libglew-dev libglfw3-dev libglm-dev
sudo apt-get install libao-dev libmpg123-dev



NOTE: If you're using WSL, you don't need to install VSCode on Linux, just on Windows. Just make sure you install the WSL extension and it will take you through all the neccesary steps.

Now head over to code.visualstudio.com and download VSCode if you choose to use it. Go through the installation process, and then make a new directory for these tutorials. CD into that directory
(cd [directory-path]) and then type code . to open VSCode in the current directory, or alternatively open your editor of choice and then open the directory you created.

For VSCode install the C/C++ extension, or whatever you need for C++ dev on your editor. Now, if everything's installed correctly (which it should have because I'm following this tutorial along on a fresh WSL install) then you should have a working C++ editor! You can start by making a main.cpp and putting whatever program you want in there, just to test if everything's working. For example:

#include <iostream>

int main() {
   std::cout << "Hello World!";
   return 0;
}

Now for VSCode, hit the run and debug button in the top right corner and choose g++ build and debug active file. If you're lucky, a .vscode folder should be generated and the file should be built and ran. If you're unlucky, there are plenty of resources online to troubleshoot.

Now let's include OpenGL. All you need to do for this is update your headers to look like this:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>

And let's also add these namespaces (I'll explain what glm is in a second).

using namespace std
using namespace glm

So we've done quite a lot to our file, and you might be wondering, "what are all these things we've included?" Here are each of them:

Now we need to make sure these get linked when our program is compiled. If you're not using VSCode you'll have to figure this out yourself, or just use the command line to compile (you won't get debugging unless you know gcc).

Inside .vscode/tasks.json find the args array and add three new items. -lGL, -lglfw, and -lGLFW. So your final array should look something like this (sorry the styling for that snippet looks garbage, I made the converter script only for C++):

"args": [
   "-fdiagnostics-color=always",
   "-g",
   "${file}",
   "-o",
   "${fileDirname}/${fileBasenameNoExtension}",
   "-lGL",
   "-lglfw",
   "-lGLEW"
],

Great, now we can finally get to programming OpenGL. First let's create our OpenGL context (this is the first step towards creating a window):

// Create OpenGL context.
if (!glfwInit()) {
   printf("ERROR: Failed to initialize GLFW window.\n");
   return -1;
}

Now some flags:

glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

Now we can finally create our window.

// Make our window!
GLFWwindow* window = glfwCreateWindow(1000, 800, "OpenGL Window!", NULL, NULL);
if (window == NULL) {
   printf("ERROR: Failed to oepn GLFW window.\n");
   glfwTerminate();
   return -1;
}

You'll see there's two parameters that we set to NULL. The first one, monitor is for if we want to open our window on a seperate monitor. The second one, share is for sharing a context with another window. For example, we might want to have a popup, but have that popup share the same OpenGL context as our main window.

Now we're almost done, we just need to initialize GLEW:

// Initialize GLEW
glewExperimental = true;
if (glewInit() != GLEW_OK) {
   printf("ERROR: Failed to init GLEW.\n");
   glfwTerminate();
   return -1;
}

// Don't worry about this for now.
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Now, if you run the code, you should see a window quickly open and then disapear. This is because our program is stopping and so of course the window closes. Let's create a mainloop.

do {
   glfwSwapBuffers(window);

   // Gets all the keys pressed (including the X button in the top right!)
   glfwPollEvents();
}
while (!glfwWindowShouldClose(window));

glfwTerminate();

Amazing! We've opened a window! Hopefully all of this stuff makes sense to you so far (save for the stuff inside the mainloop, we'll get to that later).

Now, below is the full code that we've written in case you've missed anything:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>

using namespace glm;

int main() {
   // Create OpenGL context.
   if (!glfwInit()) {
      printf("ERROR: Failed to initialize GLFW window.\n");
      return -1;
   }
  
   glfwWindowHint(GLFW_SAMPLES, 4);
   glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
   glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
   glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
   glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

   // Make our window!
   GLFWwindow* window = glfwCreateWindow(1000, 800, "OpenGL Window!", NULL, NULL);
   if (window == NULL) {
      printf("ERROR: Failed to oepn GLFW window.\n");
      glfwTerminate();
      return -1;
   }

   // Makes this the window everything gets drawn to for the current thread.
   glfwMakeContextCurrent(window);

   // Initialize GLEW
   glewExperimental = true;
   if (glewInit() != GLEW_OK) {
      printf("ERROR: Failed to init GLEW.\n");
      glfwTerminate();
      return -1;
   }

   // Don't worry about this for now.
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  
   do {
      glfwSwapBuffers(window);

      // Gets all the keys pressed (including the X button in the top right!)
      glfwPollEvents();
   }
   while (!glfwWindowShouldClose(window));

   glfwTerminate();

   return 0;
}

I've also decided it would be fun to include a cool link I've found with each blog post (completely unrelated to the post of course). So, for this one I thought I'd link to a video by GeoGuessr player rainbolt: www.youtube.com/video. You'll quickly see what the game is about. At about 3:45 minutes into the video you'll see how insane it can get.

Anyway, expect a second part coming out in about a week or so from when I've written this. Again, if you've come from Google, please subscribe to my RSS feed lol. carsonetb.com/feed.xml. Then you'll see when the next part of this series comes out. In the meantime ...

Happy February 9th, 2025, 11:43 AM PDT!















































































© 2025 Carson Bates. Some people are surprised that the above animation doesn't use JS, others aren't.