Creating a Dynamic Background in OpenGL

Background (Back in Black, where “Black” is potentially UB)

Last year, I took a new job which resulted in me going back to C++ for work. I tend to end up doing my personal projects in whatever language I use work-wise, so I ended up deciding to build a grame from scratch in C++. (For some definition of “from scratch”, because I’m currently using GLFW, GL3W, Assimp and GLM, which feels…acceptable?)

I’ve also started learning Blender, because it is incredibly gratifying to render a model I created on screen! I mostly followed Learn OpenGL to get started, though I’ll admit I sometimes feel like it’s a little “on rails” and I’m not always sure I fully udnerstand the why of what’s discussed. Nevertheless, I was able to implement a first-person maze game – I even had a little character you could find and get to follow you. However, as noted, it was implemented following Learn OpenGL, and I eventually found my own code incredibly difficult to follow. It was spaghetti of the most half-cooked order!

And what do you do when your codebase is a mess? Spend several months explaining to your manager why you simply must rewrite! But I have no manager here – and definitely nothing holding me back from choosing the worst possible option – so rewriting it brought to a vote and passed.

Now, I’m starting on what I’m going to call my First Real Game (it’s really not – my first game was a stick figure fighting game implemented in QBasic in like 1999), which is supposed to be small and quick to “finish”. The basic idea is you’re a rocket ship pilot and you have to deliver supplies to space stations floating in space. The models are 3D, but it’s basically 2D, because I really do want this to be small and quick to finish.

Why you might care (maybe)

Of course, I’m totally lost 95% of the time, but I’m learning a lot. Like, this last weekend I spent entirely too much time figuring out how to add a background. First, I want to record some of the stunningly silly mistakes I made.

Don’t forget to use your program before setting uniforms

Look, I’m very much well aware that you need to “use” your shader progam before setting uniforms…but also when you spread logic out over an entire game, it’s easy to miss this step.

First, what was the symptom?

Currently, I have an Object class, which is responsible for in-game objects (like rocketships and space stations). It loads models with Assimp and holds state information for each object (it’s ultimately going to need to be refactored, because it doesn’t handle instancing, but we’ll blow up that bridge when we come to it), and then you pass a program to it when it’s time to render. Before adding a background, everything rendered nicely and exactly where it was supposed to be. Rocket ships rotated and flew and retained momentum and space stations sat in space doing nothing (it’s still very early in development). Then, I added my Background class – it’s not an object, because it’s literally 2 triangles orthographically displayed at a z position behind everything else, with a fragment shader adding a grid of stars (again, still early in development).

But now, one (and only one) of my space stations was in entirely the wrong position. “That’s weird,” I thought to myself, and removed the background. As you might expect, everything went back to how it was supposed to look. After a bit of experimentation, I found that the order the objects were instantiated determined what was out of place!! Even more startling, if you rendered the objects, then the background, and then rendered the objects again, they were in the right place with one duplicate in the wrong position!

By now, anyone with a modicom of experience with OpenGL probably has a very clear idea of what is happening, but I was absolutely stumped, until I started messing with setting uniforms. If I set the uniform after drawing the objects, they were all in the right place. At this point, I realized I was setting one uniform before calling the draw function, so the order looked like:

- loop through objects and for each one
    - set uniform
    - use program
    - set more uniforms
    - render
- render background
- repeat above loop for object

Finally, I realized my mistake and move uniform setting so it happened in the object’s rendering function. Of course, this is terrible inefficient for cases where the uniform should be the same for all objects, so that’s something else I’m going to need to refactor. But what a face-smacking moment it was to realize what I was doing wrong.

Why did I write all this out?

Honestly, I have no idea if this will help anyone else, but I spent so much time trying to search for an explanation without much of anything really being useful. I’m taking that classic xkcd cartoon to heart, and trying to post the answer in hope it might save someone a few hours of grief.

Actually implmenting the background

So, now that my background class isn’t completely breaking everything else just by existing, I can actually make a star field. It might be worth noting (or possibly not) that I chose to use a 2D background instead of a skybox for 2 reasons: 1) since the camera and game movement is constrained to two dimensions, a skybox isn’t really necessary, and 2) I wanted to see if I could make it work!

And it did!

My simple game with a grid of stars

Obviously, you can’t see it in a screenshot, but flying the rocketship around, moves the starts in the opposite direction in the background in a kind of parallax. The implemention was relatively straightforward, but there were a few gotchas I missed. So, here’s the highlights if you want to build something similar!

First, it’s impportant to note that I’m goint to use orthographic rendering instead of perspective. This is important because most things with OpenGL and GLM are in perspective (at least in my limited experience) and usually break the screen into coordinates from -1 to 1 in the x and y axes. But for orthographic, it’s actually (0, 0) to whatever width/height you set, such as (1920, 980). So, for example, if you render triangles with coordinates like this:

         1.f,  1.0f, 0.1f, // top right
         1.f, -1.0f, 0.1f, // bottom right
        -1.f,  1.0f, 0.1f, // top left
         1.f, -1.0f, 0.1f, // bottom right
        -1.f, -1.0f, 0.1f, // bottom left
        -1.f,  1.0f, 0.1f, // top left

It’s actually going to be a tiny square! So, you actually need to use something like this:

           0.0f,   0.0f, 0.1f,
        1920.0f,   0.0f, 0.1f,
        1920.0f, 980.0f, 0.1f,
        1920.0f, 980.0f, 0.1f,
           0.0f, 980.0f, 0.1f,
           0.0f,   0.0f, 0.1f

And then you can use that with a program where the vertex shader looks like this:

#version 330 core
layout (location = 0) in vec3 aPos;
out vec2 pos;

// The ortho matrix is generated with GLM ortho like:
// glm::ortho(0.0f, 1920.f, 0.0f, 980.0f, -100.f, 100.f)
uniform mat4 ortho;

void main() {
    gl_Position = ortho * vec4(aPos.xyz, 1.);
    pos = aPos.xy;
}

And then the fragment shader can look like this:

#version 330 core

in vec2 pos;

out vec4 FragColor;

// Offset is just the camera position, since the camera is constrained to two dimensions.
uniform vec3 offset;

void main() {
    // This adds a star every 50 pixels, but "moves" the stars based on the movement of
    // the camera, which is also tied to the "location" of the rocketship.
    if (int(pos.x - (offset.x * 20)) % 50 == 0 && int(pos.y + (offset.y * 20)) % 50 == 0) {
        FragColor = vec4(1.0, 1.0, 1.0f, .1f);
    } else {
        FragColor = vec4(0.0f);
    }
}

This is basically a “first draft” – the next step is to use some psuedo random number generation to spread stars out more “randomly” to make it a little more “natural” looking.

Wrapping up

This is all for today, but if you see any errors or just want to yell at me for some reason, you can find me on Mastadon!

Back to home