• Stars
    star
    307
  • Rank 131,843 (Top 3 %)
  • Language
  • License
    MIT License
  • Created over 7 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A brief introduction to fragment shaders.
jam3-lesson » webgl » shader-intro

WebGL Lessons — Fragment Shaders

Shaders are a key concept in WebGL and graphics programming, so this lesson will be one of the first in our jam3-lesson-webgl series. Even if you only end up working with higher level abstractions (like ThreeJS and materials), understanding shaders will give you a better appreciation of the entire graphics pipeline.

Once you master shaders, you have a world of new effects, transitions, and rendering possibilities at your fingertips.

A complex fragment shader rendering an abstract 3D mesh. (source)

This lesson will cover the very basics of GLSL, and introduce a couple of simple image effects.

What are shaders?

Shaders are small programs written in the OpenGL Shading Language (GLSL), a language similar to C/C++. They are compiled specifically to run on the GPU, and take advantage of parallelization to quickly compute things like pixel color and vertex positions.

A simple program might look like this:

precision highp float;
varying vec2 vUv;

void main () {
  gl_FragColor = vec4(vUv.xy, 1.0, 1.0);
}

Applied to a rectangle, we end up with:

Shader Types

There are two types of shader programs used in WebGL: vertex and fragment shaders. Together, they are used to draw content on the screen with WebGL.

  • Vertex Shaders are typically used to place primitives (i.e. triangles, lines) in 2D or 3D space
  • Fragment Shaders are typically used to output the pixel colors of these primitives

In a way, the vertex shaders figure out the shape, and the fragment shaders are painting in the color-by-number of each shape.

This lesson will focus on Fragment Shaders, since we can begin using them immediately to practice the language and concepts.

What does a fragment shader do?

Fragment shaders, also known as pixel shaders, output a single color for each rendered fragment. In most cases, a fragment is just a single pixel on the screen.

The pixel color is stored with separate components for RGBA (Red, Green, Blue, Alpha). Each component is in the range 0.0 to 1.0, so:

  • (0.0, 0.0, 0.0, 1.0) represents opaque black
  • (1.0, 1.0, 1.0, 1.0) represents opaque white
  • (1.0, 0.0, 0.0, 0.5) represents pure red with 50% alpha

The fragment shader is run once per pixel. In other words, if your canvas is 1920x1080 pixels and your triangle fills the whole canvas, the fragment shader will need to run 2,073,600 times per frame! GPUs can handle this because they draw many pixels in parallel (i.e. at the same time). Because of this, the program can only operate on a single pixel at a time, and can't access the values of neighbouring pixels.

ShaderToy

We're going to write our shaders using an online tool called Shadertoy. This will get us running without any boilerplate or set-up. Let's begin!

Step 1. Turning The Screen Blue

Let's open up Shadertoy.com in Chrome or FireFox and click New Shader on the top right.

shadertoy

The left panel shows the WebGL canvas and the output of our shader. On the right, we have a code editor for our fragment shader, showing some GLSL code.

By modifying the value of fragColor — the output pixel color — we can produce a wide variety of images and effects. This is a 4-component vector, a vec4, holding the RGBA components we mentioned earlier.

Let's change this code to something simpler, to render a blue canvas. Remember that colors are RGBA vectors, so we can use 1.0 for the blue channel and 1.0 for the alpha channel.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  fragColor = vec4(0.0, 0.0, 1.0, 1.0);
} 

After you change the code, press the Play button at the bottom of the code editor (or press Cmd/Ctrl + Enter).

blue

Woah, a blue rectangle! ✨

Data Types & Syntax

Before we go further, let's take a second to look at the syntax of GLSL.

In GLSL, a vector is much like an array of numbers, holding a fixed number of elements (2, 3, or 4). Here is a brief list of some data types available in GLSL:

  • float - a simple float value, e.g. 0.75
  • vec2 - a vector with 2 floats, e.g. (x, y)
  • vec3 - a vector with 3 floats, e.g. (x, y, z) or (r, g, b)
  • vec4 - a vector with 4 floats, e.g. (r, g, b, a)
  • int - an integer, e.g. 4
  • sampler2D - a special type which we will get to when dealing with texture sampling

Notice that WebGL's GLSL compilers are a little picky. For example, the following might give us an error, because we are trying to give an int type where the compiler expects a float:

float x = 5; // ERROR
vec2 v = vec2(10, 5); // ERROR

Instead, you should add a decimal place for float values, like so:

float x = 5.0;
vec2 v = vec2(10.0, 5.0);

To create a new vector, you use the constructor like so:

vec2 pixelPosition = vec2(125.0, 5.0);
vec3 position3d = vec3(5.0, 1.0, 2.0);
vec4 blue = vec4(0.0, 0.0, 1.0, 1.0);

// sets all components to 1.0
vec4 white = vec4(1.0);

// create a new vec4 from a vec3
vec4 color = vec4(blue, 1.0);

And we can access them in a number of ways:

// copies only the first three components, notice we need to use vec3
vec3 other = myVec.xyz;
 
// copies all four components
vec4 aColor = myVec.rgba;

// copy only first three components
vec3 aColorRGB = myVec.rgb;
 
// "swizzles" the (x, y) components
myVec.xy = myVec.yx;

// the first component
float x = myVec.x;
 
// we could even treat vectors as an array
float e0 = myVec[0];

It doesn't matter whether you use .rgba or .xyzw to access a vector, so long as it's consistent (you cannot do .rgzw, for example). You can also use .stpq (typically used in texture coordinates), though it is less common.

Also useful to know is the const keyword, similar to ES2015, which allows us to declare constants. For example, our earlier shader could be written as:

const vec3 blue = vec3(0.0, 0.0, 1.0); 

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  fragColor = vec4(blue, 1.0);
} 

All of the following lines are equivalent:

fragColor = vec4(blue, 1.0);
fragColor = vec4(blue.rgb, 1.0);
fragColor = vec4(blue.xyz, 1.0);
fragColor = vec4(vec3(blue), 1.0);
fragColor = vec4(blue.r, blue.g, blue.b, 1.0);

For a nice GLSL cheat sheet, see here.

Textures

Shadertoy gives us some nice features out of the box for working with inputs such as images, videos, cube maps and even audio. These inputs use the variable names iChannel0, iChannel1, etc.

Let's add some texture — first, click on the black iChannel0 and choose an image, ideally the European street. Once you click it, you can close the input panel and paste the following code:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = fragCoord.xy / iResolution.xy;
  fragColor = texture2D(iChannel0, uv);
}

Now you should see an image:

Image Output

The built-in texture2D method will sample our texture in iChannel0 at the specified texture coordinate, uv, and returns a vec4 color. We will talk a bit more about texture coordinates later, but for now let's see if we can change the image colors a bit!

More Blues

No that we have an image let's manipulate it by only displaying the blue channel from our 3 RGB channels. We will create a temporary vec4, and only pass the blue channel to the final fragColor.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = fragCoord.xy / iResolution.xy;
  vec4 color = texture2D(iChannel0, uv);
  fragColor = vec4(0.0, 0.0, color.b, 1.0);
}

This gives a nice blue tint to our image:

Blue Image Output

With a similar approach, we can turn the image black & white. Here we use color.bbb, which expands to vec3(color.b, color.b, color.b).

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = fragCoord.xy / iResolution.xy;
  vec4 color = texture2D(iChannel0, uv);
  fragColor = vec4(color.bbb, 1.0);
}

Black and White Image Output

💡 Note: This is a naïve approach to grayscale an image — there are some other approaches that compensate for the way the human eye perceives the three color channels.

UV Space

So what is the uv for, you might ask? This vec2 represents the texture coordinates for that pixel, which tells the shader where in the texture the pixels should be fetched.

In WebGL, texture coordinates typically start at (0.0, 0.0) in the lower left, and go to (1.0, 1.0) in the upper right.

uv

We can confirm this by rendering the uv.x and uv.y coordinates independently to the screen. For example, uv.x will show a gradient from black (i.e. 0.0) to white (i.e. 1.0).

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = fragCoord.xy / iResolution.xy;
  vec4 color = texture2D(iChannel0, uv);
  fragColor = vec4(vec3(uv.x), 1.0);
}

Result:

uvx

By manipulating this vector, we can change the way the texture is sampled, leading to a variety of interesting effects.

Flip & Scale

By multiplying the uv.x and/or uv.y by -1, we can flip the image horizontally and/or vertically.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = fragCoord.xy / iResolution.xy;
  uv.y *= -1.0;
  vec4 color = texture2D(iChannel0, uv);
  fragColor = vec4(color.rgb, 1.0);
}

Result:

flip

You can multiply the UV coordinates by a number greater than 1.0 to "zoom out" or less than 1.0 to "zoom in." In reality, we are just modifying how the program will sample the texture.

vec2 uv = fragCoord.xy / iResolution.xy;
uv *= 0.5;
vec4 color = texture2D(iChannel0, uv);

💡 If you multiply a vector by a single number, it multiplies all components of the vector by that number.

scale

Pinch Distortion

Lastly, let's try something a bit more advanced. We know that (0.5, 0.5) is the center of the screen, and if we take the current coordinate and subtract that point, we will end up with a direction vector that gets larger for pixels further away from the center.

vec2 direction = uv - 0.5;

💡 The above is equivalent to:

vec2 direction = vec2(uv.xy - vec2(0.5, 0.5));


Now, we can get the *length* or *magnitude* of this vector to determine how far away the current pixel is from the center. We can use this to scale the `uv` coordinates:

```glsl
vec2 direction = uv - 0.5;
uv *= length(direction);

Result:

squash

Aspect Correction

Finally, let's make the squash effect a circle, rather than an ellipse, by correcting for our rectangular aspect ratio.

To do this, we will modify the direction vector before computing its length, so that our distance from center is not affected by the rectangular aspect ratio. We can use iResolution to get the width and height of the canvas in pixels.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
  vec2 uv = fragCoord.xy / iResolution.xy;
  vec2 direction = uv - 0.5;
  direction.x *= iResolution.x / iResolution.y;
  uv *= length(direction);

  vec4 color = texture2D(iChannel0, uv);
  fragColor = vec4(color.rgb, 1.0);
}

Result:

squash

Next Steps

That's all for this lesson! Our next lesson introduces vertex and fragment shaders in the context of ThreeJS, applying them to a custom geometry.

WebGL Lessons — ThreeJS Shaders

Stay tuned for more lessons! 😄

Further Reading

Authors

More Repositories

1

math-as-code

a cheat-sheet for mathematical notation in code form
14,818
star
2

devtool

[OBSOLETE] runs Node.js programs through Chromium DevTools
JavaScript
3,774
star
3

nice-color-palettes

nice colour palettes as JSON
JavaScript
848
star
4

three-bmfont-text

renders BMFont files in ThreeJS with word-wrapping
JavaScript
764
star
5

glsl-fast-gaussian-blur

optimized single-pass blur shaders for GLSL
JavaScript
659
star
6

hihat

🎩 local Node/Browser development with Chrome DevTools
JavaScript
447
star
7

jam3-lesson-webgl-shader-threejs

Using custom vertex and fragment shaders in ThreeJS
JavaScript
361
star
8

web-audio-player

a cross-browser WebAudio player
JavaScript
244
star
9

360-image-viewer

A standalone panorama viewer with WebGL
JavaScript
243
star
10

ae-to-json

will export an After Effects project as a JSON object
JavaScript
225
star
11

msdf-bmfont

Generate BMFont texture and spec using msdfgen
JavaScript
161
star
12

jam3-lesson

142
star
13

audiobuffer-to-wav

convert an AudioBuffer to .wav format
JavaScript
132
star
14

Invisible-Highway

Invisible Highway is an experiment in controlling physical things in the real world by drawing in AR. Simply make a pathway along the floor on your phone and the robot car will follow that path on the actual floor in your room. A custom highway with scenery is generated along the path to make the robots a little more scenic on your phone screen.
C#
130
star
15

awesome-streetview

beautiful [lat, lng] Google Street View locations
JavaScript
129
star
16

ffmpeg-gif

shell script to convert video to high quality GIF with ffmpeg
JavaScript
124
star
17

jam3-lesson-module-basics

intro to modular programming for frontend JavaScript
113
star
18

orbit-controls

generic controls for orbiting a target in 3D
JavaScript
110
star
19

nextjs-boilerplate

Jam3 NextJS Generator for SPA, SSG, SSR and JAMStack applications
TypeScript
104
star
20

svg-to-image

convert SVG text to a Image that can be drawn in canvas
JavaScript
103
star
21

voice-activity-detection

Voice activity detection
JavaScript
102
star
22

extract-streetview

extract street view spherical images and depth information
JavaScript
101
star
23

react-f1

F1 ui animation library for React
JavaScript
90
star
24

webgl-react-boilerplate

WebGL React App ⚡️
JavaScript
87
star
25

opentype-layout

word wraps and lays out Opentype.js glyphs
JavaScript
86
star
26

chaikin-smooth

Chaikin's smoothing algorithm for 2D polylines
JavaScript
82
star
27

f1

A stateful ui library
JavaScript
78
star
28

touch-scroll-physics

scroll physics for a scroll pane with edge bounce and velocity
JavaScript
77
star
29

preloader

A library for loading common web assets
JavaScript
69
star
30

three-png-stream

streams ThreeJS render target pixel data
JavaScript
66
star
31

layout-bmfont-text

word-wraps and lays out text glyphs
JavaScript
59
star
32

react-background-video-player

React background video component with simple player API
JavaScript
58
star
33

ios-safe-audio-context

create a WebAudio context that works in iOS and everywhere else
JavaScript
57
star
34

perspective-camera

a high-level 3D perspective camera
JavaScript
53
star
35

glsl-hsl2rgb

HSL to RGB color conversion in GLSL
GLSL
50
star
36

touches

simplified touch/mouse events for flick and swipe
JavaScript
45
star
37

ios-video-test

a test of inline iOS video playback
JavaScript
45
star
38

three-path-geometry

thick 2D lines for ThreeJS
JavaScript
42
star
39

jam3-lesson-canvas2d

open source code for a Canvas2D workshop at Jam3
JavaScript
41
star
40

maya-json-export

a generic Maya to JSON exporter for triangle meshes
JavaScript
41
star
41

glsl-100-to-300

transpiles GLSL tokens from v100 to v300 es
JavaScript
39
star
42

xhr-request

tiny http client for Node and the browser
JavaScript
39
star
43

webvr-gearvr-test

a simple test of WebVR running natively in GearVR
JavaScript
38
star
44

generator-jam3

This is a generator for Jam3 projects
JavaScript
37
star
45

google-maps-api

Get up and running with the google maps API quickly
JavaScript
34
star
46

babel-plugin-static-fs

statically transforms Node fs for the browser
JavaScript
33
star
47

ae-to-json-cli

This is a command line application to export After Effects files as JSON
JavaScript
33
star
48

tech-we-use

A list of technologies: modules, libraries, and tools we use
32
star
49

load-bmfont

loads a BMFont file in Node and the browser
JavaScript
31
star
50

jam3-testing-tools

a brief intro to testing tools
30
star
51

gl-pixel-stream

streaming gl.readPixels from an FBO
JavaScript
30
star
52

jam3-lesson-module-creation

introduction to creating a new npm module
29
star
53

threejs-generate-gif

Generate an animated GIF (using the GPU) from a threejs scene
JavaScript
29
star
54

tap-dev-tool

prettifies TAP in the browser's console
JavaScript
29
star
55

text-split

Utility for splitting text into chunks based on regex.
JavaScript
27
star
56

three-simplicial-complex

render simplicial complexes with ThreeJS
JavaScript
27
star
57

three-buffer-vertex-data

an easy way to set vertex data on a BufferGeometry
JavaScript
27
star
58

parse-dds

parses the headers of a DDS texture file
JavaScript
26
star
59

threejs-post-process-example

a tutorial on ThreeJS post processing
JavaScript
24
star
60

add-px-to-style

Will add px to the end of style values which are Numbers
JavaScript
24
star
61

google-panorama-by-location

gets a Google StreetView by [ lat, lng ]
JavaScript
24
star
62

detect-import-require

list require and import paths from a JavaScript source
JavaScript
24
star
63

mesh-heightmap-contours

Given a heightmap, generate a "contoured" terrain mesh
JavaScript
24
star
64

says

cross-platform 'say' command using Electron
JavaScript
23
star
65

background-cover

Simulate 'background-size: cover' on HTMLVideoElement and HTMLImageElement
JavaScript
23
star
66

analyser-frequency-average

gets an average intensity between two frequency ranges
JavaScript
23
star
67

webgl-components

Modular components and utilities used for WebGL based projects 💅
TypeScript
22
star
68

video-element

A simple HTML5/YouTube Video Element with a unified interface
JavaScript
22
star
69

three-fluid-demo

ThreeJS fluid simulation demo
GLSL
22
star
70

delaunify

randomly delaunay-triangulates an image
JavaScript
22
star
71

camera-unproject

unproject 2D point to 3D coordinate
JavaScript
21
star
72

heightmap-contours

Generate a series of 2D contour meshes over a heightmap
JavaScript
21
star
73

glsl-blend-overlay

blend mode 'overlay' for GLSL
C
20
star
74

ray-3d

a high-level ray picking helper for 3D intersection
JavaScript
19
star
75

scroll-manager

A handler for scrolling inside elements with different eases
JavaScript
19
star
76

touch-pinch

minimal two-finger pinch gesture detection
JavaScript
19
star
77

three-orbit-viewer

quick harness for viewing a mesh with orbit viewer
JavaScript
18
star
78

css-transform-to-mat4

Will take a string which is a css transform value (2d or 3d) and return a mat4 or 3d transformation matrix from the string.
JavaScript
17
star
79

gl-shader-output

test a shader's gl_FragColor output on a 1x1 canvas
JavaScript
17
star
80

gh-api-stream

streams JSON content from a GitHub API
JavaScript
17
star
81

uploadr

CLI tool which uploads a folder via SFTP
JavaScript
17
star
82

camera-spin

Mouse/touch-draggable first-person camera
JavaScript
17
star
83

exif-orientation-image

Properly displays an image via canvas based on the exif orientation data.
JavaScript
16
star
84

jam3-lessons-react

Quick and brief reference for React development
15
star
85

preview-dds

preview and save DDS textures from the command line
JavaScript
15
star
86

meetup-creative-coding-webgl

A WebGL experiment created for Jam3's Creative Coding Meetup on October 23rd 2019.
JavaScript
14
star
87

detect-audio-autoplay

detects whether the browser can auto-play audio
JavaScript
14
star
88

ae-threejs-multichannel-sdf

A signed distance field Effect plugin for Adobe After Effects
C
13
star
89

google-maps-image-api-url

This module will return a string which is a url to load an image from the Google Maps Image API
JavaScript
13
star
90

webgl-to-canvas2d

Convert a webgl context or webgl canvas into a 2d canvas
JavaScript
13
star
91

f1-dom

Create f1 ui with the dom
JavaScript
12
star
92

nyg

Not another yeoman generator, a simplified project generator based around prompts and events.
JavaScript
12
star
93

slot-machine-button

🎰 React Slot Machine Button
JavaScript
12
star
94

camera-picking-ray

creates a picking ray for a 2D/3D camera
JavaScript
11
star
95

scroll-bar-width

Detect browser scrollbar size
JavaScript
11
star
96

nyg-jam3

Second generation of the Jam3 Generator, many new features and breaking change features
JavaScript
11
star
97

google-assistant-21-days-of-gratitude

JavaScript
11
star
98

awwwards-stream

scrape Awwwards data
JavaScript
11
star
99

adviser

Jam3 quality advisor. Integrates checking for best practices at Jam3
JavaScript
11
star
100

google-assistant-greeting-cards

JavaScript
10
star