Learning Shaders by Example

This post is meant for developers who are very new to WebGL and would like to jump right into building things.

(Inspired by @Izumenko's Infinite loop)

There are many options for building visual projects in the browser, and they each have their own benefits and drawbacks. CSS can be easy to learn but can animate slowly in complex cases and appear inconsistent across browsers. SVG can be a step up, but it's still not going to be fast enough for 3D graphics. Canvas 2D is very powerful but it's just not great for large amounts of individual pixel manipulation. These technologies have been very widely adopted and amazing things have been created with them, but the performance boundaries are often an issue. Now that WebGL is very commonly available, a lot of rendering power is ready for use. It seems that this technology hasn't been used by the community as much as these other visualization methods, and the reason for this may be the steep learning curve.

Please review the code in the HTML section of each sample project below. These are best studied in sequence, as they build on each other and become more complicated. This article will not attempt to explain the inner workings or design principles of WebGL, but there are several excellent resources for that listed below. Readers who have questions about the code should first search through this WebGL Reference Card, it'll explain things like varying, uniform and vec4.

A Visual Demo

By combining the individual techniques described in this post, it's possible to create animations that are visually appealing while also achieving very good performance. The sample below is inspired by @darrylhuffman's Spilled Paint project.

Basic Start

Below is a pen with the boilerplate code that will be the base of all examples on this page. The JS section contains the setup code that will hardly change; the HTML section has the shader scripts where most of the logic lies. Mouse over the canvas to change its color.

The shader code will be used by Three.js to draw the surface of a rectangle that stretches to fit its container. Variables like screen size, mouse position and time are passed into the shaders using the uniform qualifier.

Drawing a Line

Here's a sample of how things are done with a fragment shader - a simple horizontal line. Take a look at the HTML section to see the shader's code.

It isn't the same as 2D canvas drawing, where it's possible to simply draw a line from point A to point B. With fragment shaders, the code is responsible for just assigning a color to the single pixel on which it is currently executing. The fragment shader function must discover whether the line exists on top of current pixel.

Repeating Tiles

Repeating a pattern or animation in a grid formation is also a useful and versatile technique. Doing this is very simple and unlocks a lot of potential for animation.

Using Colors

The hsb2rgb function used in the example below is a very easy way to specify colors in the HSB color space. The function accepts a single vec3 argument which has the component values as numbers between 0.0 and 1.0; it also returns a vec3 value, representing the color in the RGB color space.

Plotting a Wave

Another basic and very common building block of animations is repeating cycles, which makes the sine function very useful. Below is an example of how the sin function can be used to draw a sine wave. The input will be a range between 0.0 and , while the output is a value between -1.0 and 1.0. It may be preferable to scale this domain to a range between 0.0 and 1.0.

Adding Time

The JavaScript code is using Three.js to send information about elapsed time to the shader, which can be used freely by both vertex and fragment shaders. The time variable is a floating scalar called uTime. Animating the plotted sine wave from the previous example is very simple -

Making it Move

Here is an example showing the previous principles brought together. It also introduces a 'camera' which is actually a simple illusion: the grid is moving around, not the viewport.

Needs Noise

Along with colors, repetition, and movement, noise can be a very powerful tool for building animations and textures. Thankfully, these functions are available to copy from other sources. Using multiple noise sources will lead to more visually appealing results.

Wrapping it Up

This final demonstration shows a 3D object which has shaders applied to it, a very common use case. This example requires the communication between the vertex and fragment shaders.

WebGL shaders provide incredible performance speeds but can be tricky to get into, especially since debugging is so difficult. With practice of these fundamental techniques, it's easy to quickly build very neat animations.

See Also


2,675 2 35