Merge branch 'master' of github.com:patriciogonzalezvivo/thebookofshaders

pull/9/head
Patricio Gonzalez Vivo 10 years ago
commit f6c6bd75c0

@ -10,7 +10,7 @@ This book is about the revolutionary computational technique, *fragment shaders*
Fragment shaders give you total control over the pixels rendered on the screen at a super fast speed. This is why they're used in all sort of cases, from video filters on cellphones to incredible 3D video games.
![Journey by The Game Company](journey.jpg)
![Journey by That Game Company](journey.jpg)
In the following chapters you will discover how incredibly fast and powerful this technique is and how to apply it to your professional and personal work.

@ -2,7 +2,7 @@
So far we have seen how the GPU manages large numbers of parallel threads, each one responsible for assigning the color to a fraction of the total image. Although each parallel thread is blind to the others, we need to be able to send some inputs from the CPU to all the threads. Because of the architecture of the graphic card those inputs are going to be equal (*uniform*) to all the threads and necessarily set as *read only*. In other words, each thread receives the same data which it can read but cannot change.
These inputs are call ```uniform``` and come in most of the supported types: ```float```, ```vec2```, ```vec3```, ```vec4```, ```mat2```, ```mat3```, ```mat4```, ```sampler2D``` and ```samplerCube```. Uniforms are defined with the corresponding type at the top of the shader right after assigning the default floating point permission.
These inputs are call ```uniform``` and come in most of the supported types: ```float```, ```vec2```, ```vec3```, ```vec4```, ```mat2```, ```mat3```, ```mat4```, ```sampler2D``` and ```samplerCube```. Uniforms are defined with the corresponding type at the top of the shader right after assigning the default floating point precision.
```glsl
#ifdef GL_ES
@ -28,7 +28,7 @@ Enough talking, let's see the uniforms in action. In the following code we use `
As you can see GLSL has more surprises. The GPU has hardware accelerated angle, trigonometric and exponential functions. Some of those functions are: [```sin()```](http://www.shaderific.com/glsl-functions/#sine), [```cos()```](http://www.shaderific.com/glsl-functions/#cosine), [```tan()```](http://www.shaderific.com/glsl-functions/#tangent), [```asin()```](http://www.shaderific.com/glsl-functions/#arcsine), [```acos()```](http://www.shaderific.com/glsl-functions/#arccosine), [```atan()```](http://www.shaderific.com/glsl-functions/#arctangent), [```pow()```](http://www.shaderific.com/glsl-functions/#exponentiation), [```exp()```](http://www.shaderific.com/glsl-functions/#exponentiation), [```log()```](http://www.shaderific.com/glsl-functions/#naturallogarithm), [```sqrt()```](http://www.shaderific.com/glsl-functions/#squareroot), [```abs()```](http://www.shaderific.com/glsl-functions/#absolutevalue), [```sign()```](http://www.shaderific.com/glsl-functions/#sign), [```floor()```](http://www.shaderific.com/glsl-functions/#floor), [```ceil()```](http://www.shaderific.com/glsl-functions/#ceiling), [```fract()```](http://www.shaderific.com/glsl-functions/#fractionalpart), [```mod()```](http://www.shaderific.com/glsl-functions/#modulo), [```min()```](http://www.shaderific.com/glsl-functions/#minimum), [```max()```](http://www.shaderific.com/glsl-functions/#maximum) and [```clamp()```](http://www.shaderific.com/glsl-functions/#clamp).
Now is time again to play with the above code.
Now it is time again to play with the above code.
* Slow down the frequency until the color change becomes almost imperceptible.

@ -5,7 +5,7 @@ precision mediump float;
uniform float u_time;
void main() {
gl_FragColor = vec4( abs(sin(u_time)) ,0.0,0.0,1.0);
gl_FragColor = vec4(abs(sin(u_time)),0.0,0.0,1.0);
}

@ -113,7 +113,7 @@ void draw() {
}
```
In the order for the shader to work on versions previus to 2.1, you need to add the following line at the beginning of your shader: ```#define PROCESSING_COLOR_SHADER```. So it looks like this:
In order for the shader to work on versions previous to 2.1, you need to add the following line at the beginning of your shader: ```#define PROCESSING_COLOR_SHADER```. So that it looks like this:
```glsl
#ifdef GL_ES
precision mediump float;
@ -135,7 +135,7 @@ For more information about shaders in Processing check out this [tutorial](https
### In **openFrameworks**
Everybody has a place where they feel comfortable, in my case, thats still the [openFrameworks community](http://openframeworks.cc/). This C++ framework wraps around OpenGL and other open source C++ libraries. In many ways it's very similar to Processing, but with the obvious complications of dealing with C++ compilers. In the same way as Processing, openFrameworks will search for your shaders files in the data folder, so dont forget to copy the ```.frag``` files you want to use and change the name when you load them.
Everybody has a place where they feel comfortable, in my case, thats still the [openFrameworks community](http://openframeworks.cc/). This C++ framework wraps around OpenGL and other open source C++ libraries. In many ways it's very similar to Processing, but with the obvious complications of dealing with C++ compilers. In the same way as Processing, openFrameworks will search for your shader files in the data folder, so dont forget to copy the ```.frag``` files you want to use and change the name when you load them.
```cpp
void ofApp::draw(){

@ -1,11 +1,11 @@
# Algorithmic drawing
## Shaping functions
This chapter could be named "Mr. Miyagi's fence lesson." Previously, we mapped the normalized position on *x* and *y* to the *red* and *green* channels. Essentially we made a function that takes a two dimensional vector (x and y) and returns a four dimensional vector (r, g, b and a). But before we go further transforming data between dimensions we need to start simpler... much simpler. That means understanding how to make one dimensional functions. The more energy and time you spend learning and mastering this, the stronger your shader karate will be.
This chapter could be named "Mr. Miyagi's fence lesson." Previously, we mapped the normalized position of *x* and *y* to the *red* and *green* channels. Essentially we made a function that takes a two dimensional vector (x and y) and returns a four dimensional vector (r, g, b and a). But before we go further transforming data between dimensions we need to start simpler... much simpler. That means understanding how to make one dimensional functions. The more energy and time you spend learning and mastering this, the stronger your shader karate will be.
![The Karate Kid (1984)](mr_miyagi.jpg)
The following code structure is going to be our fence. In it, we visualize the normalized value of the *x* coordinate (```st.x```) in two ways: one with brightness (observe the nice gradient from black to white) and the other by plotting a green line on top (in that case the *x* value is assigned directly to *y*).
The following code structure is going to be our fence. In it, we visualize the normalized value of the *x* coordinate (```st.x```) in two ways: one with brightness (observe the nice gradient from black to white) and the other by plotting a green line on top (in that case the *x* value is assigned directly to *y*). Don't focus too much on the plot function, we will go through it in more detail in a moment.
<div class="codeAndCanvas" data="linear.frag"></div>
@ -19,9 +19,9 @@ This one-to-one relationship between *x* and *y* (or the brightness) is know as
Interesting, right? On line 19 try different exponents: 20.0, 2.0, 1.0, 0.0, 0.2 and 0.02 for example. Understanding this relationship between the value and the exponent will be very helpful. Using these types of mathematical functions here and there will give you expressive control over your code, a sort of data acupuncture that let you control the flow of values.
```pow``` is a native function in GLSL, and there are many others. Most of them are accelerated at the level of the hardware, which means if they are used in the right way and with discretion they will make your code faster.
```pow``` is a native function in GLSL and there are many others. Most of them are accelerated at the level of the hardware, which means if they are used in the right way and with discretion they will make your code faster.
Replace the power function on line 19. Try other ones like: [```exp()```](http://www.shaderific.com/glsl-functions/#exponentiation), [```log()```](http://www.shaderific.com/glsl-functions/#naturallogarithm) and [```sqrt()```](http://www.shaderific.com/glsl-functions/#squareroot). Some of these functions are more interesting when you play with them using PI. For that you can see that in line 5 I have defined a macro that will replace any call to ```PI``` with the value ```3.14159265359```.
Replace the power function on line 19. Try other ones like: [```exp()```](http://www.shaderific.com/glsl-functions/#exponentiation), [```log()```](http://www.shaderific.com/glsl-functions/#naturallogarithm) and [```sqrt()```](http://www.shaderific.com/glsl-functions/#squareroot). Some of these functions are more interesting when you play with them using PI. You can see on line 5 that I have defined a macro that will replace any call to ```PI``` with the value ```3.14159265359```.
### Step and Smoothstep
@ -47,7 +47,7 @@ In the previous example, on line 12, notice that weve been using smoothstep t
When you want to use some math to animate, shape or blend values, there is nothing better than being friends with sine and cosine.
These two basic trigonometric functions that work together to construct circles are as handy as MacGyvers Swiss army knife. Its important to know how they behave and in what ways they can be combined. In a nutshell, given an angle (in radians) they will return the correct position of *x* (cosine) and *y* (sine) of a point on the edge of a circle with a radius equal to 1. But, the fact that they return normalized values (values between -1 and 1) in such a smooth way makes them an incredible tool.
These two basic trigonometric functions work together to construct circles that are as handy as MacGyvers Swiss army knife. Its important to know how they behave and in what ways they can be combined. In a nutshell, given an angle (in radians) they will return the correct position of *x* (cosine) and *y* (sine) of a point on the edge of a circle with a radius equal to 1. But, the fact that they return normalized values (values between -1 and 1) in such a smooth way makes them an incredible tool.
![](sincos.gif)
@ -63,7 +63,7 @@ Try the following exercises and notice what happens:
* Multiply *x* by ```PI``` before computing the ```sin```. Note how the two phases **shrink** so each cycle repeats every 2 integers.
* Multply time (```u_time```) by *x* before computing the ```sin```. See how the **frequency** between phases becomes more and more compressed.
* Multiply time (```u_time```) by *x* before computing the ```sin```. See how the **frequency** between phases becomes more and more compressed. Note that u_time may have already become very large, making the graph hard to read.
* Add 1.0 to ```sin(x)```. See how all the wave is **displaced** up and now all values are between 0.0 and 2.0.
@ -89,7 +89,7 @@ At the end of the last exercise we introduced some new functions. Now its tim
//y = abs(x); // return the absolute value of x
//y = clamp(x,0.0,1.0); // constrain x to lie between 0.0 and 1.0
//y = min(0.0,x); // return the lesser of x and 0.0
//y = max(1.0,x); // return the greater of x and 1.0 "></div>
//y = max(0.0,x); // return the greater of x and 0.0 "></div>
### Advance shaping functions

@ -47,7 +47,7 @@
<ul class="navigationBar" >
<li class="navigationBar" onclick="previusPage()">&lt; &lt; Previous</li>
<li class="navigationBar" onclick="homePage()"> Home </li>
<!-- <li class="navigationBar" onclick="nextPage()">Next &gt; &gt;</li> -->
<li class="navigationBar" onclick="nextPage()">Next &gt; &gt;</li>
</ul>
<footer>

@ -0,0 +1,260 @@
/*
** Copyright (c) 2012, Romain Dura romain@shazbits.com
**
** Permission to use, copy, modify, and/or distribute this software for any
** purpose with or without fee is hereby granted, provided that the above
** copyright notice and this permission notice appear in all copies.
**
** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
** WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
** SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
** WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
** ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
** IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
** Photoshop & misc math
** Blending modes, RGB/HSL/Contrast/Desaturate, levels control
**
** Romain Dura | Romz
** Blog: http://mouaif.wordpress.com
** Post: http://mouaif.wordpress.com/?p=94
*/
/*
** Desaturation
*/
vec4 Desaturate(vec3 color, float Desaturation)
{
vec3 grayXfer = vec3(0.3, 0.59, 0.11);
vec3 gray = vec3(dot(grayXfer, color));
return vec4(mix(color, gray, Desaturation), 1.0);
}
/*
** Hue, saturation, luminance
*/
vec3 RGBToHSL(vec3 color)
{
vec3 hsl; // init to 0 to avoid warnings ? (and reverse if + remove first part)
float fmin = min(min(color.r, color.g), color.b); //Min. value of RGB
float fmax = max(max(color.r, color.g), color.b); //Max. value of RGB
float delta = fmax - fmin; //Delta RGB value
hsl.z = (fmax + fmin) / 2.0; // Luminance
if (delta == 0.0) //This is a gray, no chroma...
{
hsl.x = 0.0; // Hue
hsl.y = 0.0; // Saturation
}
else //Chromatic data...
{
if (hsl.z < 0.5)
hsl.y = delta / (fmax + fmin); // Saturation
else
hsl.y = delta / (2.0 - fmax - fmin); // Saturation
float deltaR = (((fmax - color.r) / 6.0) + (delta / 2.0)) / delta;
float deltaG = (((fmax - color.g) / 6.0) + (delta / 2.0)) / delta;
float deltaB = (((fmax - color.b) / 6.0) + (delta / 2.0)) / delta;
if (color.r == fmax )
hsl.x = deltaB - deltaG; // Hue
else if (color.g == fmax)
hsl.x = (1.0 / 3.0) + deltaR - deltaB; // Hue
else if (color.b == fmax)
hsl.x = (2.0 / 3.0) + deltaG - deltaR; // Hue
if (hsl.x < 0.0)
hsl.x += 1.0; // Hue
else if (hsl.x > 1.0)
hsl.x -= 1.0; // Hue
}
return hsl;
}
float HueToRGB(float f1, float f2, float hue)
{
if (hue < 0.0)
hue += 1.0;
else if (hue > 1.0)
hue -= 1.0;
float res;
if ((6.0 * hue) < 1.0)
res = f1 + (f2 - f1) * 6.0 * hue;
else if ((2.0 * hue) < 1.0)
res = f2;
else if ((3.0 * hue) < 2.0)
res = f1 + (f2 - f1) * ((2.0 / 3.0) - hue) * 6.0;
else
res = f1;
return res;
}
vec3 HSLToRGB(vec3 hsl)
{
vec3 rgb;
if (hsl.y == 0.0)
rgb = vec3(hsl.z); // Luminance
else
{
float f2;
if (hsl.z < 0.5)
f2 = hsl.z * (1.0 + hsl.y);
else
f2 = (hsl.z + hsl.y) - (hsl.y * hsl.z);
float f1 = 2.0 * hsl.z - f2;
rgb.r = HueToRGB(f1, f2, hsl.x + (1.0/3.0));
rgb.g = HueToRGB(f1, f2, hsl.x);
rgb.b= HueToRGB(f1, f2, hsl.x - (1.0/3.0));
}
return rgb;
}
/*
** Contrast, saturation, brightness
** Code of this function is from TGM's shader pack
** http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=21057
*/
// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
vec3 ContrastSaturationBrightness(vec3 color, float brt, float sat, float con)
{
// Increase or decrease theese values to adjust r, g and b color channels seperately
const float AvgLumR = 0.5;
const float AvgLumG = 0.5;
const float AvgLumB = 0.5;
const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
vec3 brtColor = color * brt;
vec3 intensity = vec3(dot(brtColor, LumCoeff));
vec3 satColor = mix(intensity, brtColor, sat);
vec3 conColor = mix(AvgLumin, satColor, con);
return conColor;
}
/*
** Float blending modes
** Adapted from here: http://www.nathanm.com/photoshop-blending-math/
** But I modified the HardMix (wrong condition), Overlay, SoftLight, ColorDodge, ColorBurn, VividLight, PinLight (inverted layers) ones to have correct results
*/
#define BlendLinearDodgef BlendAddf
#define BlendLinearBurnf BlendSubstractf
#define BlendAddf(base, blend) min(base + blend, 1.0)
#define BlendSubstractf(base, blend) max(base + blend - 1.0, 0.0)
#define BlendLightenf(base, blend) max(blend, base)
#define BlendDarkenf(base, blend) min(blend, base)
#define BlendLinearLightf(base, blend) (blend < 0.5 ? BlendLinearBurnf(base, (2.0 * blend)) : BlendLinearDodgef(base, (2.0 * (blend - 0.5))))
#define BlendScreenf(base, blend) (1.0 - ((1.0 - base) * (1.0 - blend)))
#define BlendOverlayf(base, blend) (base < 0.5 ? (2.0 * base * blend) : (1.0 - 2.0 * (1.0 - base) * (1.0 - blend)))
#define BlendSoftLightf(base, blend) ((blend < 0.5) ? (2.0 * base * blend + base * base * (1.0 - 2.0 * blend)) : (sqrt(base) * (2.0 * blend - 1.0) + 2.0 * base * (1.0 - blend)))
#define BlendColorDodgef(base, blend) ((blend == 1.0) ? blend : min(base / (1.0 - blend), 1.0))
#define BlendColorBurnf(base, blend) ((blend == 0.0) ? blend : max((1.0 - ((1.0 - base) / blend)), 0.0))
#define BlendVividLightf(base, blend) ((blend < 0.5) ? BlendColorBurnf(base, (2.0 * blend)) : BlendColorDodgef(base, (2.0 * (blend - 0.5))))
#define BlendPinLightf(base, blend) ((blend < 0.5) ? BlendDarkenf(base, (2.0 * blend)) : BlendLightenf(base, (2.0 *(blend - 0.5))))
#define BlendHardMixf(base, blend) ((BlendVividLightf(base, blend) < 0.5) ? 0.0 : 1.0)
#define BlendReflectf(base, blend) ((blend == 1.0) ? blend : min(base * base / (1.0 - blend), 1.0))
/*
** Vector3 blending modes
*/
// Component wise blending
#define Blend(base, blend, funcf) vec3(funcf(base.r, blend.r), funcf(base.g, blend.g), funcf(base.b, blend.b))
#define BlendNormal(base, blend) (blend)
#define BlendLighten BlendLightenf
#define BlendDarken BlendDarkenf
#define BlendMultiply(base, blend) (base * blend)
#define BlendAverage(base, blend) ((base + blend) / 2.0)
#define BlendAdd(base, blend) min(base + blend, vec3(1.0))
#define BlendSubstract(base, blend) max(base + blend - vec3(1.0), vec3(0.0))
#define BlendDifference(base, blend) abs(base - blend)
#define BlendNegation(base, blend) (vec3(1.0) - abs(vec3(1.0) - base - blend))
#define BlendExclusion(base, blend) (base + blend - 2.0 * base * blend)
#define BlendScreen(base, blend) Blend(base, blend, BlendScreenf)
#define BlendOverlay(base, blend) Blend(base, blend, BlendOverlayf)
#define BlendSoftLight(base, blend) Blend(base, blend, BlendSoftLightf)
#define BlendHardLight(base, blend) BlendOverlay(blend, base)
#define BlendColorDodge(base, blend) Blend(base, blend, BlendColorDodgef)
#define BlendColorBurn(base, blend) Blend(base, blend, BlendColorBurnf)
#define BlendLinearDodge BlendAdd
#define BlendLinearBurn BlendSubstract
// Linear Light is another contrast-increasing mode
// If the blend color is darker than midgray, Linear Light darkens the image by decreasing the brightness. If the blend color is lighter than midgray, the result is a brighter image due to increased brightness.
#define BlendLinearLight(base, blend) Blend(base, blend, BlendLinearLightf)
#define BlendVividLight(base, blend) Blend(base, blend, BlendVividLightf)
#define BlendPinLight(base, blend) Blend(base, blend, BlendPinLightf)
#define BlendHardMix(base, blend) Blend(base, blend, BlendHardMixf)
#define BlendReflect(base, blend) Blend(base, blend, BlendReflectf)
#define BlendGlow(base, blend) BlendReflect(blend, base)
#define BlendPhoenix(base, blend) (min(base, blend) - max(base, blend) + vec3(1.0))
#define BlendOpacity(base, blend, F, O) (F(base, blend) * O + blend * (1.0 - O))
// Hue Blend mode creates the result color by combining the luminance and saturation of the base color with the hue of the blend color.
vec3 BlendHue(vec3 base, vec3 blend)
{
vec3 baseHSL = RGBToHSL(base);
return HSLToRGB(vec3(RGBToHSL(blend).r, baseHSL.g, baseHSL.b));
}
// Saturation Blend mode creates the result color by combining the luminance and hue of the base color with the saturation of the blend color.
vec3 BlendSaturation(vec3 base, vec3 blend)
{
vec3 baseHSL = RGBToHSL(base);
return HSLToRGB(vec3(baseHSL.r, RGBToHSL(blend).g, baseHSL.b));
}
// Color Mode keeps the brightness of the base color and applies both the hue and saturation of the blend color.
vec3 BlendColor(vec3 base, vec3 blend)
{
vec3 blendHSL = RGBToHSL(blend);
return HSLToRGB(vec3(blendHSL.r, blendHSL.g, RGBToHSL(base).b));
}
// Luminosity Blend mode creates the result color by combining the hue and saturation of the base color with the luminance of the blend color.
vec3 BlendLuminosity(vec3 base, vec3 blend)
{
vec3 baseHSL = RGBToHSL(base);
return HSLToRGB(vec3(baseHSL.r, baseHSL.g, RGBToHSL(blend).b));
}
/*
** Gamma correction
** Details: http://blog.mouaif.org/2009/01/22/photoshop-gamma-correction-shader/
*/
#define GammaCorrection(color, gamma) pow(color, 1.0 / gamma)
/*
** Levels control (input (+gamma), output)
** Details: http://blog.mouaif.org/2009/01/28/levels-control-shader/
*/
#define LevelsControlInputRange(color, minInput, maxInput) min(max(color - vec3(minInput), vec3(0.0)) / (vec3(maxInput) - vec3(minInput)), vec3(1.0))
#define LevelsControlInput(color, minInput, gamma, maxInput) GammaCorrection(LevelsControlInputRange(color, minInput, maxInput), gamma)
#define LevelsControlOutputRange(color, minOutput, maxOutput) mix(vec3(minOutput), vec3(maxOutput), color)
#define LevelsControl(color, minInput, gamma, maxInput, minOutput, maxOutput) LevelsControlOutputRange(LevelsControlInput(color, minInput, gamma, maxInput), minOutput, maxOutput)

@ -0,0 +1,142 @@
## Colors
![Paul Klee - Color Chart (1931)](klee.jpg)
We haven't much of a chance to talk about GLSL vector types. Before going further it's important to learn more about these variables and the subject of colors is a great way to find out more about them.
If you are familiar with object oriented programming paradigms you've probably noticed that we have been accessing the data inside the vectors like any regular C-like ```struct```.
```glsl
vec3 red = vec3(1.0,0.0,0.0);
red.x = 1.0;
red.y = 0.0;
red.z = 0.0;
```
Defining color using an *x*, *y* and *z* notation can be confusing and misleading, right? That's why there are other ways to access this same information, but with different names. The values of ```.x```, ```.y``` and ```.z``` can also be called ```.r```, ```.g``` and ```.b```, and ```.s```, ```.t``` and ```.p```. (```.s```, ```.t``` and ```.p``` are usually used for spatial coordinates of a texture, which we'll see in a later chapter.) You can also access the data in a vector by using the index position, ```[0]```, ```[1]``` and ```[2]```.
The following lines show all the ways to access the same data:
```glsl
vec4 vector;
vector[0] = vector.r = vector.x = vector.s;
vector[1] = vector.g = vector.y = vector.t;
vector[2] = vector.b = vector.z = vector.p;
vector[3] = vector.a = vector.w = vector.q;
```
These different ways of pointing to the variables inside a vector are just nomenclatures designed to help you write clear code. This flexibility embedded in shading language is a door for you to start thinking interchangably about color and space coordinates.
Another great feature of vector types in GLSL is that the properties can be combined in any order you want, which makes it easy to cast and mix values. This ability is call *swizzle*.
```glsl
vec3 yellow, magenta, green;
// Making Yellow
yellow.rg = vec2(1.0); // Assigning 1. to red and green channels
yellow[2] = 0.0; // Assigning 0. to blue channel
// Making Magenta
magenta = yellow.rbg; // Assign the channels with green and blue swapped
// Making Green
green.rgb = yellow.bgb; // Assign the blue channel of Yellow (0) to red and blue channels
```
#### For your toolbox
You might not be used to picking colors with numbers - it can be very counterintuitive. Lucky for you, there are a lot of smart programs that make this job easy. Find one that fits your needs and then train it to deliver colors in ```vec3``` or ```vec4``` format. For example, here are the templates I use on [Spectrum](http://www.eigenlogik.com/spectrum/mac):
```
vec3({{rn}},{{gn}},{{bn}})
vec4({{rn}},{{gn}},{{bn}},1.0)
```
### Mixing color
Now that you know how colors are defined, it's time to integrate this with our previous knowledge. In GLSL there is a very useful function, ```mix()```, that lets you mix two values in percentages. Can you guess what the percentage range is? Yes, values between 0.0 and 1.0! Which is perfect for you, after those long hours practicing your karate moves with the fence - it is time to use them!
![](mix-f.jpg)
Check the following code at line 18 and see how we are using the absolute values of a sin wave over time to mix ```colorA``` and ```colorB```.
<div class="codeAndCanvas" data="mix.frag"></div>
Show off your skills by:
* Make an expressive transition between colors. Think of a particular emotion. What color seems most representative of it? How does it appear? How does it fade away? Think of another emotion and the matching color for it. Change the beginning and ending color of the above code to match those emotions. Then animate the transition using shaping functions. Robert Penner developed a series of popular shaping functions for computer animation known as [easing functions](http://easings.net/), you can use [this example](../edit.html#06/easing.frag) as research and inspiration but the best result will come from making your own transitions.
### Playing with gradients
The ```mix``` function has more to offer. Instead of a single ```float```, we can pass a variable type that matches the two first arguments, in our case a ```vec3```. By doing that we gain control over the mixing percentages of each individual color channel, ```r```, ```g``` and ```b```.
![](mix-vec.jpg)
Take a look at the following example. Like the examples in the previous chapter, we are hooking the transition to the normalized *x* coordinate and visualizing it with a line. Right now all the channels go along the same line.
Now, uncomment line number 25 and watch what happens. Then try uncommenting lines 26 and 27. Remember that the lines visualize the amount of ```colorA``` and ```colorB``` to mix per channel.
<div class="codeAndCanvas" data="gradient.frag"></div>
You probably recognize the three shaping functions we are using on lines 25 to 27. Play with them! It's time for you to explore and show off your skills from the previous chapter and make interesting gradients. Try the following exercises:
![William Turner - The Fighting Temeraire (1838)](turner.jpg)
* Compose a gradient that resembles a William Turner sunset
* Animate a transition between a sunrise and sunset using ```u_time```.
* Can you make a rainbow using what we have learned so far?
* Use the ```step()``` function to create a colorful flag.
### HSB
We can't talk about color without speaking about color space. As you probably know there are different ways to organize color besides by red, green and blue channels.
[HSB](http://en.wikipedia.org/wiki/HSL_and_HSV) stands for Hue, Saturation and Brightness (or Value) and is a more intuitive and useful organization of colors. Take a moment to read the ```rgb2hsv()``` and ```hsv2rgb()``` functions in the following code.
By mapping the position on the x axis to the Hue and the position on the y axis to the Brightness, we obtain a nice spectrum of visible colors. This spatial distribution of color can be very handy; it's more intuitive to pick a color with HSB than with RGB.
<div class="codeAndCanvas" data="hsb.frag"></div>
### HSB in polar coordinates
HSB was originally designed to be represented in polar coordinates (based on the angle and radius) instead of cartesian coordinates (based on x and y). To map our HSB function to polar coordinates we need to obtain the angle and distance from the center of the billboard to the pixel coordinate. For that we will use the ```length()``` function and ```atan(y,x)``` (which is the GLSL version of the commonly used ```atan2(y,x)```).
When using vector and trigonometric functions, ```vec2```, ```vec3``` and ```vec4``` are treated as vectors even when they represent colors. We will start treating colors and vectors similarly, in fact you will come to find this conceptual flexibility very empowering.
**Note:** If you were wondering, there are more geometric functions besides [```length```](http://www.shaderific.com/glsl-functions/#length) like: [```distance()```](http://www.shaderific.com/glsl-functions/#distance), [```dot()```](http://www.shaderific.com/glsl-functions/#dotproduct), [```cross```](http://www.shaderific.com/glsl-functions/#crossproduct), [```normalize()```](http://www.shaderific.com/glsl-functions/#normalize), [```faceforward()```](http://www.shaderific.com/glsl-functions/#faceforward), [```reflect()```](http://www.shaderific.com/glsl-functions/#reflect) and [```refract()```](http://www.shaderific.com/glsl-functions/#refract). Also GLSL has special vector relational functions such as: [```lessThan()```](http://www.shaderific.com/glsl-functions/#lessthancomparison), [```lessThanEqual()```](http://www.shaderific.com/glsl-functions/#lessthanorequalcomparison), [```greaterThan()```](http://www.shaderific.com/glsl-functions/#greaterthancomparison) and [```greaterThanEqual()```](http://www.shaderific.com/glsl-functions/#greaterthanorequalcomparison).
Once we obtain the angle and length we need to “normalize” their values to the range between 0.0 to 1.0. On line 27, ```atan(y,x)``` will return an angle in radians between -PI and PI (-3.14 to 3.14), so we need to divide this number by ```TWO_PI``` (defined at the top of the code) to get values between -0.5 to 0.5, which by simple addition we change to the desired range of 0.0 to 1.0. The radius will return a maximum of 0.5 (because we are calculating the distance from the center of the viewport) so we need to double this range (by multiplying by two) to get a maximum of 1.0.
As you can see, our game here is all about transforming and mapping ranges to the 0.0 to 1.0 that we like.
<div class="codeAndCanvas" data="hsb-colorwheel.frag"></div>
Try the following exercises:
* Modify the polar example to get a spinning color wheel, just like the waiting mouse icon.
* Use a shaping function together with the conversion function from HSB to RGB to expand a particular hue value and shrink the rest.
![William Home Lizars - Red, blue and yellow spectra, with the solar spectrum (1834)](spectrums.jpg)
* If you look closely at the color wheel used on color pickers (see the image below), they use a different spectrum according to RYB color space. For example, the opposite color of red should be green, but in our example it is cyan. Can you find a way to fix that in order to look exactly like the following image? [Hint: this is a great moment to use shaping functions.]
![](colorwheel.png)
#### Note about functions and arguments
Before jumping to the next chapter lets stop and rewind. Go back and take look at the functions in previous examples. You will notice ```in``` before the type of the arguments. This is a [*qualifier*](http://www.shaderific.com/glsl-qualifiers/#inputqualifier) and in this case it specifies that the variable is read only. In future examples we will see that it is also possible to define arguments as ```out``` or ```inout```. This last one, ```inout```, is conceptually similar to passing an argument by reference which will give us the possibility to modify a passed variable.
```glsl
int newFunction(in vec4 aVec4, // read-only
out vec3 aVec3, // write-only
inout int aInt); // read-write
```
You may not believe it but now we have all the elements to make cool drawings. In the next chapter we will learn how to combine all our tricks to make geometric forms by *blending* the space. Yep... *blending* the space.

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

@ -0,0 +1,189 @@
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.141592653589793
#define HALF_PI 1.5707963267948966
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
// Robert Penner's easing functions in GLSL
// https://github.com/stackgl/glsl-easings
float linear(float t) {
return t;
}
float exponentialIn(float t) {
return t == 0.0 ? t : pow(2.0, 10.0 * (t - 1.0));
}
float exponentialOut(float t) {
return t == 1.0 ? t : 1.0 - pow(2.0, -10.0 * t);
}
float exponentialInOut(float t) {
return t == 0.0 || t == 1.0
? t
: t < 0.5
? +0.5 * pow(2.0, (20.0 * t) - 10.0)
: -0.5 * pow(2.0, 10.0 - (t * 20.0)) + 1.0;
}
float sineIn(float t) {
return sin((t - 1.0) * HALF_PI) + 1.0;
}
float sineOut(float t) {
return sin(t * HALF_PI);
}
float sineInOut(float t) {
return -0.5 * (cos(PI * t) - 1.0);
}
float qinticIn(float t) {
return pow(t, 5.0);
}
float qinticOut(float t) {
return 1.0 - (pow(t - 1.0, 5.0));
}
float qinticInOut(float t) {
return t < 0.5
? +16.0 * pow(t, 5.0)
: -0.5 * pow(2.0 * t - 2.0, 5.0) + 1.0;
}
float quarticIn(float t) {
return pow(t, 4.0);
}
float quarticOut(float t) {
return pow(t - 1.0, 3.0) * (1.0 - t) + 1.0;
}
float quarticInOut(float t) {
return t < 0.5
? +8.0 * pow(t, 4.0)
: -8.0 * pow(t - 1.0, 4.0) + 1.0;
}
float quadraticInOut(float t) {
float p = 2.0 * t * t;
return t < 0.5 ? p : -p + (4.0 * t) - 1.0;
}
float quadraticIn(float t) {
return t * t;
}
float quadraticOut(float t) {
return -t * (t - 2.0);
}
float cubicIn(float t) {
return t * t * t;
}
float cubicOut(float t) {
float f = t - 1.0;
return f * f * f + 1.0;
}
float cubicInOut(float t) {
return t < 0.5
? 4.0 * t * t * t
: 0.5 * pow(2.0 * t - 2.0, 3.0) + 1.0;
}
float elasticIn(float t) {
return sin(13.0 * t * HALF_PI) * pow(2.0, 10.0 * (t - 1.0));
}
float elasticOut(float t) {
return sin(-13.0 * (t + 1.0) * HALF_PI) * pow(2.0, -10.0 * t) + 1.0;
}
float elasticInOut(float t) {
return t < 0.5
? 0.5 * sin(+13.0 * HALF_PI * 2.0 * t) * pow(2.0, 10.0 * (2.0 * t - 1.0))
: 0.5 * sin(-13.0 * HALF_PI * ((2.0 * t - 1.0) + 1.0)) * pow(2.0, -10.0 * (2.0 * t - 1.0)) + 1.0;
}
float circularIn(float t) {
return 1.0 - sqrt(1.0 - t * t);
}
float circularOut(float t) {
return sqrt((2.0 - t) * t);
}
float circularInOut(float t) {
return t < 0.5
? 0.5 * (1.0 - sqrt(1.0 - 4.0 * t * t))
: 0.5 * (sqrt((3.0 - 2.0 * t) * (2.0 * t - 1.0)) + 1.0);
}
float bounceOut(float t) {
const float a = 4.0 / 11.0;
const float b = 8.0 / 11.0;
const float c = 9.0 / 10.0;
const float ca = 4356.0 / 361.0;
const float cb = 35442.0 / 1805.0;
const float cc = 16061.0 / 1805.0;
float t2 = t * t;
return t < a
? 7.5625 * t2
: t < b
? 9.075 * t2 - 9.9 * t + 3.4
: t < c
? ca * t2 - cb * t + cc
: 10.8 * t * t - 20.52 * t + 10.72;
}
float bounceIn(float t) {
return 1.0 - bounceOut(1.0 - t);
}
float bounceInOut(float t) {
return t < 0.5
? 0.5 * (1.0 - bounceOut(1.0 - t * 2.0))
: 0.5 * bounceOut(t * 2.0 - 1.0) + 0.5;
}
float backIn(float t) {
return pow(t, 3.0) - t * sin(t * PI);
}
float backOut(float t) {
float f = 1.0 - t;
return 1.0 - (pow(f, 3.0) - f * sin(f * PI));
}
float backInOut(float t) {
float f = t < 0.5
? 2.0 * t
: 1.0 - (2.0 * t - 1.0);
float g = pow(f, 3.0) - f * sin(f * PI);
return t < 0.5
? 0.5 * g
: 0.5 * (1.0 - g) + 0.5;
}
void main() {
vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);
float t = u_time*0.5;
float pct = cubicInOut( abs(fract(t)*2.0-1.) );
gl_FragColor = vec4(vec3(mix(colorA, colorB, pct)),1.0);
}

@ -0,0 +1,37 @@
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265359
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);
float plot (vec2 _st, float _pct){
return smoothstep( _pct-0.01, _pct, _st.y) -
smoothstep( _pct, _pct+0.01, _st.y);
}
void main() {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
vec3 pct = vec3(st.x);
// pct.r = smoothstep(0.0,1.0, st.x);
// pct.g = sin(st.x*PI);
// pct.b = pow(st.x,0.5);
color = mix(colorA, colorB, pct);
// Plot transition lines for each channel
color = mix(color,vec3(1.0,0.0,0.0),plot(st,pct.r));
color = mix(color,vec3(0.0,1.0,0.0),plot(st,pct.g));
color = mix(color,vec3(0.0,0.0,1.0),plot(st,pct.b));
gl_FragColor = vec4(color,1.0);
}

@ -0,0 +1,35 @@
#ifdef GL_ES
precision mediump float;
#endif
#define TWO_PI 6.28318530718
uniform vec2 u_resolution;
uniform float u_time;
// Function from Iñigo Quiles
// https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
6.0)-3.0)-1.0,
0.0,
1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb);
return c.z * mix( vec3(1.0), rgb, c.y);
}
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
// Use polar coordinates instead of cartesian
vec2 toCenter = vec2(0.5)-st;
float angle = atan(toCenter.y,toCenter.x);
float radius = length(toCenter)*2.0;
// Map the angle (-PI to PI) to the Hue (from 0 to 1)
// and the Saturation to the radius
color = hsb2rgb(vec3((angle/TWO_PI)+0.5,radius,1.0));
gl_FragColor = vec4(color,1.0);
}

@ -0,0 +1,43 @@
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
vec3 rgb2hsb( in vec3 c ){
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz),
vec4(c.gb, K.xy),
step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r),
vec4(c.r, p.yzx),
step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),
d / (q.x + e),
q.x);
}
// Function from Iñigo Quiles
// https://www.shadertoy.com/view/MsS3Wc
vec3 hsb2rgb( in vec3 c ){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
6.0)-3.0)-1.0,
0.0,
1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb);
return c.z * mix(vec3(1.0), rgb, c.y);
}
void main(){
vec2 st = gl_FragCoord.xy/u_resolution;
vec3 color = vec3(0.0);
// We map x (0.0 - 1.0) to the hue (0.0 - 1.0)
// And the y (0.0 - 1.0) to the brightness
color = hsb2rgb(vec3(st.x,1.0,st.y));
gl_FragColor = vec4(color,1.0);
}

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Color</title>
<meta name="keywords" content="glsl,color,hsb,rgb,hue,shader" />
<meta name="description" content="This is a gentle step-by-step guide through the abstract and complex universe of Fragment Shaders."/>
<!-- CodeMirror -->
<link type='text/css' rel='stylesheet' href="../src/codemirror/css/codemirror.css">
<link type='text/css' rel="stylesheet" href="../src/codemirror/addon/fold/foldgutter.css">
<link type='text/css' rel="stylesheet" href="../src/codemirror/addon/dialog/dialog.css">
<link type='text/css' rel="stylesheet" href="../src/codemirror/addon/hint/show-hint.css">
<link type='text/css' rel="stylesheet" href="../src/codemirror/theme/neo.css">
<script type="text/javascript" src="../src/codemirror.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/search/searchcursor.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/search/search.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/dialog/dialog.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/edit/matchbrackets.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/edit/closebrackets.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/comment/comment.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/wrap/hardwrap.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/fold/foldcode.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/fold/brace-fold.js"></script>
<script type="text/javascript" src="../src/codemirror/keymap/sublime.js"></script>
<script type="text/javascript" src="../src/codemirror/addon/hint/show-hint.js"></script>
<script type="text/javascript" src="../src/codemirror/mode/clike.js"></script>
<!-- Highlight -->
<link type='text/css' rel='stylesheet' href="../css/github.css">
<script type="text/javascript" src="../src/highlight.min.js"></script>
<!-- Marked -->
<script type="text/javascript" src="../src/marked.js"></script>
<!-- My stuff -->
<link type='text/css' rel='stylesheet' href="../css/style.css">
<script type="text/javascript" src="../src/glslCanvas.js"></script>
</head>
<body>
<div class="header"><p><a href="http://patriciogonzalezvivo.com/2015/thebookofshaders/">The Book of Shaders</a> by <a href="http://patriciogonzalezvivo.com">Patricio Gonzalez Vivo</a></p></div>
<hr>
<div id="content"> </div>
<hr>
<ul class="navigationBar" >
<li class="navigationBar" onclick="previusPage()">&lt; &lt; Previous</li>
<li class="navigationBar" onclick="homePage()"> Home </li>
<!-- <li class="navigationBar" onclick="nextPage()">Next &gt; &gt;</li>
--> </ul>
<footer>
<p> Copyright 2015 <a href="http://www.patriciogonzalezvivo.com" target="_blank">Patricio Gonzalez Vivo</a> </p>
</footer>
<script type="text/javascript" src="../src/main.js" defer></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-18824436-2', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

