# Building a bridge with code

## The bridge's function

First let's start with an empty bridge function:

```
precision highp float;
varying vec2 UV;
uniform float ratio;
vec4 bridge(vec2 p){
vec4 col = vec4(0.0);
// We will code the bridge here
return col;
}
void main(void){
float x = UV.x * ratio;
float y = UV.y;
vec2 pos = vec2(x, y);
vec4 col = vec4(0.0);
col += bridge(pos);
col.a = 1.0;
gl_FragColor = col;
}
```

You should now see that (nothing):

Marvelous! But let's actually add things!

In the bridge function, you can begin by plotting a line. Even easier: draw the area below a line:

```
float f = 0.5;
if(p.y < f){
col.rgb += vec3(0.2, 0.4, 0.0);
}
```

We now have this:

But let's say we only want to draw the line of the function.

Here we go, let's replace the function's content with:

```
float f = 0.5;
if(distance(p.y, f) < 0.01){
col.rgb += vec3(0.2, 0.4, 0.0);
}
```

We could also use `abs(p.y - f) < 0.01`

, you get the idea. If the point is too far away from the function, we don't draw it.

Result:

Let's say we want that line to be a curve:

```
float f = 0.5 + 0.2 * cos(2.0 * p.x);
```

Result:

Now is the time I realize I have not centered my position at `(0,0)`

. Screen UV coordinates go from 0 to 1. I prefer working with -0.5 to 0.5 and have 0,0 at the middle of the screen.

In the main, `pos`

becomes:

```
vec2 pos = vec2(x, y) - vec2(0.5);
```

In the bridge function, `f`

becomes:

```
float f = 0.0 + 0.2 * cos(2.0 * p.x);
```

Result:

We got it! We now have a nice bridge. But let's add some detail...

## Bridge supports

In the bridge's function, we could draw many bars in the screen like that:

```
if(cos(p.x * 40.0) > 0.9){
col.rgb += vec3(0.4, 0.4, 0.2);
}
```

The `cos`

function with gives cyclic values from -1 to 1 and we can use this fact to draw lines by selecting only places where the result is bigger than 0.9.

Now I want my supports to be over 0, which will be the horizon, and below `f`

, the bridge. So we'll replace our previous bars solution with this:

```
if(p.y < f && p.y > 0.0 && cos(p.x * 40.0) > 0.9){
col.rgb += vec3(0.4, 0.4, 0.2);
}
```

Which gives us:

Actually, I want my supports to be taller, because I'd like to code a suspension bridge.

```
if(p.y < f + 0.14 && p.y > 0.0 && cos(p.x * 40.0) > 0.9){
col.rgb += vec3(0.4, 0.4, 0.2);
}
```

I decided to make my supports tinner and draw them only if I did not already draw the bridge's deck:

```
vec4 bridge(vec2 p){
vec4 col = vec4(0.0);
float f = 0.0 + 0.2 * cos(2.0 * p.x);
if(distance(p.y,f) < 0.01){
col.rgb += vec3(0.2, 0.4, 0.0);
} else if ( p.y < f + 0.14 &&
p.y > 0.0 &&
cos(p.x * 40.0) > 0.97 ){
col.rgb += vec3(0.4, 0.4, 0.2);
}
return col;
}
```

Result:

Now we could go for some cables.

## The cables

For now, I pretty much copy pasted the bridge's deck function and I replaced `f`

with `cable_f`

. I also put an offset of `0.14`

to be at the top of the towers.

In the `bridge`

function:

```
float cable_f = 0.14 + 0.2 * cos(2.0 * p.x);
if(distance(p.y, cable_f) < 0.01){
col.rgb += vec3(0.8, 0.4, 0.0);
}
```

Result:

Now we want a function that is synchronised with the supports, so why not add this to `cable_f`

:

```
cable_f += 0.06 * cos(40.0 * p.x) - 0.06;
```

Honestly, to find this out, I just put a cos and tried many numbers to multiply `x`

until I realised it worked perfectly when it is the same number as in the `cos`

for the supports. Sometimes it is faster to randomly type on the keyboard instead of doing math. I also set both 0.06 factors by trial/error, please don't judge me.

Result:

Wow, this somewhat looks like a bridge!

