thebookofshaders/07/README.md

236 lines
15 KiB
Markdown
Raw Normal View History

2015-03-15 15:35:14 +00:00
## Shapes
2015-05-07 19:27:25 +00:00
![Alice Hubbard, Providence, United States, ca. 1892. Photo: Zindman/Freemont.](froebel.jpg)
2015-04-02 17:43:22 +00:00
2015-05-07 19:27:25 +00:00
Finally! We have been building skills for this moment! You have learned most of the GLSL foundations, types and functions. You have practiced your shaping equations over and over. Now is the time to put it all together. You are up for this challenge! In this chapter you'll learn how to draw simple shapes in a parallel procedural way.
2015-03-15 15:35:14 +00:00
### Rectangle
2015-05-07 19:27:25 +00:00
Imagine we have grid paper, like the one we used in math classes, and the homework is to draw a square. The paper size is 10x10 and the square is supposed to be 8x8. What will you do?
2015-03-15 15:35:14 +00:00
2015-04-02 14:19:21 +00:00
![](grid_paper.jpg)
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
You'd paint everything except the first and last rows and the first and last column, right? How does this relate to shaders? Each little square of our grid paper is a thread (a pixel). Each little square knows its position, like the coordinates of a chess board. In previous chapters we have mapped *x* and *y* to the *red* and *green* color channels, ____we learn thats our field and space. A narrow two dimensional territory between 0.0 and 1.0.____ How we can use this to draw a centered square in the middle of our billboard?
2015-03-31 16:04:59 +00:00
2015-05-10 19:52:56 +00:00
Let's start by sketch a pseudo code that uses ```if``` statements over our spatial field. The principles to do this are remarkably similar to how we think of the grid paper scenario.
2015-03-31 16:04:59 +00:00
2015-05-10 19:52:56 +00:00
```glsl
if ( (X OVER 1) AND (Y OVER 1) )
paint white
else
paint black
```
2015-05-07 19:27:25 +00:00
2015-05-10 19:52:56 +00:00
Now we have a better idea how this could work lets replace the ```if``` statement with a [```step()```](../glossary/index.html#step.md), also instead of using 10x10 lets use normalize values.
2015-03-15 15:35:14 +00:00
```glsl
uniform vec2 u_resolution;
void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.0);
2015-05-10 19:52:56 +00:00
float left = step(0.1,st.x); // Similar to ( X over 0.1 )
float bottom = step(0.1,st.y); // Similar to ( Y over 0.1 )
// Each result will return 1.0 (white) or 0.0 (black)
// the multiplication of both will be similar to AND
color = vec3( left * bottom );
2015-03-15 15:35:14 +00:00
gl_FragColor = vec4(color,1.0);
}
```
2015-05-10 19:52:56 +00:00
As we saw the [```step()```](../glossary/index.html#step.md) function will turn every pixel below 0.1 to black (```vec3(0.0)```) and the rest to white (```vec3(1.0)```) . The multiplication beetween them works as a logical ```AND``` operation, where both of them have to be 1.0 return 1.0 . This end up drawing two black lines, on at the bottom and other at the left side of the canvas.
2015-03-15 15:35:14 +00:00
2015-04-02 14:19:21 +00:00
![](rect-01.jpg)
2015-03-15 15:35:14 +00:00
2015-05-10 19:52:56 +00:00
In the previous code we repeat the structure for each axis (left and bottom). We can save some lines of code by passing two values directly to [```step()```](../glossary/index.html#step.md) instead of one. That will look something like this:
2015-03-15 15:35:14 +00:00
2015-05-10 19:52:56 +00:00
```glsl
vec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;
```
2015-05-07 19:27:25 +00:00
2015-05-10 19:52:56 +00:00
So far, we only draw only two borders ( buttom-left ) of our rectangle. Let's do the other two ( top-right ). Check the following code
2015-03-31 16:04:59 +00:00
2015-05-10 19:52:56 +00:00
<div class="codeAndCanvas" data="rect-making.frag"></div>
2015-05-07 19:27:25 +00:00
2015-05-10 19:52:56 +00:00
Uncomment lines 21-22 and see how we invert the ```st``` coordinates and repeat the same [```step()```](../glossary/index.html#step.md) function. That way the ```vec2(0.0,0.0)``` will be in the top right corner. This is the digital equivalent of flipping the page and repeating the previous procedure.
2015-03-31 16:04:59 +00:00
2015-04-02 14:19:21 +00:00
![](rect-02.jpg)
2015-05-10 19:52:56 +00:00
Take note that all sides are been multiply between them, this is equivalent to write:
```glsl
vec2 bl = step(vec2(0.1),st); // bottom-left
vec2 tr = step(vec2(0.1),1.0-st); // top-right
color = vec3(bl.x * bl.y * tr.x * tr.y);
```
2015-05-07 19:27:25 +00:00
2015-05-10 19:52:56 +00:00
Interesting right? This technique is all about using [```step()```](../glossary/index.html#step.md) and multiply for logical operations and flipping the coordinates.
2015-03-31 16:04:59 +00:00
2015-05-10 19:52:56 +00:00
Before going forward, try the following exercises:
2015-03-15 15:35:14 +00:00
2015-05-10 19:52:56 +00:00
* Change the size and proportions of the rectangle.
2015-03-15 15:35:14 +00:00
2015-05-03 19:30:21 +00:00
* Experiment with the same code but using [```smoothstep()```](../glossary/index.html#smoothstep.md) instead of [```step()```](../glossary/index.html#step.md). Note that by changing values, you can go from blurred edges to elegant smooth borders.
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
* Do another implementation that uses [```floor()```](../glossary/index.html#floor.md).
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
* Choose the implementation you like the most and make a function of it that you can reuse in the future. Make your function flexible and efficient.
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
* Make another function that just draws the outline of a rectangle.
2015-03-31 16:04:59 +00:00
2015-05-07 19:27:25 +00:00
* How do you think you can move and place different rectangles in the same billboard? If you figure out how, show off your skills by making a composition of rectangles and colors that resembles a [Piet Mondrian](http://en.wikipedia.org/wiki/Piet_Mondrian) painting.
2015-04-02 19:54:09 +00:00
![Piet Mondria - Tableau (1921)](mondrian.jpg)
2015-03-15 15:35:14 +00:00
### Circles
2015-05-07 19:27:25 +00:00
It's easy to draw squares on grid paper; in the same way it's simple to draw rectangles on cartesian coordinates. But circles requires another approach, especially ____if we need to come up with a "per-pixel" or "per-square" approach____. One solution is to *re-map* the spatial coordinates so that we can use a [```step()```](../glossary/index.html#step.md) function to draw a circle.
2015-03-31 16:04:59 +00:00
2015-05-07 19:27:25 +00:00
How? Let's start by going back to math class and the grid paper, where we used to open a compass to the desired radius of a circle, press one of the compass points at the center of the circle and then trace the edge of the circle with a simple spin.
2015-03-31 16:04:59 +00:00
![](compass.jpg)
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
Translating this to a shader where each square on the grid paper is a pixel implies *asking* each pixel (or thread) if it is inside the area of the circle. We do this by computing the distance from the pixel to the center of the circle.
2015-04-02 14:19:21 +00:00
![](circle.jpg)
2015-03-31 16:04:59 +00:00
2015-05-07 19:27:25 +00:00
There are several ways to calculate that distance. The easiest one uses the [```distance()```](../glossary/index.html#distance.md) function, which internally computes the [```length()```](../glossary/index.html#length.md) of the difference between two points (in our case the pixel coordinate and the center of the canvas). The ```length()``` function is nothing but a shortcut of the [hypotenuse equation](http://en.wikipedia.org/wiki/Hypotenuse) that uses square root ([```sqrt()```](../glossary/index.html#sqrt.md)) internally.
2015-03-15 15:35:14 +00:00
![](hypotenuse.png)
2015-05-07 19:27:25 +00:00
You can use [```distance()```](../glossary/index.html#distance.md), [```length()```](../glossary/index.html#length.md) or [```sqrt()```](../glossary/index.html#sqrt.md)) to calculate the distance to the center of the billboard. The following code contains these three functions and the non-surprising fact that each one returns exactly same result.
* Comment and uncomment lines to try the different ways to get the same result.
2015-03-15 15:35:14 +00:00
<div class="codeAndCanvas" data="circle-making.frag"></div>
2015-05-07 19:27:25 +00:00
In the previous example we map the distance to the center of the billboard to the color brightness of the pixel. The closer a pixel is to the center, the lower (darker) value it has. Notice that the values don't get too high because from the center ( ```vec2(0.5, 0.5)``` ) the maximum distance barely goes over 0.5. Contemplate this map and think:
2015-03-15 15:35:14 +00:00
2015-03-31 16:04:59 +00:00
* What you can infer from it?
2015-03-15 15:35:14 +00:00
2015-03-31 16:04:59 +00:00
* How we can use this to draw a circle?
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
* Modify the above code in order to contain the circular gradient inside the canvas. ____I'm not sure what this means____
2015-03-15 15:35:14 +00:00
### Distance field
2015-05-07 19:27:25 +00:00
Imagine the above example as an inverse altitude map, where darker implies taller. The gradient shows us something similar to the pattern made by a cone. ____Imagine yourself on the top of that cone, under your foot you hold the tip of a ruled tape while the rest of it goes down the hill. Because you are in the center of the canvas, the ruler will mark "0.5" in the extreme. (This isn't quite right the way it's written)____ ____This will be constant in all your directions. (This is confusing) ____ By choosing where to “cut” the cone you will get a bigger or smaller circular surface.
2015-03-15 15:35:14 +00:00
![](distance-field.jpg)
2015-05-07 19:27:25 +00:00
Basically we are using a re-interpretation of the space (based on the distance to the center) to make shapes. This technique is known as a “distance field” and is use in different ways from font outlines to 3D graphics.
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
Try the following exercises:
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
* Use [```step()```](../glossary/index.html#step.md) to turn everything above 0.5 to white and everything below to 0.0.
2015-03-15 15:35:14 +00:00
* Inverse the colors of the background and foreground.
2015-05-07 19:27:25 +00:00
* Using [```smoothstep()```](../glossary/index.html#smoothstep.md) experiment with different values to get nice smooth borders on your circle.
* Once you are happy with an implementation, make a function of it that you can reuse in the future.
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
* Use your function to mask a color with it. ____I don't know what this means____
2015-03-31 16:04:59 +00:00
2015-05-07 19:27:25 +00:00
* Can you animate your circle to grow and shrink, simulating a beating heart?
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
____show an example of animation if you're going to have exercises about animation____
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
* What about moving this circle? Can you move it and place different circles in a single billboard?
2015-03-31 16:04:59 +00:00
2015-05-07 19:27:25 +00:00
* What happens if you combine distances fields together using different functions and operations?
2015-03-31 16:04:59 +00:00
```glsl
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
```
2015-03-15 15:35:14 +00:00
2015-05-07 19:27:25 +00:00
* Make three compositions using this technique. If they are animated, even better!
2015-03-15 15:35:14 +00:00
#### For your tool box
2015-05-07 19:27:25 +00:00
In terms of computational power the [```sqrt()```](../glossary/index.html#sqrt.md) function - and all the functions that depend on it - can be expensive. Here is another way to create a circular distance field by using [```dot()```](../glossary/index.html#dot.md) product.
____do you want to describe how this is working at all?____
2015-03-15 15:35:14 +00:00
<div class="codeAndCanvas" data="circle.frag"></div>
2015-04-15 15:08:39 +00:00
### Useful properties of a Distance Field
2015-04-13 13:53:03 +00:00
2015-04-15 15:08:39 +00:00
![Zen garden](zen-garden.jpg)
2015-04-13 13:53:03 +00:00
2015-05-07 19:27:25 +00:00
Distance fields can be used to draw almost everything. Obviously the more complex a shape is, the more complicated its equation will be, but once you have the formula to make distance fields of a particular shape it is very easy to combine and/or apply effects to it, like smooth edges and multiple outlines. Because of this, distances fields are popular in font rendering. ____this is the second time you've mentioned font rendering - it's kind of weird to keep mentioning it without showing (or linking to) and example____
Take a look at the following code.
2015-04-15 15:08:39 +00:00
2015-05-07 19:27:25 +00:00
____note to jen to edit the comments in the code below____
2015-04-13 13:53:03 +00:00
<div class="codeAndCanvas" data="rect-df.frag"></div>
2015-05-07 19:27:25 +00:00
We start by moving the coordinate system to the center and shrinking it in half in order to ____contain the position values____ between -1 and 1. Also on *line 24* we are visualizing the distance field values using a [```fract()```](../glossary/index.html#fract.md) function making it easy to see the pattern they create. The distance field pattern repeats over and over like rings in a Zen garden.
2015-04-15 15:08:39 +00:00
2015-05-07 19:27:25 +00:00
Lets take a look at the distance field formula on *line 19*. There we are calculating the distance to the position on ```(.3,.3)``` or ```vec3(.3)``` in ____all four sign permutations____ (thats what [```abs()```](../glossary/index.html#abs.md) is doing there).
2015-04-15 15:08:39 +00:00
2015-05-07 19:27:25 +00:00
If you uncomment *line 20*, you will note that we are combining the distances to these four points using the [```min()```](../glossary/index.html#min.md) to zero. The result produces an interesting new pattern.
2015-04-13 13:53:03 +00:00
2015-05-07 19:27:25 +00:00
Now try uncommenting *line 21*, we are doing the same but using the [```max()```](../glossary/index.html#max.md) function. The result is a rectangle with rounded corners. Note how the rings of the distance field get smoother the further away they get from the center.
2015-04-13 13:53:03 +00:00
2015-05-07 19:27:25 +00:00
Finish uncommenting *lines 27 to 29* one by one to understand the different uses of a distance field pattern.
2015-04-13 13:53:03 +00:00
2015-03-21 13:02:43 +00:00
### Polar shapes
2015-03-22 12:07:04 +00:00
![Robert Mangold - Untitled (2008)](mangold.jpg)
2015-03-31 16:15:39 +00:00
In the chapter about color we map the cartesian coordinates to polar coordinates by calculating the *radius* and *angles* of each pixel with the following formula:
2015-03-21 13:02:43 +00:00
```glsl
vec2 pos = vec2(0.5)-st;
float r = length(pos)*2.0;
float a = atan(pos.y,pos.x);
```
2015-05-07 19:27:25 +00:00
We use part of this formula at the beginning of the chapter to draw a circle. We calculated the distance to the center using [```length()```](../glossary/index.html#length.md). Now that we know about distance fields we can learn another way of drawing shapes using polar coordinates.
2015-03-21 13:02:43 +00:00
2015-05-07 19:27:25 +00:00
This technique is a little restrictive but very simple. It consists of changing the radius of a circle depending on the angle to achieve different shapes. How does the modulation work? Yes, using shaping functions!
2015-04-15 19:30:08 +00:00
2015-05-07 19:27:25 +00:00
Below you will find the same functions in the cartesian graph and in a polar coordinates shader example (between *lines 21 and 25*). Uncomment the functions one-by-one, paying attention the relationship between one coordinate system and the other.
2015-03-21 13:02:43 +00:00
<div class="simpleFunction" data="y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x*2.5))*0.5+0.3;
//y = abs(cos(x*12.)*sin(x*3.))*.8+.1;
//y = smoothstep(-.5,1., cos(x*10.))*0.2+0.5;"></div>
<div class="codeAndCanvas" data="polar.frag"></div>
Try to:
2015-05-07 19:27:25 +00:00
* Animate these shapes.
* Combine different shaping functions to *cut holes* in the shape to make better flowers, snowflakes and gears.
2015-03-21 13:02:43 +00:00
* Use the ```plot()``` function we were using on the *Shaping Functions Chapter* to draw just the contour.
2015-04-02 21:27:45 +00:00
### Combining powers
2015-05-07 19:27:25 +00:00
____Now that we've learned how to modulate the radius of a circle according to the angle using the [```atan()```](../glossary/index.html#atan.md) to draw different shapes, we can learn how use ```atan()``` with distance fields, and apply all the tricks and effects possible with them.____
2015-04-02 21:27:45 +00:00
2015-05-07 19:27:25 +00:00
The trick will use the number of edges of a polygon to construct the distance field using polar coordinates. Check out [the following code](http://thndl.com/square-shaped-shaders.html) from [Andrew Baldwin](https://twitter.com/baldand).
2015-04-02 21:27:45 +00:00
<div class="codeAndCanvas" data="shapes.frag"></div>
2015-05-07 19:27:25 +00:00
* Using this example, make a function that inputs the position and number of corners of a desired shape and returns a distance field value.
2015-04-15 19:30:08 +00:00
2015-04-18 14:51:00 +00:00
* Mix distance fields together using [```min()```](../glossary/index.html#min.md) and [```max()```](../glossary/index.html#max.md).
2015-04-15 19:30:08 +00:00
2015-04-15 19:32:28 +00:00
* Choose a geometric logo to replicate using distance fields.
2015-05-07 19:27:25 +00:00
Congratulations! You have made it through the rough part! Take a break and let these concepts settle - drawing simple shapes in Processing is easy but not here. In shader-land ____everything the way to thing on shapes is twisted____ and it can be exhausting to adapt to this new paradigm of coding.
2015-03-21 13:02:43 +00:00
2015-05-07 19:27:25 +00:00
Now that you know how to draw shapes I'm sure new ideas will pop into your mind. ____In the following chapter we will learn more about how to move, rotate and scale them moving. This will allow you to compose them!____