@ -0,0 +1,21 @@
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform float u_time;
vec3 colorA = vec3(0.149,0.141,0.912);
vec3 colorB = vec3(1.000,0.833,0.224);
void main() {
vec3 color = vec3(0.0);
float pct = abs(sin(u_time));
// Mix uses pct (a value from 0-1) to
// mix the two colors
color = mix(colorA, colorB, pct);
gl_FragColor = vec4(color,1.0);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

@ -17,7 +17,7 @@ This is a gentle step-by-step guide through the abstract and complex universe of
* Algorithmic drawing
* [Shaping functions](05/)
* Colors
* [Colors](06/)
* Shapes
* Matrices
* Patterns
@ -55,12 +55,16 @@ This is a gentle step-by-step guide through the abstract and complex universe of
* [How to use this book in a classroom with RaspberryPi?](http://thebookofshaders.com/appendix/index.html#01.md)
* [How to print this book?](http://thebookofshaders.com/appendix/index.html#02.md)
* [Examples](examples/)
## About the Author
[Patricio Gonzalez Vivo](http://patriciogonzalezvivo.com/) (1982, Buenos Aires, Argentina) is a New York based artist and developer. He explores interstitial spaces between organic and synthetic, analog and digital, individual and collective. In his work he uses code as an expressive language with the intention of developing a better together.
Patricio studied and practiced psychotherapy and expressive art therapy. He holds an MFA in Design & Technology from Parsons The New School, where he now teaches. Currently he works as a Graphic Engineer at Mapzen making openSource mapping tools.
<p class="header"><a href="http://twitter.com/patriciogv" target="_blank">Twitter</a> - <a href="https://github.com/patriciogonzalezvivo" target="_blank">GitHub</a> - <a href="https://vimeo.com/patriciogv" target="_blank">Vimeo</a> - <a href="https://www.flickr.com/photos/106950246@N06/" target="_blank"> Flickr</a></div>
## Acknowledgements
Thanks to my wife [Jen Lowe](http://www.datatelling.com/), for her unconditional support, help and time editing this book.

@ -84,7 +84,6 @@ img {
}
.caption {
/*font-family: Baskerville, "Times New Roman", serif;*/
font-style: italic;
font-size: 16px;
line-height: 1.3;
@ -92,8 +91,6 @@ img {
}
.header {
/*font-family: Baskerville, "Times New Roman", serif;*/
/*font-style: italic;*/
font-size: 16px;
line-height: 0px;
margin-top: 60px;
@ -106,7 +103,6 @@ img {
font-style: italic;
text-decoration: none;
border:0!important;
}
a:hover, a:visited, a:link, a:active {

@ -1,25 +1,34 @@
# List of Examples
The following is a list of examples present in this book.
### Chapters examples
* Getting started
* Hello World
- ["Hello World!"](http://thebookofshaders.com/edit.html#02/hello_world.frag)
- ["Hello World!"](../edit.html#02/hello_world.frag)
* Uniforms
- [u_time](http://thebookofshaders.com/edit.html#03/time.frag)
- [gl_FragCoord](http://thebookofshaders.com/edit.html#03/space.frag)
- [u_time](../edit.html#03/time.frag)
- [gl_FragCoord](../edit.html#03/space.frag)
* Algorithmic drawing
* Shaping functions
- [Linear Interpolation](http://thebookofshaders.com/edit.html#05/linear.frag)
- [Exponential Interpolation](http://thebookofshaders.com/edit.html#05/expo.frag)
- [Step function](http://thebookofshaders.com/edit.html#05/step.frag)
- [Smoothstep function](http://thebookofshaders.com/edit.html#05/smoothstep.frag)
- [Iñigo Quiles's impulse](http://thebookofshaders.com/edit.html#05/impulse.frag)
- [Iñigo Quiles's Cubic Pulse](http://thebookofshaders.com/edit.html#05/cubicpulse.frag)
- [Iñigo Quiles's Exponential Step](http://thebookofshaders.com/edit.html#05/expstep.frag)
- [Iñigo Quiles's Parabola](http://thebookofshaders.com/edit.html#05/parabola.frag)
- [Iñigo Quiles's Power Curve](http://thebookofshaders.com/edit.html#05/pcurve.frag)
- [Linear Interpolation](../edit.html#05/linear.frag)
- [Exponential Interpolation](../edit.html#05/expo.frag)
- [Step function](../edit.html#05/step.frag)
- [Smoothstep function](../edit.html#05/smoothstep.frag)
- [Iñigo Quiles's Impulse](../edit.html#05/impulse.frag)
- [Iñigo Quiles's Cubic Pulse](../edit.html#05/cubicpulse.frag)
- [Iñigo Quiles's Exponential Step](../edit.html#05/expstep.frag)
- [Iñigo Quiles's Parabola](../edit.html#05/parabola.frag)
- [Iñigo Quiles's Power Curve](../edit.html#05/pcurve.frag)
* Color
- [Mix](../edit.html#06/mix.frag)
- [Easing functions](../edit.html#06/easing.frag)
- [Gradient](../edit.html#06/gradient.frag)
- [HSB](../edit.html#06/hsb.frag)
- [HSB - Color Wheel](../edit.html#06/hsb-colorwheel.frag)
### Advance
* [Moon](http://thebookofshaders.com/edit.html#examples/moon.frag&examples/images/moon-texture.jpg)
* [Moon](../edit.html#examples/moon.frag&examples/images/moon-texture.jpg)

@ -45,6 +45,9 @@
<hr>
<div id="content"> </div>
<hr>
<ul class="navigationBar" >
<li class="navigationBar" onclick="homePage()"> Home </li>
</ul>
<footer>
<p> Copyright 2015 <a href="http://www.patriciogonzalezvivo.com" target="_blank">Patricio Gonzalez Vivo</a> </p>

Loading…
Cancel
Save