Shaders are extremely powerful, but that doesn’t help much if you don’t know how to use them. To that end, I’m in the process of writing a comprehensive Guide to Shaders in Jax. Unfortunately, it’s not ready yet.
The fact is, I’m currently running in about a hundred different directions trying to wrap Jax up for its first official release (so far, they’ve all been beta-quality prereleases at best). In addition to the guides, I’m trying desperately to fill in the blanks regarding API documentation. I’ve also got a couple of cool new demos half-written, and tonight I added the last major feature I expect to support with the initial release: picking.
All of these things are worthy of their own posts, and I’ll add posts for them when I have time. For now, I wanted to show you very quickly how you can add your own custom shaders to a Jax project. As with the rest of Jax, it’s really quite easy!
Like most things, it all starts with a generator. Run the following command:
$ jax generate shader custom_shader
This will generate a couple of specs, which will automatically test your shader as you write it to verify that the shader can compile, both standalone and in combination with other shaders. If you add any mandatory options to your shader, you’ll want to modify the test cases to supply those options or else they’ll start failing.
In addition, it generates the shader itself. Most Jax shaders span 5 files, and they are described below:
- common.ejs — common code which is inserted into the top of both the vertex and fragment shaders. This is so you don’t have to repeat yourself (it keeps your code DRY). You’ll primarily specify uniforms and varyings here, though you could define functions and global variables as well.
- fragment.ejs — the fragment shader code.
- vertex.ejs — the vertex shader code.
- material.js — the material file. This is the interface between Jax materials and your shader; it is responsible for setting values, validating options, etc.
- manifest.yml — a descriptor for your shader. This is used exclusively by the Jax generators. As an experiment, after generating your shader, run the following command:
$ jax generate material
You’ll see your shader listed as one of the effects you can add!
You’ve got almost everything you need, now, to at least get started with your own shaders. There’s one more thing you should be aware of, however:
- As you’ll see by just taking a glance at the files listed above, you can define any uniform, attribute or varying variable as shared. This means that no matter how many shaders and effects are in use by a material, the named variable will be shared across all of them. This makes some of the more complicated combinations of shaders possible, especially on lower end hardware, without exhausing the GPU’s hardware limitations. It’s also an important caveat, though, because if you change one shared value, you’ve changed them all! Be careful, particularly when setting shared varyings.
- Each shader has its own main function, but when Jax combines shaders to produce interesting effects for complicated materials, these functions are aliased away. They can take 3 optional parameters in the fragment shader: ambient, diffuse and specular color. These are primarily modified by the lighting shaders, and you can produce interesting effects in combination with light sources by modifying these independently. Also experiment with placing your shader before or after the lighting effect.
- You’ll notice that this architecture produces a lot of repetitive assignments. Luckily, the GLSL compiler is smart enough to optimize all that away so it won’t affect the speed of the program. Just thought I’d mention that so you don’t get worried.
- Similarly to the rest of Jax, you can use the following style of comment:
//= require "path/to/file"
This will cause Jax to search for the specified file relative to the app/shaders directory. This is useful for importing code that you plan to use over many different shaders, such as Perlin noise (although Jax has Perlin noise built-in, now, too. More on that in the guides.)
That’s pretty much all I can think of for the moment; it should be enough to get you started, in any case. You can use the shader just like any other: by generating the Material you plan to use it in, and appending the shader to the material file:
$ jax generate material [my-material] [my-shader-name] --append
I’ll add tons more information, including which files you can import directly out of Jax (e.g. Perlin noise, lighting effects, etc.), to the guides just as soon as I have time. I hope this information was coherent; I’m getting pretty tired.