If you got there, this is the point where you put a paypal link on your page to get paid for your content (no).

Here is the whole code for now:

```
precision highp float;
varying vec2 UV;
uniform float ratio;
vec4 bridge(vec2 p){
vec4 col = vec4(0.0);
float f = 0.0 + 0.2 * cos(2.0 * p.x);
if(distance(p.y,f) < 0.01){
col.rgb += vec3(0.2, 0.4, 0.0);
} else if ( p.y < f + 0.14 &&
p.y > 0.0 &&
cos(p.x * 40.0) > 0.97 ){
col.rgb += vec3(0.4, 0.4, 0.2);
}
float cable_f = 0.14 + 0.2 * cos(2.0 * p.x);
cable_f += 0.06 * cos(40.0 * p.x) - 0.06;
if(distance(p.y, cable_f) < 0.01){
col.rgb += vec3(0.8, 0.4, 0.0);
}
return col;
}
void main(void){
float x = UV.x * ratio;
float y = UV.y;
vec2 pos = vec2(x, y) - vec2(0.5);
vec4 col = vec4(0.0);
col += bridge(pos);
col.a = 1.0;
gl_FragColor = col;
}
```

## Suspender cables

First, a little refactoring. At this point I did some google image search for `suspension bridge parts`

. I know the deck is called a deck, the cables are cables, the supports are actually called towers and the smaller cables are suspender cables. Praise the lord for the existence of the internet.

So I renamed `f`

to `deck_f`

in the function `bridge`

.

To build the suspenders I added this:

```
if(cos(p.x * 270.0) > 0.9 && p.y > deck_f && p.y < cable_f){
col.rgb += vec3(0.9);
}
```

As you can see, I use the same method as I used for the supports (towers). Only, the cos' factor is higher and I limit the visibility to be only between the deck and the cables.

Result:

## Scene & reflection

Now, I would like to add a background. Later, I would like to reflect the entire scene to create water. To accomplish this, I will create a new function called `scene`

. Instead of calling `bridge`

, the main function will call `scene`

, which will manage the bridge and background.

Here is `scene`

:

```
vec4 scene(vec2 p){
vec4 col = vec4(0.0);
col += bridge(p);
return col;
}
```

Note that `scene`

has to be placed after `bridge`

(because `scene`

uses `bridge`

and the compiler needs every function in a function to be already defined).

Don't forget to change `bridge`

to `scene`

in the main!

Now for the background, I will go with a sunset-ish blend of colors. This is pretty much pseudo random typing on my keyboard, with the general Idea that I use the `y`

position to create a nice gradient.

Here is what I ended up with, placed just before the call to `bridge`

:

```
col.r += 0.6 - 0.6 * p.y;
col.g += 0.2 - 0.3 * p.y;
col.b += 0.3;
```

Result:

Then, for the water, I will at this at the end of my `main`

function:

```
if(pos.y < 0.0){
col.b += 0.2;
}
```

Result:

To create the reflection I simply have to call the `scene`

, but with the y axis flipped. I do this by multiplying by a vector (1,-1):

In the `main`

:

```
col += scene(pos * vec2(1.0, -1.0));
```

Result:

Note that the result is much too bright. This is because the background gradient is applied 2 times. A solution would be to multiply the color by `0.0`

for `y < 0`

in `scene`

:

```
if(p.y < 0.0){
col *= 0.0;
}
```

Result:

I want the reflection to be a bit transparent, so I changed my reflection line to this:

```
col += 0.4 * scene(pos * vec2(1.0, -1.0));
```

Also, to add some waves, I copied `pos`

to another variable, adding a sine to it.

So the previous line became:

```
vec2 water_pos = pos;
water_pos += 0.003 * cos(pos.y * 230.0);
col += 0.4 * scene(water_pos * vec2(1.0, -1.0));
```

Now, I want to create an animated gif, so I will need the water to move... To do this, I must declare a uniform `time`

at the top of the file, right after `uniform float ratio;`

:

I now have this at the top of my file:

```
precision highp float;
varying vec2 UV;
uniform float ratio;
uniform float time;
[...]
```

Now, I can take `time`

into account in my `water_pos`

. I replaced the original line with :

