I don’t like writing boilerplate code.
A while back, I started dragging my game engine kicking and screaming into the world of OpenGL ES 2. The good news is this lets me write shaders. The bad news is I need to write boilerplate code every time I write a shader — you know the kind — scaffolding code to load the shader, code to bind the attributes and uniforms and provide some sort of constant, enum or handle to refer to them, etc.
So of course this is a terrific opportunity to write some code to write my code for me.
I’ve blogged on Ragel and its usefulness for parsing things like shaders before. I don’t know what happened to that post, though. It seems to have disappeared in the transition from Tumblr to my new site. Oh well. It was a bad post anyway.
I won’t bore you with all the details, but I wrote a little tool called
shaderize which takes a folder full of vertex and fragment shaders (hereafter referred to as the shader directory) along with an output folder containing templates (hereafter referred to as the output directory), parses the shaders and applies the data to the templates producing… code I did not have to write! Hooray!
I used Ruby to write
shaderize, so I packaged it up as a gem. All you need to do is:
You’ll probably need to prefix that with
sudo unless you’re using Ruby Version Manager. Note: OS X comes with Ruby installed out-of-the-box.
The templates are written in mustache, a wonderfully simple little templating language. Seriously, it will take you about 2 minutes to learn. Your code templates can have anything in them, but mine look something like:
You can see the files have a double extension. Shaderize expects the templates to end in
.tpl and will produce files in the same directory with the same name sans the
You can run
shaderize from the command line like so:
I have a shell build phase that does this for me. Shaderize is smart enough to only update the scaffolded files if something has changed that requires it to do so.
And that’s it. Now when I hit build,
shaderize will take all my shaders and produce wrapper classes for me, including members for the attributes and uniforms, along with a method that loads them all. This means I can write a shader, and use it without writing a single line of boilerplate code to manage it.
Of course there is a lot more code hiding behind
ShaderUniform, but that’s just vanilla C++ code and left as an exercise to the reader.
I do actually have a C version of my shader parser that I use to extract the uniforms and attributes at load time so I can set those up with the appropriate OpenGL handles and so on. As a result I have no enums, constants or
#define statements littering my code. That’s really a more complicated subject and could be done with
shaderize too — I just haven’t got around to it yet.
I’m really happy with my shader workflow now. Code for using them is very clean. There’s a lot more that could be done, including smart shader generation, but I’ve done enough yak shaving for now.
You can get the Ruby source for
shaderize on my GitHub page.
The code is released under the MIT license.