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
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.
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
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 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.
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
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
2π, while the output is a value between
1.0. It may be preferable to scale this domain to a range between
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.
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.
WebGL Reference Card - Extremely handy, explains things like
uniformand helps in figuring out what to search for
The Book of Shaders - Glossary - An excellent API reference with visual samples
Aerotwist - An Introduction to Shaders - Great example of how to actually do shading in a shader, with lights
Terrain from Noise - Anything can be accomplished by using noise and this article is the best place for beginners to start
Equations for Organic Motion - A list of equations useful for motion and texture generation
GLSL Noise Algorithms - A collection of noise functions ready to be pasted into a GLSL program
Vertex displacement with a noise function using GLSL and three.js - A great intro to vertex shading.