├── .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 | --------------------------------------------------------------------------------