```
water_pos += 0.003 * cos(pos.y * 230.0 + time * 6.28);
```

(Note: I had to reload (`CTRL`

+`R`

) for shadergif to manage the new uniform and actually move my water)

Result (It's alive!):

# Final artistic tweaks

I decided to change the bridge cable function to something more realistic:

```
cable_f += 0.09 * (1.0 - abs(cos(20.0 * p.x + 3.1416 / 2.0))) - 0.09;
```

Also, I reordered the code so I only draw once in `bridge`

(Note that I also changed the colors):

```
vec4 bridge(vec2 p){
vec4 col = vec4(0.0);;
float deck_f = 0.0 + 0.2 * (cos(2.0 * p.x));
float cable_f = 0.14 + 0.2 * cos(2.0 * p.x);
cable_f += 0.09 * (1.0 - abs(cos(20.0 * p.x + 3.1416 / 2.0))) - 0.09;
if(distance(p.y, deck_f) < 0.01){
col.rgb += vec3(0.1, 0.1, 0.2);
} else if ( p.y < deck_f + 0.14 &&
p.y > 0.0 &&
cos(p.x * 40.0) > 0.99 ){
col.rgb += vec3(0.2, 0.2, 0.5);
} else if (distance(p.y, cable_f) < 0.004){
col.rgb += vec3(0.2, 0.3, 0.4);
} else if(cos(p.x * 490.0) > 0.9 && p.y > deck_f && p.y < cable_f){
col.rgb += vec3(0.6);
}
return col;
}
```

I also added this line in the `scene`

to create a sun:

```
col.rg += 1.8 * pow((1.0 - distance(p, vec2(0.0))), 18.0);
```

The development process for this line was something like:

```
col.rg += distance(p, vec2(0.0))
```

Nice, I want the color to be the most intense near the center though:

```
col.rg += 1.0 - distance(p, vec2(0.0))
```

What if I take the power:

```
col.rg += pow(1.0 - distance(p, vec2(0.0)), 2)
```

Wow that's a nice glow, lets put a bigger exponent:

```
col.rg += pow(1.0 * distance(p, vec2(0.0)), 4)
```

[...]

As you see, I try stuff and see if I like it. You should do the same. Find the right combination of math and human input.

The code looks like this:

```
precision highp float;
varying vec2 UV;
uniform float ratio;
uniform float time;
vec4 bridge(vec2 p){
vec4 col = vec4(0.0);;
float deck_f = 0.0 + 0.2 * (cos(2.0 * p.x));
float cable_f = 0.14 + 0.2 * cos(2.0 * p.x);
cable_f += 0.09 * (1.0 - abs(cos(20.0 * p.x + 3.1416 / 2.0))) - 0.09;
if(distance(p.y, deck_f) < 0.01){
col.rgb += vec3(0.1, 0.1, 0.2);
} else if ( p.y < deck_f + 0.14 &&
p.y > 0.0 &&
cos(p.x * 40.0) > 0.99 ){
col.rgb += vec3(0.2, 0.2, 0.5);
} else if (distance(p.y, cable_f) < 0.004){
col.rgb += vec3(0.2, 0.3, 0.4);
} else if(cos(p.x * 490.0) > 0.9 && p.y > deck_f && p.y < cable_f){
col.rgb += vec3(0.6);
}
return col;
}
vec4 scene(vec2 p){
vec4 col = vec4(0.0);
col.r += 0.8 - 0.5 * p.y;
col.g += 0.2 - 0.3 * p.y;
col.b += 0.3;
col += bridge(p);
col.rg += 1.8 * pow((1.0 - distance(p, vec2(0.0))), 18.0);
if(p.y < 0.0){
col *= 0.0;
}
return col;
}
void main(void){
float x = UV.x * ratio;
float y = UV.y;
vec2 pos = vec2(x, y) - vec2(0.5);
vec4 col = vec4(0.0);
col += scene(pos);
vec2 water_pos = pos;
water_pos += 0.003 * cos(pos.y * 230.0 + time * 6.28);
col += 0.4 * scene(water_pos * vec2(1.0, -1.0));
if(pos.y < 0.0){
col.b += 0.2;
}
col.a = 1.0;
gl_FragColor = col;
}
```