The other day, I thought it would be fun to play with a little OpenGL cube-drawing code.
The idea came up after finishing the “Getting Started” section on Learn OpenGL (great tutorial, btw). You start with some super simple triangle stuff and build up to a few cubes spinning around. Oh, and a first person camera to check out your handy work. Basic, but a great start.
Before the tutorial, I had worked through part of the big red book to do some 2d tilemap stuff (instancing is great). However, this was my first step into the 3rd dimension.
Let’s talk about how I draw the cube
I already knew that there was a much better way to draw cubes than what Learn OpenGL presents. Instead of using 36 vertices (2 triangles per face * 3 vertices per triangle) I got it down to 24 using a simple, and not optimal, triangle strip.
Dammit, I wanted to do better than that. Naturally, I asked Google what the best way to draw a triangle was, and found this post. Nice.
You may have noticed the link to a paper on the subject. Now I have all the tools to draw the perfect cube! Kinda.
A few minutes later, I cut down my vertex buffer to only 14 vertices! But, could I go further?
Like an Obsessed Optimization Loon…
I wanted more. Martin, a friend of mine, is particularly good at figuring out interesting ways to draw stuff with computers. I went to him after figuring out that if I used the optimal number of vertices for a cube, I would need to satisfy this table:
id x y z -------------------- 0 -1 -1 -1 1 1 -1 -1 2 -1 1 -1 3 1 1 -1 4 1 1 1 5 1 -1 -1 6 1 -1 1 7 -1 -1 -1 8 -1 -1 1 9 -1 1 -1 10 -1 1 1 11 1 1 1 12 -1 -1 1 13 1 -1 1
These are the values I would need to calculate if I wanted to only pass in the center point and dimensions of the cube. This is where my brain started to melt.
Can you see the patterns? Well, Martin could. Like a scene from A Beautiful Mind, he worked out a crazy formula that would figure out the location of all 14 vertices.
This was exactly the sort of thing I was trying to figure out, but I would have never gotten far enough to be dangerous. Here is the crux of the shader code:
float3((int3(id | id2, (id1 ^ id2) ^ (id & id2), ~id & id2) & 1) ^ (vertex_id < 7 ? 0 : int3(1, 0, 1))) * 2.0f - 1.0f
I don’t know about you, but I’m digging it
I’m guessing at least half of you are yelling at me through your monitor. “It’s just a cube, moron! You don’t need these ‘premature optimizations‘ for such a simple task!” (by the way, here’s Knuth’s entire quote)
Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.
Eff that. This optimization was fun and I learned a shitload from two brilliant friends, brain-melting trial and error, and hacking away at my OpenGL code. In fact, this isn’t the only approach to making this cube as slick as possible. Jeroen has a few things to say about Karnaugh Maps and how programmers could benefit from bringing them over into the software world rather than leaving them for the electronics crew.
Don’t settle with doing things the boring way. Optimize your ass off if you enjoy it. That’s what I do: enjoy myself.