The idiots guide to getting your Hello World OpenGL program working on MacOS in the year 2021

So I have some time off work and thought it would be fun to learn OpenGL and modern C++. I haven't touched C++ in a long time, and OpenGL has some complexity to get even a basic window up. As always figuring out how to install your dependencies and get the compiler to link to the right things is half the battle.

Many of the tutorials I found on the web had more stuff then you needed to just get started, and assumed you wanted to use a specific IDE. On the other hand other tutorials just gave you some code snippets and told you to figure out how to make the compiler work on your environment. 

So I present to you my setp-by-step guide on how to get a minimal OpenGL program compiled and running on MacOS in the year 2021. You're welcome.

Before we begin: Dependencies

OpenGL is complex, and there are a few dependencies we will need to get our program up and running.

  1. Brew. We will use Brew to install the libraries we need. I will assume you already have Brew installed on your machine.
  2. A C++ compiler. MacOS comes with Clang preinstalled. We will use that.
  3. pkg-config. This is a super useful tool that basically lets you generate compiler options for Clang so we can tell it how to link to out libs.
  4. OpenGL. This comes pre installed on MacOS as a "framework". We will see how to tell Clang about his in a later section.
  5. GLFW. A simple API for creating windows, OpenGL contexts, and receiving input events.
  6. GLEW The OpenGL Extension Wrangler Library. Think of this as some magic glue that connects your code with the vendors OpenGL implementation at runtime. Without this library using OpenGL would be extremely tedious.
We will come back to how to install all this in a minute.

The Code

I have hacked down to what I think is about the minimal viable OpenGL program. I won't explain how it all works since this tutorial is more about how to get it compiled and running, but it shouldn't be too hard to figure out the gist of it. Copy this into a file named hello.cpp.

// My first OpenGL program.

#include <iostream>

#include <GL/glew.h>
#include <GLFW/glfw3.h>


using namespace std;


// Boilerplate to set up a window
GLFWwindow* setUpWindow() {
GLFWwindow* window;

// Initialise GLFW
if( !glfwInit() )
{
cerr << "Failed to initialize GLFW\n";
return NULL;
}

glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // For some reason MacOS needs this.
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

// Open a window and create its OpenGL context
window = glfwCreateWindow( 1024, 768, "OpenGL 01", NULL, NULL);

if( window == NULL ){
cerr << "Failed to create GLFW window.\n";
glfwTerminate();
return NULL;
}

glfwMakeContextCurrent(window);

// Initialize GLEW
glewExperimental = GL_TRUE;
if (glewInit() != GLEW_OK) {
cerr << "Failed to initialize GLEW\n";
glfwTerminate();
return NULL;
}

return window;
}

// Main rendering loop
int main() {
GLFWwindow* window = setUpWindow();

if (window == NULL) {
return -1;
}

// Green background
glClearColor(0.0f, 0.4f, 0.0f, 0.0f);

do{
// Clear the screen.
glClear( GL_COLOR_BUFFER_BIT );

// Swap buffers
glfwSwapBuffers(window);
glfwPollEvents();

} // Check if the window was closed
while( !glfwWindowShouldClose(window) );

// Close OpenGL window and terminate GLFW
glfwTerminate();

return 0;
}


Compiling it

Ok now the bit that is always the hardest. Getting the damn thing to compile! Here are the steps I followed.

  1. First we need to install our dependencies.
    • brew install glfw
    • brew install glew
  2. Next we need to install pkg-config
    1. brew install pkg-config
  3. Ok, let's try compiling. We are compiling C++ so we need to call clang++, not clang Naively you might try this: 
    • clang++ hello.cpp
  4. You will find that throws a lot of errors because we didn't tell Clang about all the stuff it needs. First let's tell it about the OpenGL framework
    • clang++ -framework OpenGL hello.cpp
  5. Now we get different errors. Progress! Next we need to tell Clang how to link to our libs. This is where  pkg-config comes in. If we ask pkg-config nicely it will generate the arguments we need to send Clang so it can link to our libs. Run the following command:
    •  pkg-config glew glfw3 --libs 
  6. The output you see from this command is what we need to tell Clang so it can link to the libs we need. So let's throw it all together:
    • clang++ `pkg-config glew glfw3 --libs` -framework OpenGL hello.cpp

  7. If all goes well it should compile without error. You should find a new binary file a.out has been created. Run it with the command ./a.out and you should see your new OpenGL program running. Nifty!

Final thoughts

Obviously you have a lot more work to do before you have a useful OpenGL program. Just getting the environment set up is often the most frustrating part, so hopefully you found this useful and can get on with coding! You probably want to set up  cmake or something, this is left as an exercise for the reader.
 
There are lots of options for IDEs. Personally I gave VSCode a go. It was very easy to set up, I just had to point it to Clang as my compiler of choice. It found the installed libs without me needing to configure anything. It provides syntax highlighting, squiggles, and autocomplete. Unfortunately it doesn't have any refactoring for C++. You might want to try CLion or Eclipse for that.























Comments