Hello Window
Getting-started/Hello-Window
Let's see if we can get GLFW up and running. First, create a .cpp
file and add the following includes to the top of your newly created file.
#include <glad/glad.h>
#include <GLFW/glfw3.h>
GL/gl.h
) so be sure to include GLAD before other header files that require OpenGL (like GLFW).
Next, we create the
int main()
{
glfwInit ();
glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
return 0;
}
In the main function we first initialize GLFW with GLFW_
. The second argument is an integer that sets the value of our option. A list of all the possible options and its corresponding values can be found at GLFW's window handling documentation. If you try to run the application now and it gives a lot of undefined reference errors it means you didn't successfully link the GLFW library.
Since the focus of this book is on OpenGL version 3.3 we'd like to tell GLFW that 3.3 is the OpenGL version we want to use. This way GLFW can make the proper arrangements when creating the OpenGL context. This ensures that when a user does not have the proper OpenGL version GLFW fails to run. We set the major and minor version both to 3
. We also tell GLFW we want to explicitly use the core-profile. Telling GLFW we want to use the core-profile means we'll get access to a smaller subset of OpenGL features without backwards-compatible features we no longer need. Note that on Mac OS X you need to add
to your initialization code for it to work.
Next we're required to create a window object. This window object holds all the windowing data and is required by most of GLFW's other functions.
GLFWwindow* window = glfwCreateWindow (800, 600, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate ();
return -1;
}
glfwMakeContextCurrent (window);
The "LearnOpenGL"
but you're allowed to name it however you like. We can ignore the last 2 parameters. The function returns a
GLAD
In the previous chapter we mentioned that GLAD manages function pointers for OpenGL so we want to initialize GLAD before we call any OpenGL function:
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
We pass GLAD the function to load the address of the OpenGL function pointers which is OS-specific. GLFW gives us
Viewport
Before we can start rendering we have to do one last thing. We have to tell OpenGL the size of the rendering window so OpenGL knows how we want to display the data and coordinates with respect to the window. We can set those dimensions via the
glViewport (0, 0, 800, 600);
The first two parameters of
We could actually set the viewport dimensions at values smaller than GLFW's dimensions; then all the OpenGL rendering would be displayed in a smaller window and we could for example display other elements outside the OpenGL viewport.
(-0.5,0.5)
would (as its final transformation) be mapped to (200,450)
in screen coordinates. Note that processed coordinates in OpenGL are between -1 and 1 so we effectively map from the range (-1 to 1) to (0, 800) and (0, 600).
However, the moment a user resizes the window the viewport should be adjusted as well. We can register a callback function on the window that gets called each time the window is resized. This resize callback function has the following prototype:
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
The framebuffer size function takes a
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport (0, 0, width, height);
}
We do have to tell GLFW we want to call this function on every window resize by registering it:
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
When the window is first displayed
There are many callbacks functions we can set to register our own functions. For example, we can make a callback function to process joystick input changes, process error messages etc. We register the callback functions after we've created the window and before the render loop is initiated.
Ready your engines
We don't want the application to draw a single image and then immediately quit and close the window. We want the application to keep drawing images and handling user input until the program has been explicitly told to stop. For this reason we have to create a while loop, that we now call the
while(!glfwWindowShouldClose (window))
{
glfwSwapBuffers (window);
glfwPollEvents ();
}
The true
and the render loop stops running, after which we can close the application.
The
When an application draws in a single buffer the resulting image may display flickering issues. This is because the resulting output image is not drawn in an instant, but drawn pixel by pixel and usually from left to right and top to bottom. Because this image is not displayed at an instant to the user while still being rendered to, the result may contain artifacts. To circumvent these issues, windowing applications apply a double buffer for rendering. The front buffer contains the final output image that is shown at the screen, while all the rendering commands draw to the back buffer. As soon as all the rendering commands are finished we swap the back buffer to the front buffer so the image can be displayed without still being rendered to, removing all the aforementioned artifacts.
One last thing
As soon as we exit the render loop we would like to properly clean/delete all of GLFW's resources that were allocated. We can do this via the
glfwTerminate ();
return 0;
This will clean up all the resources and properly exit the application. Now try to compile your application and if everything went well you should see the following output:
If it's a very dull and boring black image, you did things right! If you didn't get the right image or you're confused as to how everything fits together, check the full source code here (and if it started flashing different colors, keep reading).
If you have issues compiling the application, first make sure all your linker options are set correctly and that you properly included the right directories in your IDE (as explained in the previous chapter). Also make sure your code is correct; you can verify it by comparing it with the full source code.
Input
We also want to have some form of input control in GLFW and we can achieve this with several of GLFW's input functions. We'll be using GLFW's
void processInput(GLFWwindow *window)
{
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
Here we check whether the user has pressed the escape key (if it's not pressed, true
using while
loop will then fail and the application closes.
We then call
while (!glfwWindowShouldClose (window))
{
processInput(window);
glfwSwapBuffers (window);
glfwPollEvents ();
}
This gives us an easy way to check for specific key presses and react accordingly every
Rendering
We want to place all the rendering commands in the render loop, since we want to execute all the rendering commands each iteration or frame of the loop. This would look a bit like this:
// render loop
while(!glfwWindowShouldClose (window))
{
// input
processInput(window);
// rendering commands here
...
// check and call events and swap the buffers
glfwPollEvents ();
glfwSwapBuffers (window);
}
Just to test if things actually work we want to clear the screen with a color of our choice. At the start of frame we want to clear the screen. Otherwise we would still see the results from the previous frame (this could be the effect you're looking for, but usually you don't). We can clear the screen's color buffer using
glClear Color (0.2f, 0.3f, 0.3f, 1.0f);
glClear (GL_COLOR_BUFFER_BIT);
Note that we also specify the color to clear the screen with using
The full source code of the application can be found here.
So right now we got everything ready to fill the render loop with lots of rendering calls, but that's for the next chapter. I think we've been rambling long enough here.