## Image filters

This is a series of blog posts related to WebGL. New post will be available every day

Join mailing list to get new posts right to your inbox

Built with

Hey 👋 Welcome back to WebGL month

Yesterday we've learned how to use textures in webgl, so let's get advantage of that knowledge to build something fun.

Today we're going to explore how to implement simple image filters

### Inverse

The very first and simple filter might be inverse all colors of the image.

How do we inverse colors?

Original values are in range `[0..1]`

If we subtract from each component `1`

we'll get negative values, there's an `abs`

function in glsl

You can also define other functions apart of `void main`

in glsl pretty much like in C/C++, so let's create `inverse`

function

📄 src/shaders/texture.f.glsl

```
uniform sampler2D texture;
uniform vec2 resolution;
+ vec4 inverse(vec4 color) {
+ return abs(vec4(color.rgb - 1.0, color.a));
+ }
+
void main() {
vec2 texCoord = gl_FragCoord.xy / resolution;
gl_FragColor = texture2D(texture, texCoord);
```

and let's actually use it

📄 src/shaders/texture.f.glsl

```
void main() {
vec2 texCoord = gl_FragCoord.xy / resolution;
gl_FragColor = texture2D(texture, texCoord);
+
+ gl_FragColor = inverse(gl_FragColor);
}
```

Voila, we have an inverse filter with just 4 lines of code

### Black and White

Let's think of how to implement black and white filter.

White color is `vec4(1, 1, 1, 1)`

Black is `vec4(0, 0, 0, 1)`

What are shades of gray? Aparently we need to add the same value to each color component.

So basically we need to calculate the "brightness" value of each component. In very naive implmentation we can just add all color components and divide by 3 (arithmetical mean).

Note: this is not the best approach, as different colors will give the same result (eg. vec3(0.5, 0, 0) and vec3(0, 0.5, 0), but in reality these colors have different "brightness", I'm just trying to keep these examples simple to understand)

Ok, let's try to implement this

📄 src/shaders/texture.f.glsl

```
return abs(vec4(color.rgb - 1.0, color.a));
}
+ vec4 blackAndWhite(vec4 color) {
+ return vec4(vec3(1.0, 1.0, 1.0) * (color.r + color.g + color.b) / 3.0, color.a);
+ }
+
void main() {
vec2 texCoord = gl_FragCoord.xy / resolution;
gl_FragColor = texture2D(texture, texCoord);
- gl_FragColor = inverse(gl_FragColor);
+ gl_FragColor = blackAndWhite(gl_FragColor);
}
```

Whoa! Looks nice

### Sepia

Ok, one more fancy effect is a "old-fashioned" photos with sepia filter.

Sepia is reddish-brown color. RGB values are `112, 66, 20`

Let's define `sepia`

function and color

📄 src/shaders/texture.f.glsl

```
return vec4(vec3(1.0, 1.0, 1.0) * (color.r + color.g + color.b) / 3.0, color.a);
}
+ vec4 sepia(vec4 color) {
+ vec3 sepiaColor = vec3(112, 66, 20) / 255.0;
+ }
+
void main() {
vec2 texCoord = gl_FragCoord.xy / resolution;
gl_FragColor = texture2D(texture, texCoord);
```

A naive and simple implementation will be to interpolate original color with sepia color by a certain factor. There is a `mix`

function for this

📄 src/shaders/texture.f.glsl

```
vec4 sepia(vec4 color) {
vec3 sepiaColor = vec3(112, 66, 20) / 255.0;
+ return vec4(
+ mix(color.rgb, sepiaColor, 0.4),
+ color.a
+ );
}
void main() {
vec2 texCoord = gl_FragCoord.xy / resolution;
gl_FragColor = texture2D(texture, texCoord);
- gl_FragColor = blackAndWhite(gl_FragColor);
+ gl_FragColor = sepia(gl_FragColor);
}
```

Result:

This should give you a better idea of what can be done in fragment shader.

Try to implement some other filters, like saturation or vibrance

See you tomorrow 👋

This is a series of blog posts related to WebGL. New post will be available every day

Join mailing list to get new posts right to your inbox

Built with

## Discussion (0)