Then as I mentioned in the last post I've been messing around with OpenGL shaders. To be honest I know next to aught near shaders or OpenGL only it'due south fun to accept a await at these things to try and gain some intuition about how they work. Hopefully past doing so I'll be able to build circuitous and interesting shaders of my own, but I'm getting ahead of myself.

And then let's kickoff with something unproblematic, a shader of concentric circles. It's based upon a somewhat more fascinating shader, simply there's more than enough to talk nearly with just this simple case.

It turns out that the shader code to describe an animated set of concentric colourful circles requires just 2 lines. Well that'south not quite true you could write information technology ane line if you wanted. But that'due south also not quite true, there are a lot of lines of WebGL to go the two lines below to do something in the first place. But in one case those lines of lawmaking are done the flake that makes only the drawing is effectively ane formula that can exist compiled on the GPU.

              void mainImage(out vec4 fragColor, in vec2 fragCoord) {      vec2 u = vec2(fragCoord/iResolution.xy-.v) * vec2(1.,iResolution.y/iResolution.x);   fragColor = vec4(fract(length(u)*4. + vec3(0.,ane.,two.)/iii. + iTime/16.),1.); }            

Yous'll find that there aren't any loops in the code and that'southward because what we're writing is conceptually the centre of a loop managed by WebGL that will get executed once for every pixel we want to shade.

As well Shadey

I should explicate a little bit first. In OpenGL there are a few types of shader that form part of the rendering pipeline. Simply, for the purposes of this series of posts the OpenGL pipeline is a black box and we're only really interested in the fragment shader that can exist synthetic in such a way as to supply window input co-ordinates and produce an associated colour for that co-ordinate.

According Systems

A WebGL fragment shader receives pixel co-ordinate every bit natural numbers where the origin (0,0) is at the lesser left of the canvas. Therefore the first thing many shaders exercise is to convert those co-ordinates into a new set where the origin is in the centre of the screen and the range of x and y is some set of values that makes the subsequent maths a bit simpler.

How they do this varies, and is not super interesting, just being able to extract the range from the domain of input values is often important for making sense of what happens next. A lot of OpenGL math involves vector and matrix math. For this example it's not as well important to understand this more than securely than a vec2 is a pair of x,y points.

              vec2 u = vec2(fragCoord/iResolution.xy-.5) * vec2(1.,iResolution.y/iResolution.x);            

I loved my Atari ST information technology was the third personal computer I owned (preceded by a ZX81 and a ZX Spectrum) and it had a whopping 320×200 resolution with a mighty 512 colours. All on the same screen at the same fourth dimension! Astonishing. And then if our device has that resolution then we merely calculate the range of y past substituting in the display resolution.

\tfrac{0}{200}- \tfrac{i}{two} * \tfrac{200}{320} \leq y \leq \tfrac{200}{200} - \tfrac{1}{2} * \tfrac{200}{320}

When you simplify you get the new range of ten,y values which reveal that the range of values and that the pixels accept been corrected for the aspect ratio to keep them foursquare.

-\tfrac{1}{2} \leq x \leq \tfrac{1}{2},  -\tfrac{v}{16}\leq y \leq \tfrac{5}{16}

Unremarkably, it's not magic

The last part, and where the magic actually happens, is in the post-obit lawmaking segment. The function that provides the 'circularity' is the telephone call to length this is because this tells us how far our indicate in the airplane is from the origin (which we placed in the centre). Length is the aforementioned as the linear algebra vector performance 'normalise' (or norm), that in two dimensions is also just the sometime high schoolhouse favourite Pythagoras.

              fragColor = vec4(fract(length(u)*4. + vec3(0.,1.,2.)/3. + iTime/16.),1.);            

If we imagine a slice through our circles along the line y=0 so length(u) is equal to 10 since clearly:

length(x,0)=\sqrt{ten^2+0^2}=ten

By removing a dimension we tin can now reason nigh what the balance of the adding must do. Since our length will be ten then when we scale information technology past the constant four we volition end upward with x in the range of between -ii and two. We then add this to the vector:

\brainstorm{pmatrix}\tfrac{0}{3} & \tfrac{1}{3} & \tfrac{2}{3}\end{pmatrix}

Information technology is this footstep that essentially gives the circles their colour alter. We at present accept three different ranges for the dissimilar colour channels and and then we will end up with the post-obit:

\brainstorm{matrix}-2 \leq r \leq ii \\ -ane\tfrac{2}{3} \leq g \leq 2\tfrac{1}{3} \\ -1\tfrac{one}{3} \leq b \leq 2\tfrac{ii}{three}\end{matrix}

Withal OpenGL only shows colours in the range of nada to one (if the value is greater than one it is capped at ane) and this is the terminal step. We take the fract in social club to give us only the fractional part of the calculation so our values for each channel volition range between zero and one. This gives us a blazon of oscillation where each color channel will ascension steadily towards full saturation but then drop sharply drib to goose egg afterwards. The primal is that each aqueduct will do this on a slightly different menstruation and this is what gives us the candy type colours. In gild to visualise this a bit more than clearly I've made a plot showing how the 3 channels vary when y=0.

Time to wrap it upwards

This has been a long post, but we're not quite done. The part of the formula that gives the circles their animation is the addition of iTime/xvi. Since iTime is the amount of seconds since the shader began this volition have the effect of gently pushing each peak of the RGB aqueduct toward the centre on every frame. I should also point out that it significantly changes the ranges of the colour channels (RGB) prior to the call to fract only because we call fract information technology makes no difference to the final output range of those channels of between 0 and 1.

This has been a pretty long post. At that place were a lot of bones things to explain earlier actually getting to the point. Subsequent posts will be a lot shorter I hope so that we tin can focus on the interesting stuff!