Introduction

I’ve previously given you an introduction to Three.js. If you’ve not read that you might want to as it’s the foundation on which I will be building during this article.

What I want to do is discuss shaders. WebGL is brilliant, and as I’ve said before Three.js (and other libraries) do a fantastic job of abstracting away the difficulties for you. But there will be times you want to achieve a specific effect, or you will want to dig a little deeper into how that amazing stuff appeared on your screen, and shaders will almost certainly be a part of that equation. Also if you’re like me you may well want to go from the basic stuff in the last tutorial to something a little more tricky. I’ll work on the basis that you’re using Three.js, since it does a lot of the donkey work for us in terms of getting the shader going.

I’ll say up front as well that quite a lot of this will be me explaining the context for shaders, and that there will be a second part of this guide where we will get into slightly more advanced territory. The reason for this is that shaders are unusual at first sight and take a bit of explaining.

Our Two Shaders

WebGL does not offer the use of the Fixed Pipeline, which is a shorthand way of saying that it doesn’t give you any means of rendering your stuff out of the box. What it does offer, however, is the Programmable Pipeline, which is more powerful but also more difficult to understand and use. In short the Programmable Pipeline means as the programmer you take responsibility for getting the vertices and so forth rendered to the screen. Shaders are a part of this pipeline, and there are two types of them:

Vertex shaders Fragment shaders

Both of which, I’m sure you’ll agree, mean absolutely nothing by themselves. What you should know about them is that they both run entirely on your graphics card’s GPU. This means that we want to offload all that we can to them, leaving our CPU to do other work. A modern GPU is heavily optimised for the functions that shaders require so it’s great to be able to use it.

Vertex Shaders

Take a standard primitive shape, like a sphere. It’s made up of vertices, right? A vertex shader is given every single one of these vertices in turn and can mess around with them. It’s up to the vertex shader what it actually does with each one, but it has one responsibility: it must at some point set something called gl_Position , a 4D float vector, which is the final position of the vertex on screen. In and of itself that’s quite an interesting process, because we’re actually talking about getting a 3D position (a vertex with x,y,z) onto, or projected, to a 2D screen. Thankfully for us if we’re using something like Three.js we will have a shorthand way of setting the gl_Position without things getting too heavy.

Fragment Shaders

So we have our object with its vertices, and we’ve projected them to the 2D screen, but what about the colours we use? What about texturing and lighting? That’s exactly what the fragment shader is there for.

Very much like the vertex shader, the fragment shader also only has one must- do job: it must set or discard the gl_FragColor variable, another 4D float vector, which the final colour of our fragment. But what is a fragment? Think of three vertices which make a triangle. Each pixel within that triangle needs to be drawn out. A fragment is the data provided by those three vertices for the purpose of drawing each pixel in that triangle. Because of this the fragments receive interpolated values from their constituent vertices. If one vertex is coloured red, and its neighbour is blue we would see the colour values interpolate from red, through purple, to blue.

Shader Variables

When talking about variables there are three declarations you can make: Uniforms, Attributes and Varyings. When I first heard of those three I was very confused since they don’t match anything else I’d ever worked with. But here’s how you can think of them:

Uniforms are sent to both vertex shaders and fragment shaders and contain values that stay the same across the entire frame being rendered. A good example of this might be a light’s position. Attributes are values that are applied to individual vertices. Attributes are only available to the vertex shader. This could be something like each vertex having a distinct colour. Attributes have a one-to-one relationship with vertices. Varyings are variables declared in the vertex shader that we want to share with the fragment shader. To do this we make sure we declare a varying variable of the same type and name in both the vertex shader and the fragment shader. A classic use of this would be a vertex’s normal since this can be used in the lighting calculations.

In the second part of this article we’ll use all three types so you can get a feel for how they are applied for real.

Now we’ve talked about vertex shaders and fragment shaders and the types of variables they deal with, it’s now worth looking at the simplest shaders we can create.

Bonjourno World

Here, then, is the Hello World of vertex shaders: