├── .gitignore
├── 01-phong-lighting
└── shader.frag
├── 02-circle-sdf
└── shader.frag
├── 03-value-noise
└── shader.frag
├── 04-worley-voronoi-noise
└── shader.frag
├── 05-perlin-noise
└── shader.frag
├── 06-fbm-dw-normals
└── shader.frag
├── 07-ray-marching
└── shader.frag
├── 08-checkerboard
├── fast-shader.frag
└── slow-shader.frag
├── 09-gaussian-blur
├── mario.png
└── shader.frag
├── 10-piano
└── piano.frag
├── LICENSE
├── README.md
├── _thumbnails
├── glsl_00.png
├── glsl_01.png
├── glsl_02.png
├── glsl_03.png
├── glsl_04.png
├── glsl_05.png
├── glsl_06.png
└── glsl_07.png
└── common
└── functions.glsl
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
--------------------------------------------------------------------------------
/01-phong-lighting/shader.frag:
--------------------------------------------------------------------------------
1 | precision lowp float;
2 |
3 | varying vec4 v_normal;
4 |
5 | void main() {
6 | // ambient lighting (global illuminance)
7 | vec3 ambient = vec3(0.5, 0.5, 0.5); // color - grey
8 |
9 | // diffuse (lambertian) lighting
10 | // lightColor, lightSource, normal, diffuseStrength
11 | vec3 normal = normalize(v_normal.xyz);
12 | vec3 lightColor = vec3(1.0, 1.0, 1.0); // color - white
13 | vec3 lightSource = vec3(1.0, 1.0, 1.0); // coord - (1, 0, 0)
14 | float diffuseStrength = max(0.0, dot(lightSource, normal));
15 | vec3 diffuse = diffuseStrength * lightColor;
16 |
17 | // specular light
18 | // lightColor, lightSource, normal, specularStrength, viewSource
19 | vec3 cameraSource = vec3(0.0, 0.0, 1.0);
20 | vec3 viewSource = normalize(cameraSource);
21 | vec3 reflectSource = normalize(reflect(-lightSource, normal));
22 | float specularStrength = max(0.0, dot(viewSource, reflectSource));
23 | specularStrength = pow(specularStrength, 256.0);
24 | vec3 specular = specularStrength * lightColor;
25 |
26 | // lighting = ambient + diffuse + specular
27 | vec3 lighting = vec3(0.0, 0.0, 0.0); // color - black
28 | // lighting = ambient;
29 | // lighting = ambient * 0.0 + diffuse;
30 | // lighting = ambient * 0.0 + diffuse * 0.0 + specular;
31 | lighting = ambient * 0.0 + diffuse * 0.5 + specular * 0.5;
32 |
33 | // color = modelColor * lighting
34 | vec3 modelColor = vec3(0.75, 0.75, 0.75);
35 | vec3 color = modelColor * lighting;
36 |
37 | gl_FragColor = vec4(color, 1.0);
38 | }
--------------------------------------------------------------------------------
/02-circle-sdf/shader.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform float u_time;
4 | uniform vec2 u_resolution;
5 |
6 | // https://iquilezles.org/articles/distfunctions2d/
7 | float sdfCircle(vec2 p, float r) {
8 | // note: sqrt(pow(p.x, 2.0) + pow(p.y, 2.0)) - r;
9 | return length(p) - r;
10 | }
11 |
12 | void main() {
13 | // note: set up uv coordinates
14 | vec2 uv = gl_FragCoord.xy / u_resolution;
15 | uv = uv - 0.5;
16 | uv = uv * u_resolution / 100.0;
17 |
18 | // note: set up basic colors
19 | vec3 black = vec3(0.0);
20 | vec3 white = vec3(1.0);
21 | vec3 red = vec3(1.0, 0.0, 0.0);
22 | vec3 blue = vec3(0.65, 0.85, 1.0);
23 | vec3 orange = vec3(0.9, 0.6, 0.3);
24 | vec3 color = black;
25 | color = vec3(uv.x, uv.y, 0.0);
26 |
27 | // note: draw circle sdf
28 | float radius = 2.5;
29 | // radius = 3.0;
30 | vec2 center = vec2(0.0, 0.0);
31 | // center = vec2(sin(2.0 * u_time), 0.0);
32 | float distanceToCircle = sdfCircle(uv - center, radius);
33 | color = distanceToCircle > 0.0 ? orange : blue;
34 |
35 | // note: adding a black outline to the circle
36 | // color = color * exp(distanceToCircle);
37 | // color = color * exp(2.0 * distanceToCircle);
38 | // color = color * exp(-2.0 * abs(distanceToCircle));
39 | color = color * (1.0 - exp(-2.0 * abs(distanceToCircle)));
40 | // color = color * (1.0 - exp(-5.0 * abs(distanceToCircle)));
41 | // color = color * (1.0 - exp(-5.0 * abs(distanceToCircle)));
42 |
43 | // note: adding waves
44 | // color = color * 0.8 + color * 0.2;
45 | // color = color * 0.8 + color * 0.2 * sin(distanceToCircle);
46 | // color = color * 0.8 + color * 0.2 * sin(50.0 * distanceToCircle);
47 | color = color * 0.8 + color * 0.2 * sin(50.0 * distanceToCircle - 4.0 * u_time);
48 |
49 | // note: adding white border to the circle
50 | // color = mix(white, color, step(0.1, distanceToCircle));
51 | // color = mix(white, color, step(0.1, abs(distanceToCircle)));
52 | color = mix(white, color, smoothstep(0.0, 0.1, abs(distanceToCircle)));
53 |
54 | // note: thumbnail?
55 | // color = mix(white, color, abs(distanceToCircle));
56 | // color = mix(white, color, 2.0 * abs(distanceToCircle));
57 | // color = mix(white, color, 4.0 * abs(distanceToCircle));
58 |
59 | gl_FragColor = vec4(color, 1.0);
60 | }
61 |
--------------------------------------------------------------------------------
/03-value-noise/shader.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform float u_time;
4 | uniform vec2 u_resolution;
5 |
6 | vec2 cubic(vec2 p) {
7 | return p * p * (3.0 - 2.0 * p);
8 | }
9 |
10 | vec2 quintic(vec2 p) {
11 | return p * p * p * (10.0 + p * (-15.0 + p * 6.0));
12 | }
13 |
14 | float whiteNoise2x1(vec2 p) {
15 | // return p.x;
16 |
17 | // return fract(p.x * p.y * 1000.0123);
18 |
19 | // generic noise function - replace with something better
20 | // float random = dot(p, vec2(12.9898, 78.233));
21 | float random = dot(p, vec2(12., 78.));
22 | random = sin(random);
23 | random = random * 43758.5453;
24 | random = fract(random);
25 | return random;
26 | }
27 |
28 | float valueNoiseFn(vec2 uv) {
29 | vec2 gridUv = fract(uv);
30 | vec2 gridId = floor(uv);
31 |
32 | gridUv = quintic(gridUv);
33 |
34 | float botLeft = whiteNoise2x1(gridId);
35 | float botRight = whiteNoise2x1(gridId + vec2(1.0, 0.0));
36 | float b = mix(botLeft, botRight, gridUv.x);
37 |
38 | float topLeft = whiteNoise2x1(gridId + vec2(0.0, 1.0));
39 | float topRight = whiteNoise2x1(gridId + vec2(1.0, 1.0));
40 | float t = mix(topLeft, topRight, gridUv.x);
41 |
42 | float noise = mix(b, t, gridUv.y);
43 |
44 | return noise;
45 | }
46 |
47 | void main() {
48 | vec2 uv = gl_FragCoord.xy / u_resolution;
49 | uv = gl_FragCoord.xy / u_resolution.y;
50 |
51 | vec3 color = vec3(1.0);
52 |
53 | // part 1 - create white noise function
54 | // color = vec3(whiteNoise2x1(uv));
55 |
56 | // part 2.1 - add hidden grid overlay
57 | // uv = uv * 2.0;
58 | // uv = uv * 4.0;
59 | // uv = uv * 8.0;
60 | // vec2 gridUv = fract(uv);
61 | // color = vec3(gridUv, 0.0);
62 |
63 | // part 2.2 - set up grid ids
64 | // vec2 gridId = floor(uv);
65 | // color = vec3(gridId, 0.0);
66 | // color = vec3(gridId, 0.0) * 0.25;
67 |
68 | // part 4 - smoothstep uv coordinates to fix rough edges
69 | // gridUv = smoothstep(0.0, 1.0, gridUv);
70 | // gridUv = cubic(gridUv);
71 | // gridUv = quintic(gridUv);
72 |
73 | // part 3.1 - lerp between bottom two coordinates
74 | // float botLeft = whiteNoise2x1(gridId);
75 | // float botRight = whiteNoise2x1(gridId + vec2(1.0, 0.0));
76 | // float b = mix(botLeft, botRight, gridUv.x);
77 | // color = vec3(b);
78 |
79 | // part 3.2 - lerp between top two coordinates
80 | // float topLeft = whiteNoise2x1(gridId + vec2(0.0, 1.0));
81 | // float topRight = whiteNoise2x1(gridId + vec2(1.0, 1.0));
82 | // float t = mix(topLeft, topRight, gridUv.x);
83 | // color = vec3(t);
84 |
85 | // part 3.3 - lerp between top and bottom based on y axis
86 | // float valueNoise = mix(b, t, gridUv.y);
87 | // color = vec3(valueNoise);
88 |
89 | // part 5 - add layers (a.k.a octaves) of value noise
90 | uv += u_time / 10.0;
91 | float vn = valueNoiseFn(uv * 4.0) * 1.0;
92 | vn += valueNoiseFn(uv * 8.0) * 0.5;
93 | vn += valueNoiseFn(uv * 16.0) * 0.25;
94 | vn += valueNoiseFn(uv * 32.0) * 0.125;
95 | vn += valueNoiseFn(uv * 64.0) * 0.0625;
96 | vn /= 2.0;
97 | color = vec3(vn);
98 |
99 | gl_FragColor = vec4(color, 1.0);
100 | }
--------------------------------------------------------------------------------
/04-worley-voronoi-noise/shader.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform float u_time;
4 | uniform vec2 u_resolution;
5 |
6 | vec2 noise2x2(vec2 p) {
7 | float x = dot(p, vec2(123.4, 234.5));
8 | float y = dot(p, vec2(345.6, 456.7));
9 | vec2 noise = vec2(x, y);
10 | noise = sin(noise);
11 | noise = noise * 43758.5453;
12 | noise = fract(noise);
13 | return noise;
14 | }
15 |
16 | void main() {
17 | vec2 uv = gl_FragCoord.xy / u_resolution;
18 | uv = gl_FragCoord.xy / u_resolution.y;
19 |
20 | vec3 color = vec3(0.0);
21 |
22 | // part 1.1 - set up the grid
23 | // clouds thumbnail -> uv = uv * 3.0;
24 | uv = uv * 8.0;
25 | vec2 currentGridId = floor(uv);
26 | vec2 currentGridCoord = fract(uv);
27 | color = vec3(currentGridCoord, 0.0);
28 | currentGridCoord = currentGridCoord - 0.5;
29 | color = vec3(currentGridCoord, 0.0);
30 |
31 | // part 1.2 - add red grid to help visualize voronoi noise
32 | vec2 redGridUv = currentGridCoord;
33 | redGridUv = abs(redGridUv);
34 | float distToEdgeOfGridCell = 2.0 * max(redGridUv.x, redGridUv.y);
35 |
36 | // part 1.3 - uncomment one line at a time for explanation
37 | color = vec3(distToEdgeOfGridCell);
38 | color = vec3(smoothstep(0.5, 1.0, distToEdgeOfGridCell));
39 | color = vec3(smoothstep(0.9, 1.0, distToEdgeOfGridCell), 0.0, 0.0);
40 |
41 | // part 1.4 - store this variable for later use
42 | vec3 redGridColor = vec3(smoothstep(0.9, 1.0, distToEdgeOfGridCell), 0.0, 0.0);
43 |
44 | // part 2.1 - add a point at the center of each grid
45 | float pointsOnGrid = 0.0;
46 | float minDistFromPixel = 100.0;
47 |
48 | for (float i = -1.0; i <= 1.0; i++) {
49 | for (float j = -1.0; j <= 1.0; j++) {
50 | vec2 adjGridCoords = vec2(i, j);
51 | vec2 pointOnAdjGrid = adjGridCoords;
52 |
53 | // part 3.1 - vary the points based on time + noise
54 | // pointOnAdjGrid = adjGridCoords + sin(u_time) * 0.5;
55 | vec2 noise = noise2x2(currentGridId + adjGridCoords);
56 | pointOnAdjGrid = adjGridCoords + sin(u_time * noise) * 0.5;
57 |
58 | float dist = length(currentGridCoord - pointOnAdjGrid);
59 | minDistFromPixel = min(dist, minDistFromPixel);
60 |
61 | pointsOnGrid += smoothstep(0.95, 0.96, 1.0 - dist);
62 | }
63 | }
64 |
65 | // part 2.2 - draw white points on grid for reference
66 | vec3 pointsOnGridColor = vec3(pointsOnGrid);
67 | color = redGridColor + pointsOnGridColor;
68 | color = redGridColor + pointsOnGridColor + minDistFromPixel;
69 | color = redGridColor + minDistFromPixel;
70 |
71 | // part 3.2 - display voronoi noise
72 | color = vec3(minDistFromPixel);
73 | color = vec3(smoothstep(0.0, 1.0, minDistFromPixel));
74 |
75 | // part 3.3 - display clouds
76 | color = vec3(smoothstep(0.25, 1.0, 1.0 - minDistFromPixel));
77 |
78 | // part 3.4 - display clouds with dots
79 | color = vec3(smoothstep(0.2, 1.0, 1.0 - minDistFromPixel)) - pointsOnGridColor;
80 |
81 | // part 4 - create display for final section
82 | // if (uv.x > 4.745) {
83 | // color = pointsOnGridColor + vec3(smoothstep(0.0, 1.0, minDistFromPixel));
84 | // } else {
85 | // color = vec3(smoothstep(0.1, 1.0, 1.0 - minDistFromPixel)) - pointsOnGridColor;
86 | // }
87 |
88 | gl_FragColor = vec4(color, 1.0);
89 | }
--------------------------------------------------------------------------------
/05-perlin-noise/shader.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform float u_time;
4 | uniform vec2 u_resolution;
5 |
6 | vec2 randomGradient(vec2 p) {
7 | p = p + 0.02;
8 | float x = dot(p, vec2(123.4, 234.5));
9 | float y = dot(p, vec2(234.5, 345.6));
10 | vec2 gradient = vec2(x, y);
11 | gradient = sin(gradient);
12 | gradient = gradient * 43758.5453;
13 |
14 | // part 4.5 - update noise function with time
15 | gradient = sin(gradient + u_time);
16 | return gradient;
17 |
18 | // gradient = sin(gradient);
19 | // return gradient;
20 | }
21 |
22 | // inigo quilez - https://iquilezles.org/articles/distfunctions2d/
23 | float sdfCircle(in vec2 p, in float r) {
24 | return length(p) - r;
25 | }
26 |
27 | // inigo quilez - https://iquilezles.org/articles/distfunctions2d/
28 | float sdfOrientedBox(in vec2 p, in vec2 a, in vec2 b, float th) {
29 | float l = length(b - a);
30 | vec2 d = (b - a) / l;
31 | vec2 q = (p - (a + b) * 0.5);
32 | q = mat2(d.x, -d.y, d.y, d.x) * q;
33 | q = abs(q) - vec2(l, th) * 0.5;
34 | return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0);
35 | }
36 |
37 | vec2 cubic(vec2 p) {
38 | return p * p * (3.0 - p * 2.0);
39 | }
40 |
41 | vec2 quintic(vec2 p) {
42 | return p * p * p * (10.0 + p * (-15.0 + p * 6.0));
43 | }
44 |
45 | void main() {
46 | // part 0 - basic shader setup
47 | vec2 uv = gl_FragCoord.xy / u_resolution;
48 |
49 | // uncomment for final final demo
50 | uv = gl_FragCoord.xy / u_resolution.y;
51 |
52 | vec3 black = vec3(0.0);
53 | vec3 white = vec3(1.0);
54 | vec3 color = black;
55 |
56 | // part 1 - set up a grid of cells
57 | uv = uv * 4.0;
58 | vec2 gridId = floor(uv);
59 | vec2 gridUv = fract(uv);
60 | color = vec3(gridId, 0.0);
61 | color = vec3(gridUv, 0.0);
62 |
63 | // part 2.1 - start by finding the coords of grid corners
64 | vec2 bl = gridId + vec2(0.0, 0.0);
65 | vec2 br = gridId + vec2(1.0, 0.0);
66 | vec2 tl = gridId + vec2(0.0, 1.0);
67 | vec2 tr = gridId + vec2(1.0, 1.0);
68 |
69 | // part 2.2 - find random gradient for each grid corner
70 | vec2 gradBl = randomGradient(bl);
71 | vec2 gradBr = randomGradient(br);
72 | vec2 gradTl = randomGradient(tl);
73 | vec2 gradTr = randomGradient(tr);
74 |
75 | // part 2.3 - visualize gradients (for demo purposes)
76 | vec2 gridCell = gridId + gridUv;
77 | float distG1 = sdfOrientedBox(gridCell, bl, bl + gradBl / 2.0, 0.02);
78 | float distG2 = sdfOrientedBox(gridCell, br, br + gradBr / 2.0, 0.02);
79 | float distG3 = sdfOrientedBox(gridCell, tl, tl + gradTl / 2.0, 0.02);
80 | float distG4 = sdfOrientedBox(gridCell, tr, tr + gradTr / 2.0, 0.02);
81 | if (distG1 < 0.0 || distG2 < 0.0 || distG3 < 0.0 || distG4 < 0.0) {
82 | color = vec3(1.0);
83 | }
84 |
85 | // part 3.1 - visualize a single center pixel on each grid cell
86 | float circleRadius = 0.025;
87 | vec2 circleCenter = vec2(0.5, 0.5);
88 | float distToCircle = sdfCircle(gridUv - circleCenter, circleRadius);
89 | color = distToCircle > 0.0 ? color : white;
90 |
91 | // part 3.2 - find distance from current pixel to each grid corner
92 | vec2 distFromPixelToBl = gridUv - vec2(0.0, 0.0);
93 | vec2 distFromPixelToBr = gridUv - vec2(1.0, 0.0);
94 | vec2 distFromPixelToTl = gridUv - vec2(0.0, 1.0);
95 | vec2 distFromPixelToTr = gridUv - vec2(1.0, 1.0);
96 |
97 | // part 4.1 - calculate the dot products of gradients + distances
98 | float dotBl = dot(gradBl, distFromPixelToBl);
99 | float dotBr = dot(gradBr, distFromPixelToBr);
100 | float dotTl = dot(gradTl, distFromPixelToTl);
101 | float dotTr = dot(gradTr, distFromPixelToTr);
102 |
103 | // part 4.4 - smooth out gridUvs
104 | // gridUv = smoothstep(0.0, 1.0, gridUv);
105 | // gridUv = cubic(gridUv);
106 | gridUv = quintic(gridUv);
107 |
108 | // part 4.2 - perform linear interpolation between 4 dot products
109 | float b = mix(dotBl, dotBr, gridUv.x);
110 | float t = mix(dotTl, dotTr, gridUv.x);
111 | float perlin = mix(b, t, gridUv.y);
112 |
113 | // part 4.3 - display perlin noise
114 | color = vec3(perlin + 0.2);
115 | // color = distToCircle > 0.0 ? color : white;
116 | // if (distG1 < 0.0 || distG2 < 0.0 || distG3 < 0.0 || distG4 < 0.0) {
117 | // color = vec3(1.0);
118 | // }
119 |
120 | // part 4.5 - update randomGradient function with time
121 |
122 | // part 5.1 - billow noise
123 | // float billow = abs(perlin);
124 | // color = vec3(billow);
125 |
126 | // part 5.2 - ridged noise
127 | // float ridgedNoise = 1.0 - abs(perlin);
128 | // ridgedNoise = ridgedNoise * ridgedNoise;
129 | // color = vec3(ridgedNoise);
130 |
131 | gl_FragColor = vec4(color, 1.0);
132 | }
133 |
--------------------------------------------------------------------------------
/06-fbm-dw-normals/shader.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform float u_time;
4 | uniform vec2 u_resolution;
5 |
6 | vec2 randomGradient(vec2 p) {
7 | float x = dot(p, vec2(123.4, 234.5));
8 | float y = dot(p, vec2(234.5, 345.6));
9 | vec2 gradient = vec2(x, y);
10 | gradient = sin(gradient);
11 | gradient = gradient * 43758.5453;
12 | // gradient = sin(gradient);
13 | gradient = sin(gradient + u_time);
14 | return gradient;
15 | }
16 |
17 | vec2 quintic(vec2 p) {
18 | return p * p * p * (10.0 + p * (-15.0 + p * 6.0));
19 | }
20 |
21 | float perlinNoise(vec2 uv) {
22 | vec2 gridId = floor(uv);
23 | vec2 gridUv = fract(uv);
24 |
25 | vec2 bl = gridId + vec2(0.0, 0.0);
26 | vec2 br = gridId + vec2(1.0, 0.0);
27 | vec2 tl = gridId + vec2(0.0, 1.0);
28 | vec2 tr = gridId + vec2(1.0, 1.0);
29 |
30 | vec2 g1 = randomGradient(bl);
31 | vec2 g2 = randomGradient(br);
32 | vec2 g3 = randomGradient(tl);
33 | vec2 g4 = randomGradient(tr);
34 |
35 | vec2 distFromBl = gridUv - vec2(0.0, 0.0);
36 | vec2 distFromBr = gridUv - vec2(1.0, 0.0);
37 | vec2 distFromTl = gridUv - vec2(0.0, 1.0);
38 | vec2 distFromTr = gridUv - vec2(1.0, 1.0);
39 |
40 | float d1 = dot(g1, distFromBl);
41 | float d2 = dot(g2, distFromBr);
42 | float d3 = dot(g3, distFromTl);
43 | float d4 = dot(g4, distFromTr);
44 |
45 | gridUv = quintic(gridUv);
46 |
47 | float bot = mix(d1, d2, gridUv.x);
48 | float top = mix(d3, d4, gridUv.x);
49 | float pNoise = mix(bot, top, gridUv.y);
50 |
51 | return pNoise + 0.1;
52 | }
53 |
54 | float fbmPerlinNoise(vec2 uv) {
55 | float fbmNoise = 0.0;
56 | float amplitude = 1.0;
57 | // const float octaves = 1.0;
58 | const float octaves = 2.0;
59 | // const float octaves = 3.0;
60 | // const float octaves = 4.0;
61 | // const float octaves = 5.0;
62 |
63 | for (float i = 0.0; i < octaves; i++) {
64 | fbmNoise = fbmNoise + perlinNoise(uv) * amplitude;
65 | amplitude = amplitude * 0.5;
66 | uv = uv * 2.0;
67 | }
68 |
69 | // fbmNoise = fbmNoise / 2.0;
70 |
71 | return fbmNoise;
72 | }
73 |
74 | float domainWarpFbmPerlinNoise(vec2 uv) {
75 | // return fbmPerlinNoise(uv);
76 |
77 | float fbm1 = fbmPerlinNoise(uv + vec2(0.0, 0.0));
78 | float fbm2 = fbmPerlinNoise(uv + vec2(5.2, 1.3));
79 | return fbmPerlinNoise(vec2(fbm1, fbm2));
80 |
81 | float fbm3 = fbmPerlinNoise(uv + 4.0 * fbm1 + vec2(1.7, 9.2));
82 | float fbm4 = fbmPerlinNoise(uv + 4.0 * fbm2 + vec2(8.3, 2.8));
83 | return fbmPerlinNoise(vec2(fbm3, fbm4));
84 | }
85 |
86 | vec3 calcNormal(vec2 uv) {
87 | float diff = 0.001;
88 | float p1 = domainWarpFbmPerlinNoise(uv + vec2(diff, 0.0));
89 | float p2 = domainWarpFbmPerlinNoise(uv - vec2(diff, 0.0));
90 | float p3 = domainWarpFbmPerlinNoise(uv + vec2(0.0, diff));
91 | float p4 = domainWarpFbmPerlinNoise(uv - vec2(0.0, diff));
92 |
93 | vec3 normal = normalize(vec3(p1 - p2, p3 - p4, 0.001));
94 | return normal;
95 | }
96 |
97 | void main() {
98 | vec2 uv = gl_FragCoord.xy / u_resolution;
99 | uv = gl_FragCoord.xy / u_resolution.y;
100 |
101 | vec3 color = vec3(0.0);
102 |
103 | uv = uv * 4.0;
104 | float pNoise = perlinNoise(uv);
105 | color = vec3(pNoise);
106 |
107 | // part 1 - fractional brownian motion
108 | float fbmNoise = fbmPerlinNoise(uv);
109 | // color = vec3(fbmNoise);
110 |
111 | // part 2 - domain warping
112 | float dwNoise = domainWarpFbmPerlinNoise(uv);
113 | color = vec3(dwNoise);
114 |
115 | // part 3.1 - central differences method
116 | vec3 normal = calcNormal(uv);
117 |
118 | // part 3.2 - diffuse lighting
119 | vec3 white = vec3(1.0);
120 | vec3 blue = vec3(0.65, 0.85, 1.0);
121 | vec3 lightColor = white;
122 | lightColor = blue;
123 | vec3 lightSource = vec3(1.0, 1.0, 1.0);
124 | float diffuseStrength = max(0.0, dot(normal, lightSource));
125 | vec3 diffuse = diffuseStrength * lightColor;
126 | vec3 lighting = diffuse * 0.5;
127 | color = lighting;
128 |
129 | // part 3.3 - specular lighting
130 | vec3 cameraSource = vec3(0.0, 0.0, 1.0);
131 | vec3 viewSource = normalize(cameraSource);
132 | vec3 reflectSource = normalize(reflect(-lightSource, normal));
133 | float specularStrength = max(0.0, dot(viewSource, reflectSource));
134 | specularStrength = pow(specularStrength, 32.0);
135 | vec3 specular = specularStrength * lightColor;
136 |
137 | lighting = diffuse * 0.5 + specular * 0.5;
138 | color = lighting;
139 |
140 | gl_FragColor = vec4(color, 1.0);
141 | }
--------------------------------------------------------------------------------
/07-ray-marching/shader.frag:
--------------------------------------------------------------------------------
1 | // Helful Resources
2 | // ----------------
3 | // Ray Marching Blog Post by Michael Walczyk
4 | // https://michaelwalczyk.com/blog-ray-marching.html
5 | // Inigo Quilez SDF Functions
6 | // https://iquilezles.org/articles/distfunctions/
7 |
8 | precision mediump float;
9 |
10 | uniform float u_time;
11 | uniform vec2 u_resolution;
12 |
13 | const float NUM_OF_STEPS = 128.0;
14 | const float MIN_DIST_TO_SDF = 0.001;
15 | const float MAX_DIST_TO_TRAVEL = 64.0;
16 |
17 | float opSmoothUnion(float d1, float d2, float k) {
18 | float h = clamp(0.5 + 0.5 * (d2 - d1) / k, 0.0, 1.0);
19 | return mix(d2, d1, h) - k * h * (1.0 - h);
20 | }
21 |
22 | float sdfPlane(vec3 p, vec3 n, float h) {
23 | // n must be normalized
24 | return dot(p, n) + h;
25 | }
26 |
27 | float sdfSphere(vec3 p, vec3 c, float r) {
28 | return length(p - c) - r;
29 | }
30 |
31 | float map(vec3 p) {
32 | float radius = 0.75;
33 | vec3 center = vec3(0.0);
34 |
35 | // part 4 - change height of the sphere based on time
36 | center = vec3(0.0, -0.25 + sin(u_time) * 0.5, 0.0);
37 |
38 | float sphere = sdfSphere(p, center, radius);
39 | float m = sphere;
40 |
41 | // part 1.2 - display plane
42 | float h = 1.0;
43 | vec3 normal = vec3(0.0, 1.0, 0.0);
44 | float plane = sdfPlane(p, normal, h);
45 | m = min(sphere, plane);
46 |
47 | // part 4 - add smooth blending
48 | m = opSmoothUnion(sphere, plane, 0.5);
49 |
50 | return m;
51 | }
52 |
53 | float rayMarch(vec3 ro, vec3 rd, float maxDistToTravel) {
54 | float dist = 0.0;
55 |
56 | for (float i = 0.0; i < NUM_OF_STEPS; i++) {
57 | vec3 currentPos = ro + rd * dist;
58 | float distToSdf = map(currentPos);
59 |
60 | if (distToSdf < MIN_DIST_TO_SDF) {
61 | break;
62 | }
63 |
64 | dist = dist + distToSdf;
65 |
66 | if (dist > maxDistToTravel) {
67 | break;
68 | }
69 | }
70 |
71 | return dist;
72 | }
73 |
74 | vec3 getNormal(vec3 p) {
75 | vec2 d = vec2(0.01, 0.0);
76 | float gx = map(p + d.xyy) - map(p - d.xyy);
77 | float gy = map(p + d.yxy) - map(p - d.yxy);
78 | float gz = map(p + d.yyx) - map(p - d.yyx);
79 | vec3 normal = vec3(gx, gy, gz);
80 | return normalize(normal);
81 | }
82 |
83 | vec3 render(vec2 uv) {
84 | vec3 color = vec3(0.0);
85 |
86 | // note: ro -> ray origin, rd -> ray direction
87 | vec3 ro = vec3(0.0, 0.0, -2.0);
88 | vec3 rd = vec3(uv, 1.0);
89 |
90 | float dist = rayMarch(ro, rd, MAX_DIST_TO_TRAVEL);
91 |
92 | if (dist < MAX_DIST_TO_TRAVEL) {
93 | // part 1 - display ray marching result
94 | color = vec3(1.0);
95 |
96 | // part 2.1 - calculate normals
97 | // calculate normals at the exact point where we hit SDF
98 | vec3 p = ro + rd * dist;
99 | vec3 normal = getNormal(p);
100 | color = normal;
101 |
102 | // part 2.2 - add lighting
103 |
104 | // part 2.2.1 - calculate diffuse lighting
105 | vec3 lightColor = vec3(1.0);
106 | vec3 lightSource = vec3(2.5, 2.5, -1.0);
107 | float diffuseStrength = max(0.0, dot(normalize(lightSource), normal));
108 | vec3 diffuse = lightColor * diffuseStrength;
109 |
110 | // part 2.2.2 - calculate specular lighting
111 | vec3 viewSource = normalize(ro);
112 | vec3 reflectSource = normalize(reflect(-lightSource, normal));
113 | float specularStrength = max(0.0, dot(viewSource, reflectSource));
114 | specularStrength = pow(specularStrength, 64.0);
115 | vec3 specular = specularStrength * lightColor;
116 |
117 | // part 2.2.3 - calculate lighting
118 | vec3 lighting = diffuse * 0.75 + specular * 0.25;
119 | color = lighting;
120 |
121 | // part 3 - add shadows
122 |
123 | // part 3.1 - update the ray origin and ray direction
124 | vec3 lightDirection = normalize(lightSource);
125 | float distToLightSource = length(lightSource - p);
126 | ro = p + normal * 0.1;
127 | rd = lightDirection;
128 |
129 | // part 3.2 - ray march based on new ro + rd
130 | float dist = rayMarch(ro, rd, distToLightSource);
131 | if (dist < distToLightSource) {
132 | color = color * vec3(0.25);
133 | }
134 |
135 | // note: add gamma correction
136 | color = pow(color, vec3(1.0 / 2.2));
137 | }
138 |
139 | return color;
140 | }
141 |
142 | void main() {
143 | vec2 uv = 2.0 * gl_FragCoord.xy / u_resolution - 1.0;
144 | // note: properly center the shader in full screen mode
145 | // uv = (2.0 * gl_FragCoord.xy - u_resolution) / u_resolution.y;
146 | vec3 color = vec3(0.0);
147 | color = render(uv);
148 | // color = vec3(uv, 0.0);
149 | gl_FragColor = vec4(color, 1.0);
150 | }
--------------------------------------------------------------------------------
/08-checkerboard/fast-shader.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform float u_time;
4 | uniform vec2 u_resolution;
5 |
6 | void main() {
7 | vec3 black = vec3(0.0);
8 | vec3 white = vec3(1.0);
9 | vec3 color = black;
10 |
11 | vec2 uv = 2.0 * gl_FragCoord.xy / u_resolution - 1.0;
12 | uv.x = uv.x * u_resolution.x / u_resolution.y;
13 | color = vec3(uv, 0.0);
14 |
15 | float boardSize = 4.0;
16 | uv = uv * boardSize;
17 | color = vec3(uv, 0.0);
18 |
19 | vec2 gridUv = fract(uv);
20 | vec2 gridId = floor(uv);
21 | color = vec3(gridUv, 0.0);
22 | color = vec3(gridId, 0.0);
23 | color = vec3(gridId * 0.2, 0.0);
24 |
25 | if (mod(gridId.x + gridId.y, 2.0) <= 0.01) {
26 | color = white;
27 | } else {
28 | color = black;
29 | }
30 |
31 | gl_FragColor = vec4(color, 1.0);
32 | }
--------------------------------------------------------------------------------
/08-checkerboard/slow-shader.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform float u_time;
4 | uniform vec2 u_resolution;
5 |
6 | // reference: https://iquilezles.org/articles/distfunctions2d/
7 | float sdBox(in vec2 p, in vec2 b) {
8 | vec2 d = abs(p) - b;
9 | return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
10 | }
11 |
12 | void main() {
13 | vec2 uv = 2.0 * gl_FragCoord.xy / u_resolution - 1.0;
14 | uv.x = uv.x * u_resolution.x / u_resolution.y;
15 |
16 | vec3 grey = vec3(0.5);
17 | vec3 black = vec3(0.0);
18 | vec3 white = vec3(1.0);
19 |
20 | vec3 color = grey;
21 |
22 | const float halfBoardSize = 4.0;
23 | float boxWidth = 1.0 / (halfBoardSize * 4.0);
24 | vec2 boxDimensions = vec2(boxWidth, boxWidth);
25 | float separationSize = boxWidth + 0.01;
26 |
27 | for (float i = -halfBoardSize; i <= halfBoardSize; i++) {
28 | for (float j = -halfBoardSize; j <= halfBoardSize; j++) {
29 | vec2 center = vec2(i, j) * (boxWidth + separationSize);
30 | float distToBox = sdBox(uv - center, boxDimensions);
31 |
32 | if (mod(i + j, 2.0) <= 0.01) {
33 | color = distToBox < 0.0 ? white : color;
34 | } else {
35 | color = distToBox < 0.0 ? black : color;
36 | }
37 | }
38 | }
39 |
40 | gl_FragColor = vec4(color, 1.0);
41 | }
--------------------------------------------------------------------------------
/09-gaussian-blur/mario.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuboptimalEng/shader-tutorials/240917555b5de1e2b57ad69e49a6b5e36a875669/09-gaussian-blur/mario.png
--------------------------------------------------------------------------------
/09-gaussian-blur/shader.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | uniform vec2 u_resolution;
4 | uniform sampler2D u_texture_0;
5 |
6 | void main() {
7 | vec2 uv = gl_FragCoord.xy / u_resolution;
8 | uv.x = uv.x * u_resolution.x / u_resolution.y;
9 |
10 | vec3 color = vec3(0.0);
11 | color = vec3(uv, 0.0);
12 |
13 | // note: add this to VS Code settings.json when using the glsl canvas extension
14 | // "glsl-canvas.textures": {
15 | // "0": "./shader-tutorials/09-gaussian-blur/mario.png",
16 | // },
17 |
18 | vec4 texture = texture2D(u_texture_0, uv);
19 | color = texture.rgb;
20 |
21 | vec2 imageResolution = vec2(2832, 2744);
22 | imageResolution = imageResolution / 10.0;
23 | vec2 texelSize = 1.0 / imageResolution;
24 |
25 | // const float kernelSize = 0.0;
26 | const float kernelSize = 1.0;
27 | // const float kernelSize = 2.0;
28 | // const float kernelSize = 3.0;
29 | vec3 boxBlurColor = vec3(0.0);
30 | // note: if kernelSize == 1.0, then boxBlurDivisor == 9.0
31 | float boxBlurDivisor = pow(2.0 * kernelSize + 1.0, 2.0);
32 | for (float i = -kernelSize; i <= kernelSize; i++) {
33 | for (float j = -kernelSize; j <= kernelSize; j++) {
34 | vec4 texture = texture2D(u_texture_0, uv + vec2(i, j) * texelSize);
35 | boxBlurColor = boxBlurColor + texture.rgb;
36 | }
37 | }
38 | boxBlurColor = boxBlurColor / boxBlurDivisor;
39 | color = boxBlurColor;
40 |
41 | float gaussianDivisor = 16.0;
42 | vec3 gaussianBlurColor = vec3(0.0);
43 | gaussianBlurColor += texture2D(u_texture_0, uv + vec2(-1, 1) * texelSize).rgb * 1.0;
44 | gaussianBlurColor += texture2D(u_texture_0, uv + vec2(0, 1) * texelSize).rgb * 2.0;
45 | gaussianBlurColor += texture2D(u_texture_0, uv + vec2(1, 1) * texelSize).rgb * 1.0;
46 | gaussianBlurColor += texture2D(u_texture_0, uv + vec2(-1, 0) * texelSize).rgb * 2.0;
47 | gaussianBlurColor += texture2D(u_texture_0, uv + vec2(0, 0) * texelSize).rgb * 4.0;
48 | gaussianBlurColor += texture2D(u_texture_0, uv + vec2(1, 0) * texelSize).rgb * 2.0;
49 | gaussianBlurColor += texture2D(u_texture_0, uv + vec2(-1, -1) * texelSize).rgb * 1.0;
50 | gaussianBlurColor += texture2D(u_texture_0, uv + vec2(0, -1) * texelSize).rgb * 2.0;
51 | gaussianBlurColor += texture2D(u_texture_0, uv + vec2(1, -1) * texelSize).rgb * 1.0;
52 | gaussianBlurColor = gaussianBlurColor / gaussianDivisor;
53 | color = gaussianBlurColor;
54 |
55 | gl_FragColor = vec4(color, 1.0);
56 | }
--------------------------------------------------------------------------------
/10-piano/piano.frag:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 |
3 | // #extension GL_GOOGLE_include_directive : enable
4 | #include "../common/functions.glsl"
5 |
6 | // shapes
7 | float sdRoundBox(vec3 p, vec3 b, float r);
8 | // space
9 | float opUnion(float d1, float d2);
10 | vec3 opBend(in vec3 p, float bendFactor);
11 | vec3 opSpaceRepetition(in vec3 p, in vec3 s, inout vec3 id);
12 | // lighting
13 | vec3 calcNormal(vec3 p);
14 | vec3 calcLighting(vec3 ro, vec3 n);
15 | // functions
16 | mat2 rotate(float angle);
17 |
18 | // constants
19 | float MAX_DIST = 100.0;
20 | float MIN_DIST = 0.0001;
21 |
22 | // colors
23 | vec3 BLACK = vec3(0.0);
24 | vec3 WHITE = vec3(1.0);
25 | vec3 RED = vec3(1.0, 0.0, 0.0);
26 | vec3 MAGENTA = vec3(1.0, 0.0, 1.0);
27 | vec3 GREEN = vec3(0.0, 1.0, 0.0);
28 |
29 | // uniforms
30 | uniform float u_time;
31 | uniform vec2 u_resolution;
32 |
33 | float whiteKey(vec3 p) {
34 | float y = 2.0;
35 | float x = y / 4.0;
36 | float z = y / 10.0;
37 | float d = sdRoundBox(p, vec3(x, y, z), 0.1) * .5;
38 | return d;
39 | }
40 |
41 | float blackKey(vec3 p) {
42 | float y = 1.2;
43 | float x = y / 4.0;
44 | float z = y / 8.0;
45 | float d = sdRoundBox(p, vec3(x, y, z), 0.1) * 0.5;
46 | return d;
47 | }
48 |
49 | float map(vec3 p, inout vec3 c) {
50 | // bend piano
51 | p = opBend(p, (sin(u_time) * 0.5) * 0.25);
52 | p.xzy = opBend(p.xzy, 0.05);
53 |
54 | vec3 whiteKeyId;
55 | vec3 q = opSpaceRepetition(p, vec3(1.1, 0.0, 0.0), whiteKeyId);
56 | q.y = q.y + sin(u_time + whiteKeyId.x * 0.5);
57 |
58 | vec3 blackKeyId;
59 | vec3 qb = opSpaceRepetition(p - vec3(0.5, 0.8, -0.25), vec3(1.0, 0.0, 0.0), blackKeyId);
60 | qb.y = qb.y + sin(u_time + blackKeyId.x * 0.5);
61 |
62 | // pressing keys progress
63 | float speed = 4.0;
64 | float amplitude = 0.05;
65 | vec3 axisPoint = vec3(0.0, 2.0, 0.0);
66 | if (whiteKeyId.x == 2.0) {
67 | q -= axisPoint;
68 | q.yz = q.yz * rotate((sin(u_time * speed) * 0.5 + 0.5) * amplitude);
69 | q += axisPoint;
70 | }
71 | if (blackKeyId.x == -3.0) {
72 | qb -= axisPoint;
73 | qb.yz = qb.yz * rotate((sin(u_time * speed) * 0.5 + 0.5) * amplitude);
74 | qb += axisPoint;
75 | }
76 |
77 | float wk = whiteKey(q);
78 | float bk = blackKey(qb);
79 |
80 | float m = mod(blackKeyId.x, 7.0);
81 | bk = m == 0.0 || m == 3.0 ? 20.0 : bk;
82 | c = wk < bk ? WHITE : BLACK;
83 | if (wk < bk) {
84 | if (mod(whiteKeyId.x, 7.0) == 2.0) {
85 | c = mix(WHITE, RED, smoothstep(0.1, 0.15, sin(u_time * 4.0) * 0.5 + 0.5));
86 | }
87 | } else {
88 | if (mod(blackKeyId.x, 7.0) == 4.0) {
89 | c = mix(BLACK, RED, smoothstep(0.1, 0.2, sin(u_time * 4.0) * 0.5 + 0.5));
90 | }
91 | }
92 |
93 | float d = opUnion(wk, bk);
94 | return d;
95 | }
96 |
97 | void main() {
98 | vec2 uv = (2.0 * gl_FragCoord.xy - u_resolution.xy) / u_resolution.y;
99 | vec3 ro = vec3(0.0, 0.0, -4.0);
100 | vec3 rd = normalize(vec3(uv, 1.0));
101 | float td = 0.0; // total distance
102 |
103 | vec3 c;
104 | vec3 n;
105 | float steps = 0.0;
106 | for (int i = 0; i < 100; i++) {
107 | vec3 p = ro + rd * td;
108 | float d = map(p, c);
109 | steps = float(i);
110 | if (td > MAX_DIST || d < abs(MIN_DIST)) {
111 | break;
112 | }
113 | td += d;
114 | }
115 |
116 | vec3 color = GREEN;
117 | if (td < MAX_DIST) {
118 | vec3 p = ro + rd * td;
119 | n = calcNormal(p);
120 | color = c * calcLighting(ro, n);
121 | }
122 |
123 | // uncomment each one to see different color styles
124 | // color = vec3(td * 0.05);
125 | // color = steps < 80.0 ? pow(vec3(steps * 0.01) * color, vec3(0.25)) : pow(color * steps * 0.1, vec3(0.1));
126 | // color = pow(vec3(steps * 0.01) * color * steps, vec3(0.125));
127 | // color = td < 100.0 ? pow(vec3(steps * 0.01), vec3(0.25)) : color * steps * 0.01;
128 | // color = td < 100.0 ? pow(vec3(steps * 0.01) * color, vec3(0.125)) : color * steps * 0.01;
129 | color = td < 100.0 ? pow(vec3(steps * 0.01), vec3(0.25)) * n : color * steps * 0.01;
130 |
131 | // add gamma correction
132 | color = pow(color, vec3(1.0 / 2.2));
133 |
134 | gl_FragColor = vec4(color, 1.0);
135 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Suboptimal Engineer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🎨 Shader Tutorials
2 |
3 | - This repo contains the code for my shader tutorials on YouTube
4 | - Playlist with 10+ Videos: [YouTube](https://www.youtube.com/watch?v=7UvpTTEE1Hs&list=PLTJ_bWjv6i7xnDaPMrbx69zVu82sVails&pp=gAQB)
5 |
6 | ## Tutorials
7 |
8 | ### 0. Intro to Shaders
9 |
10 |
11 |
12 | ### 1. Phong Lighting
13 |
14 |
15 |
16 | ### 2. Circle SDF
17 |
18 |
19 |
20 | ### 3. Value Noise
21 |
22 |
23 |
24 | ### 4. Voronoi Noise
25 |
26 |
27 |
28 | ### 5. Perlin Noise
29 |
30 |
31 |
32 | ### 6. FBM, Domain Warping, Normals
33 |
34 |
35 |
36 | ### 7. Ray Marching
37 |
38 |
39 |
40 | ### 8. Checkerboard
41 |
42 | - todo: add image
43 |
44 | ### 9. Gaussian Blur
45 |
46 | - todo: add image
47 |
48 | ### 10. Piano
49 |
50 | - todo: add image
51 |
52 | ## Helpful Resources
53 |
54 | - Tutorials
55 |
56 | - [Inigo Quilez's Articles](https://iquilezles.org/articles/)
57 | - [Ronja's Shader Tutorials](https://ronja-tutorials.com/)
58 | - [Catlike Coding's Unity Tutorials](https://catlikecoding.com/unity/tutorials/)
59 | - [Patricio Vivo's The Book of Shaders](https://thebookofshaders.com/)
60 | - [Michael Walczyk's Ray Marching Tutorial](https://michaelwalczyk.com/blog-ray-marching.html)
61 |
62 | - YouTube Channels
63 |
64 | - [Simon Dev](https://www.youtube.com/@simondev758)
65 | - [Inigo Quilez](https://www.youtube.com/@InigoQuilez)
66 | - [The Art of Code](https://www.youtube.com/@TheArtofCodeIsCool)
67 |
68 | - Websites
69 |
70 | - [Shadertoy](https://www.shadertoy.com/)
71 |
--------------------------------------------------------------------------------
/_thumbnails/glsl_00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuboptimalEng/shader-tutorials/240917555b5de1e2b57ad69e49a6b5e36a875669/_thumbnails/glsl_00.png
--------------------------------------------------------------------------------
/_thumbnails/glsl_01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuboptimalEng/shader-tutorials/240917555b5de1e2b57ad69e49a6b5e36a875669/_thumbnails/glsl_01.png
--------------------------------------------------------------------------------
/_thumbnails/glsl_02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuboptimalEng/shader-tutorials/240917555b5de1e2b57ad69e49a6b5e36a875669/_thumbnails/glsl_02.png
--------------------------------------------------------------------------------
/_thumbnails/glsl_03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuboptimalEng/shader-tutorials/240917555b5de1e2b57ad69e49a6b5e36a875669/_thumbnails/glsl_03.png
--------------------------------------------------------------------------------
/_thumbnails/glsl_04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuboptimalEng/shader-tutorials/240917555b5de1e2b57ad69e49a6b5e36a875669/_thumbnails/glsl_04.png
--------------------------------------------------------------------------------
/_thumbnails/glsl_05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuboptimalEng/shader-tutorials/240917555b5de1e2b57ad69e49a6b5e36a875669/_thumbnails/glsl_05.png
--------------------------------------------------------------------------------
/_thumbnails/glsl_06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuboptimalEng/shader-tutorials/240917555b5de1e2b57ad69e49a6b5e36a875669/_thumbnails/glsl_06.png
--------------------------------------------------------------------------------
/_thumbnails/glsl_07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SuboptimalEng/shader-tutorials/240917555b5de1e2b57ad69e49a6b5e36a875669/_thumbnails/glsl_07.png
--------------------------------------------------------------------------------
/common/functions.glsl:
--------------------------------------------------------------------------------
1 | // Reference from Inigo Quilez: https://iquilezles.org/articles/distfunctions/
2 | float sdSphere(vec3 p, float s) {
3 | return length(p) - s;
4 | }
5 |
6 | float sdBox(vec3 p, vec3 b) {
7 | vec3 q = abs(p) - b;
8 | return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
9 | }
10 |
11 | float sdRoundBox(vec3 p, vec3 b, float r) {
12 | vec3 q = abs(p) - b + r;
13 | return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0) - r;
14 | }
15 |
16 | float opUnion(float d1, float d2) {
17 | return min(d1, d2);
18 | }
19 |
20 | float opSubtraction(float d1, float d2) {
21 | return max(-d1, d2);
22 | }
23 |
24 | float opIntersection(float d1, float d2) {
25 | return max(d1, d2);
26 | }
27 |
28 | float opXor(float d1, float d2) {
29 | return max(min(d1, d2), -max(d1, d2));
30 | }
31 |
32 | float opOnion(in float sdf, in float thickness) {
33 | return abs(sdf) - thickness;
34 | }
35 |
36 | float opSmoothUnion(float d1, float d2, float k) {
37 | float h = clamp(0.5 + 0.5 * (d2 - d1) / k, 0.0, 1.0);
38 | return mix(d2, d1, h) - k * h * (1.0 - h);
39 | }
40 |
41 | float opSmoothSubtraction(float d1, float d2, float k) {
42 | float h = clamp(0.5 - 0.5 * (d2 + d1) / k, 0.0, 1.0);
43 | return mix(d2, -d1, h) + k * h * (1.0 - h);
44 | }
45 |
46 | float opSmoothIntersection(float d1, float d2, float k) {
47 | float h = clamp(0.5 - 0.5 * (d2 - d1) / k, 0.0, 1.0);
48 | return mix(d2, d1, h) + k * h * (1.0 - h);
49 | }
50 |
51 | vec3 opSpaceRepetition(in vec3 p, in vec3 s, inout vec3 id) {
52 | // Reference by IQ, but round function is not available.
53 | // vec3 q = p - s * round(p / s);
54 | // return q;
55 |
56 | // old version
57 | // vec3 q = mod(p + s / 2.0, s) - s / 2.0;
58 | // return q;
59 |
60 | // new version
61 | // vec3 q = p - s * (floor(p / s + 0.5));
62 | // return q;
63 |
64 | // with id
65 | id = floor(p / s + 0.5);
66 | vec3 q = p - s * id;
67 | return q;
68 | }
69 |
70 | vec3 opLimitedRepetition(in vec3 p, in float s, in vec3 l, inout vec3 id) {
71 | // Reference by IQ, but round function is not available.
72 | // vec3 q = p - s * clamp(round(p / s), -l, l);
73 | // return primitive(q);
74 |
75 | // vec3 q = p - s * clamp(floor(p / s + 0.5), -l, l);
76 | // return q;
77 |
78 | id = floor(p / s + 0.5);
79 | vec3 q = p - s * clamp(id, -l, l);
80 | return q;
81 | }
82 |
83 | // Reference by IQ, but sdf3d primitive is not available.
84 | // float opCheapBend(in sdf3d primitive, in vec3 p) {
85 | // const float k = 10.0; // or some other amount
86 | // float c = cos(k * p.x);
87 | // float s = sin(k * p.x);
88 | // mat2 m = mat2(c, -s, s, c);
89 | // vec3 q = vec3(m * p.xy, p.z);
90 | // return primitive(q);
91 | // }
92 |
93 | vec3 opBend(in vec3 p, float bendFactor) {
94 | float k = bendFactor; // or some other amount
95 | float c = cos(k * p.x);
96 | float s = sin(k * p.x);
97 | mat2 m = mat2(c, -s, s, c);
98 | vec3 q = vec3(m * p.xy, p.z);
99 | return q;
100 | }
101 |
102 | // Reference by IQ, but sdf3d primitive is not available.
103 | // float opTwist(in sdf3d primitive, in vec3 p) {
104 | // const float k = 10.0; // or some other amount
105 | // float c = cos(k * p.y);
106 | // float s = sin(k * p.y);
107 | // mat2 m = mat2(c, -s, s, c);
108 | // vec3 q = vec3(m * p.xz, p.y);
109 | // return primitive(q);
110 | // }
111 |
112 | vec3 opTwist(in vec3 p, float twistFactor) {
113 | float k = twistFactor; // or some other amount
114 | float c = cos(k * p.y);
115 | float s = sin(k * p.y);
116 | mat2 m = mat2(c, -s, s, c);
117 | vec3 q = vec3(m * p.xz, p.y);
118 | return q;
119 | }
120 |
121 | mat2 rotate(float angle) {
122 | float c = cos(angle);
123 | float s = sin(angle);
124 | return mat2(c, -s, s, c);
125 | }
126 |
127 | // This function needs to be defined in fragment shader.
128 | float map(vec3 p, inout vec3 c);
129 |
130 | vec3 calcNormal(vec3 p) {
131 | vec2 d = vec2(0.001, 0.0);
132 | vec3 c = vec3(0.0);
133 | float n = map(p, c);
134 | float gx = n - map(p - d.xyy, c);
135 | float gy = n - map(p - d.yxy, c);
136 | float gz = n - map(p - d.yyx, c);
137 | return normalize(vec3(gx, gy, gz));
138 | }
139 |
140 | vec3 calcLighting(vec3 ro, vec3 n) {
141 | vec3 lightColor = vec3(1.0);
142 | vec3 lightSource = vec3(1.0, 1.0, -3.0);
143 | float diffuseStrength = max(0.0, dot(normalize(lightSource), n));
144 | vec3 diffuse = diffuseStrength * lightColor;
145 |
146 | vec3 viewSource = normalize(ro);
147 | vec3 reflectSource = normalize(reflect(-lightSource, n));
148 | float specularStrength = max(0.0, dot(viewSource, reflectSource));
149 | specularStrength = pow(specularStrength, 64.0);
150 | vec3 specular = specularStrength * lightColor;
151 |
152 | // vec3 ambient = vec3(1.0);
153 | // return ambient * 0.1 + diffuse * 0.7 + specular * 0.2;
154 | return diffuse * 0.7 + specular * 0.3;
155 | }
156 |
--------------------------------------------------------------------------------