├── README.md ├── assets ├── diagram-1.jpg ├── diagram-2.jpg ├── diagram-3.jpg └── diagram-4.jpg ├── index.html ├── main.js ├── my-shader ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── part-0 ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── part-1 ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── part-2 ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── part-3 ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── part-4 ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── part-5 ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── part-6 ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── part-7 ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── part-8 ├── fragment.glsl ├── index.html ├── main.js ├── style.css └── vertex.glsl ├── style.css └── util.js /README.md: -------------------------------------------------------------------------------- 1 | # Shaders 2 | 3 | This is a tutorial about graphics programming with WebGL. It's really just a website with a bunch of WebGL programs in it. You can press `p` to toggle presentation mode. 4 | 5 | http://www.jonasluebbers.com/shader-tutorial/ 6 | 7 | To follow along with the tutorial, do this: 8 | 9 | 1. Download this repository. 10 | 2. Open it in the terminal program of your choice. 11 | 3. Navigate to the root of the repository. 12 | 4. If you're using Python 2, run `python -m SimpleHTTPServer 8000`. 13 | 5. If you're using Python 3, run `python -m http.server 8000`. 14 | 6. Open your browser and go to localhost:8000/my-shader. 15 | 7. You can make changes in the `/my-shader` directory as you follow along with each part of the tutorial. -------------------------------------------------------------------------------- /assets/diagram-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nonphoto/shader-tutorial/b867c2e3639a800b55620d16de4c3a2480727877/assets/diagram-1.jpg -------------------------------------------------------------------------------- /assets/diagram-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nonphoto/shader-tutorial/b867c2e3639a800b55620d16de4c3a2480727877/assets/diagram-2.jpg -------------------------------------------------------------------------------- /assets/diagram-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nonphoto/shader-tutorial/b867c2e3639a800b55620d16de4c3a2480727877/assets/diagram-3.jpg -------------------------------------------------------------------------------- /assets/diagram-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nonphoto/shader-tutorial/b867c2e3639a800b55620d16de4c3a2480727877/assets/diagram-4.jpg -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Shader Tutorial 5 | 6 | 7 | 8 | 9 |
10 |

Shaders

11 |
12 |
13 |

14 | This is a tutorial about programming shaders on the web. 15 |

16 |

17 | Shaders are programs that run on the GPU: a processor that is designed specifically for graphics operations. 18 |

19 | 22 | A diagram comparing drawing libraries to shaders 23 | 26 |

27 | In most drawing libraries we draw one shape at a time. With shaders we go through every pixel on the screen and decide what color it should be. 28 |

29 | 32 |
33 |
34 |

35 | WebGL is the interface for the GPU. 36 |

37 | 40 |

41 | WebGL can be tricky to use because it matches how the GPU works: it can't decide what to do next on its own. 42 |

43 | 46 | A diagram of WebGL interfacing with the GPU 47 | 50 |

51 | Drawing with WebGL and shaders is a fundamentally different technique from most drawing libraries. 52 |

53 | 56 |
57 |
58 |

Threads

59 |

60 | All processors work like a group of pipes. 61 |

62 | 65 |

66 | The GPU is a processor with thousands of small pipes, or threads that work asynchronously. 67 |

68 | 71 |

72 | Individual threads can't know what their neighbors are up to. 73 |

74 | A diagram comparing processors to groups of pipes. 75 |
76 |
77 |

Rasterization

78 |

79 | WebGL uses a rasterization algorithm to turn our model into pixels on the screen. 80 |

81 |
    82 |
  1. 83 |

    84 | It requires that all geometry be composed of triangles. 85 |

    86 | 89 |
  2. 90 |
  3. 91 | It runs a vertex shader on each vertex of each triangle to position it on the screen. 92 |
  4. 93 |
  5. 94 |

    95 | It removes all triangles that are covered or off-screen. 96 |

    97 | 100 |
  6. 101 |
  7. 102 | It runs a fragment shader on every pixel contained in each of the remaining triangles. 103 |
  8. 104 |
105 | A diagram of the rasterization algorithm. 106 |
107 |
108 |

109 | In our demo, we will have three programs: 110 |

111 |
    112 |
  1. 113 | A JavaScript program running in a browser. It initializes WebGL with programs 2 and 3. 114 |
  2. 115 |
  3. 116 | A vertex shader. 117 |
  4. 118 |
  5. 119 | A fragment shader. 120 |
  6. 121 |
122 |
123 |
124 |
125 |

0 — Setting up the site

126 | Demo 127 |
128 |

129 | Start by downloading the source of this page here. 130 |

131 |

132 | Follow the instructions in the README to run the page locally. 133 |

134 |

135 | The changes for each step are in the gray box: 136 |

137 | 155 |
156 |
157 |
158 |

1 — Initializing the context

159 | Demo 160 |
161 |

162 | To start drawing with our vertex and fragment shaders, we need to create a new WebGL context. 163 |

164 |

165 | The context object contains all of the methods that we will use to set up the program. I did some of the boring error handling in the utils. 166 |

167 | 185 |
186 |
187 |
188 |

2 — Creating the buffers

189 | Demo 190 |
191 |

192 | We want the vertex shader to iterate over our vertex positions, but we haven't yet given it any data to work with. 193 |

194 |

195 | We use a vertex buffer to store our vertex positions, and an element buffer to store the order in which to traverse the vertices. 196 |

197 |

198 | Even though vertex positions are 3-dimensional vectors, both the vertex and element buffers are flat arrays. We will tell WebGL to read the buffers in chunks of three components later. 199 |

200 |

201 | This won't have any visual effect because we haven't drawn the buffers to the canvas yet. 202 |

203 | 218 |
219 |
220 |
221 |

3 — Drawing triangles to the screen

222 | Demo 223 |
224 |

225 | We have created buffers for our vertex data, now its time to convert the data into pixels in the canvas. 226 |

227 |

228 | The vertex shader receives the vertex positions from the vertex buffer in an attribute. An attribute is a variable declared in the vertex shader whose memory location can be accessed from the main script. 229 |

230 |

231 | First we write each element of the vertex buffer to an attribute we declared in the vertex shader. 232 |

233 |

234 | When the vertex shader is finished processing all of the vertex data, the fragment shader colors in the pixels inside each triangle. 235 |

236 | 267 |
268 |
269 |
270 |

4 — Perspective projection

271 | Demo 272 |
273 |

274 | The entire canvas is now blue because the edges of the cube line up with the corners of the canvas. 275 |

276 |

277 | WebGL does not give you a 3d perspective by default; it just ignores the z axis. We need to do some math to project our 3d coordinates into the 2d space of the canvas. 278 |

279 |

280 | The easiest way to do this is using matrix multiplication. Simply put, matrices perform different transformations on vectors when you multiply them together. 281 |

282 | 285 | 303 |
304 |
305 |
306 |

5 — Model rotation

307 | Demo 308 |
309 |

310 | At this point we can only see the front of the cube. Let's try rotating it. 311 |

312 |

313 | We rotate the model in the same way we performed the perspective projection, with matrix multiplication. 314 |

315 | 330 |
331 |
332 |
333 |

6 — Animation

334 | Demo 335 |
336 |

337 | We can animate our scene by re-drawing the frame 60 times per second, and passing in a different global time variable for each frame. 338 |

339 |

340 | WebGL supports passing global variables to shaders with uniforms. Uniforms are just constants that we set before running the vertex and fragment shaders. 341 |

342 | 368 |
369 |
370 |
371 |

7 — Color

372 | Demo 373 |
374 |

375 | We can add color to our cube by returning a different color for each pixel in the fragment shader. 376 |

377 |

378 | We can use a varying to pass information from the vertex shader to the fragment shader. In this case, we give each vertex a color. 379 |

380 |

381 | The varying will automatically interpolate between vertex values for the given fragment. 382 |

383 | 401 |
402 |
403 |
404 |

8 — ???

405 | Demo 406 |
407 |

408 | This one is just for fun. 409 |

410 |
411 |
412 |

More

413 | 479 |

480 | Written and edited by Jonas Luebbers. 481 |

482 |
483 | 484 | 485 | 486 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('keyup', (event) => { 2 | if (event.keyCode === 80) { 3 | document.body.classList.toggle('is-presentation-mode') 4 | } 5 | }) 6 | 7 | const sections = Array.from(document.querySelectorAll('section')) 8 | 9 | sections.forEach((section, index) => { 10 | section.addEventListener('click', (event) => { 11 | if (event.target.hasAttribute('href')) return 12 | 13 | const nextIndex = (index + 1) % sections.length 14 | const next = sections[nextIndex] 15 | const bounds = next.getBoundingClientRect() 16 | const offset = bounds.top + window.pageYOffset 17 | window.scrollTo(0, offset) 18 | }) 19 | }) -------------------------------------------------------------------------------- /my-shader/fragment.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /my-shader/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /my-shader/main.js: -------------------------------------------------------------------------------- 1 | // Get the canvas element and set the resolution. 2 | const canvas = document.getElementById('canvas') 3 | canvas.width = 1024 4 | canvas.height = 1024 5 | 6 | // Request the shdaer source files, they get returned as strings. 7 | const vertexSource = request('vertex.glsl') 8 | const fragmentSource = request('fragment.glsl') 9 | 10 | // Wait for the source files to load. 11 | Promise 12 | .all([vertexSource, fragmentSource]) 13 | .then(([vertexSource, fragmentSource]) => { 14 | 15 | // Draw something in here eventually. 16 | 17 | }) 18 | -------------------------------------------------------------------------------- /my-shader/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /my-shader/vertex.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /part-0/fragment.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /part-0/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /part-0/main.js: -------------------------------------------------------------------------------- 1 | // Get the canvas element and set the resolution. 2 | const canvas = document.getElementById('canvas') 3 | canvas.width = 1024 4 | canvas.height = 1024 5 | 6 | // Request the shdaer source files, they get returned as strings. 7 | const vertexSource = request('vertex.glsl') 8 | const fragmentSource = request('fragment.glsl') 9 | 10 | // Wait for the source files to load. 11 | Promise 12 | .all([vertexSource, fragmentSource]) 13 | .then(([vertexSource, fragmentSource]) => { 14 | 15 | // Draw something in here eventually. 16 | 17 | }) 18 | -------------------------------------------------------------------------------- /part-0/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /part-0/vertex.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /part-1/fragment.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /part-1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /part-1/main.js: -------------------------------------------------------------------------------- 1 | const canvas = document.getElementById('canvas') 2 | canvas.width = 1024 3 | canvas.height = 1024 4 | 5 | const vertexSource = request('vertex.glsl') 6 | const fragmentSource = request('fragment.glsl') 7 | 8 | Promise 9 | .all([vertexSource, fragmentSource]) 10 | .then(([vertexSource, fragmentSource]) => { 11 | 12 | // Create a new WebGL context for the canvas element. 13 | const gl = createContext(canvas) 14 | 15 | // Create new shader objects from the vertex and fragment shader sources. 16 | const vertexShader = createShader(gl, vertexSource, gl.VERTEX_SHADER) 17 | const fragmentShader = createShader(gl, fragmentSource, gl.FRAGMENT_SHADER) 18 | 19 | // Create a new program with the two shaders. 20 | const program = createProgram(gl, vertexShader, fragmentShader) 21 | 22 | // Tell the GPU to use the program we just made. 23 | gl.useProgram(program) 24 | 25 | // Set the clear color to a fetching pink for debugging purposes. 26 | gl.clearColor(1.0, 0.0, 1.0, 1.0) 27 | 28 | // Clear the canvas. This sets every pixel to the clear color. 29 | gl.clear(gl.COLOR_BUFFER_BIT) 30 | }) 31 | -------------------------------------------------------------------------------- /part-1/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /part-1/vertex.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /part-2/fragment.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /part-2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /part-2/main.js: -------------------------------------------------------------------------------- 1 | // These are the vertex positions for a cube. 2 | const positions = [ 3 | -1.0, -1.0, -1.0, 4 | -1.0, -1.0, 1.0, 5 | -1.0, 1.0, -1.0, 6 | -1.0, 1.0, 1.0, 7 | 1.0, -1.0, -1.0, 8 | 1.0, -1.0, 1.0, 9 | 1.0, 1.0, -1.0, 10 | 1.0, 1.0, 1.0 11 | ] 12 | 13 | // These are the indices to use when drawing the cube as a group of triangles. 14 | const elements = [ 15 | 0, 1, 3, 16 | 0, 2, 3, 17 | 5, 4, 6, 18 | 5, 7, 6, 19 | 4, 0, 2, 20 | 4, 6, 2, 21 | 1, 5, 7, 22 | 1, 3, 7, 23 | 2, 6, 7, 24 | 2, 3, 7, 25 | 0, 4, 5, 26 | 0, 1, 5 27 | ] 28 | 29 | const canvas = document.getElementById('canvas') 30 | canvas.width = 1024 31 | canvas.height = 1024 32 | 33 | const vertexSource = request('vertex.glsl') 34 | const fragmentSource = request('fragment.glsl') 35 | 36 | Promise 37 | .all([vertexSource, fragmentSource]) 38 | .then(([vertexSource, fragmentSource]) => { 39 | 40 | const gl = createContext(canvas) 41 | 42 | const vertexShader = createShader(gl, vertexSource, gl.VERTEX_SHADER) 43 | const fragmentShader = createShader(gl, fragmentSource, gl.FRAGMENT_SHADER) 44 | 45 | const program = createProgram(gl, vertexShader, fragmentShader) 46 | gl.useProgram(program) 47 | gl.clearColor(1.0, 0.0, 1.0, 1.0) 48 | gl.clear(gl.COLOR_BUFFER_BIT) 49 | 50 | // Create a new buffer and fill it with our vertices. 51 | const vertexBuffer = gl.createBuffer() 52 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 53 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW) 54 | 55 | // Create a new buffer and fill it with our element indices. 56 | const elementBuffer = gl.createBuffer() 57 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 58 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(elements), gl.STATIC_DRAW) 59 | }) 60 | -------------------------------------------------------------------------------- /part-2/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /part-2/vertex.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /part-3/fragment.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | 3 | // Draw cyan for every fragment. 4 | gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); 5 | } 6 | -------------------------------------------------------------------------------- /part-3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /part-3/main.js: -------------------------------------------------------------------------------- 1 | const positions = [ 2 | -1.0, -1.0, -1.0, 3 | -1.0, -1.0, 1.0, 4 | -1.0, 1.0, -1.0, 5 | -1.0, 1.0, 1.0, 6 | 1.0, -1.0, -1.0, 7 | 1.0, -1.0, 1.0, 8 | 1.0, 1.0, -1.0, 9 | 1.0, 1.0, 1.0 10 | ] 11 | 12 | const elements = [ 13 | 0, 1, 3, 14 | 0, 2, 3, 15 | 5, 4, 6, 16 | 5, 7, 6, 17 | 4, 0, 2, 18 | 4, 6, 2, 19 | 1, 5, 7, 20 | 1, 3, 7, 21 | 2, 6, 7, 22 | 2, 3, 7, 23 | 0, 4, 5, 24 | 0, 1, 5 25 | ] 26 | 27 | const canvas = document.getElementById('canvas') 28 | canvas.width = 1024 29 | canvas.height = 1024 30 | 31 | const vertexSource = request('vertex.glsl') 32 | const fragmentSource = request('fragment.glsl') 33 | 34 | Promise 35 | .all([vertexSource, fragmentSource]) 36 | .then(([vertexSource, fragmentSource]) => { 37 | 38 | const gl = createContext(canvas) 39 | 40 | const vertexShader = createShader(gl, vertexSource, gl.VERTEX_SHADER) 41 | const fragmentShader = createShader(gl, fragmentSource, gl.FRAGMENT_SHADER) 42 | 43 | const program = createProgram(gl, vertexShader, fragmentShader) 44 | gl.useProgram(program) 45 | gl.clearColor(1.0, 0.0, 1.0, 1.0) 46 | gl.clear(gl.COLOR_BUFFER_BIT) 47 | 48 | const vertexBuffer = gl.createBuffer() 49 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 50 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW) 51 | 52 | const elementBuffer = gl.createBuffer() 53 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 54 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(elements), gl.STATIC_DRAW) 55 | 56 | // Get the memory location allocated to our vertex position attribute in the vertex shader. 57 | const vertexPositionAttribute = gl.getAttribLocation(program, 'a_vertexPosition') 58 | 59 | // Enable the vertex attribute array. This tells the vertex shader to iterate over our vertex attributes. 60 | gl.enableVertexAttribArray(vertexPositionAttribute) 61 | 62 | // Bind the vertex buffer so we can edit it. 63 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 64 | 65 | // Specify the memory layout. In this case our vertices each have 3 float components. 66 | gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0) 67 | 68 | // Bind the element array buffer before drawing. 69 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 70 | 71 | // Draw each of our triangles to the frame. 72 | gl.drawElements(gl.TRIANGLES, elements.length, gl.UNSIGNED_SHORT, 0) 73 | }) 74 | -------------------------------------------------------------------------------- /part-3/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /part-3/vertex.glsl: -------------------------------------------------------------------------------- 1 | // Declare a new attribute for the vertex position. 2 | attribute vec3 a_vertexPosition; 3 | 4 | void main() { 5 | 6 | // Pass our vertex attribute to the output vector by creating a new vec4. 7 | gl_Position = vec4(a_vertexPosition, 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /part-4/fragment.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); 3 | } 4 | -------------------------------------------------------------------------------- /part-4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /part-4/main.js: -------------------------------------------------------------------------------- 1 | const positions = [ 2 | -1.0, -1.0, -1.0, 3 | -1.0, -1.0, 1.0, 4 | -1.0, 1.0, -1.0, 5 | -1.0, 1.0, 1.0, 6 | 1.0, -1.0, -1.0, 7 | 1.0, -1.0, 1.0, 8 | 1.0, 1.0, -1.0, 9 | 1.0, 1.0, 1.0 10 | ] 11 | 12 | const elements = [ 13 | 0, 1, 3, 14 | 0, 2, 3, 15 | 5, 4, 6, 16 | 5, 7, 6, 17 | 4, 0, 2, 18 | 4, 6, 2, 19 | 1, 5, 7, 20 | 1, 3, 7, 21 | 2, 6, 7, 22 | 2, 3, 7, 23 | 0, 4, 5, 24 | 0, 1, 5 25 | ] 26 | 27 | const canvas = document.getElementById('canvas') 28 | canvas.width = 1024 29 | canvas.height = 1024 30 | 31 | const vertexSource = request('vertex.glsl') 32 | const fragmentSource = request('fragment.glsl') 33 | 34 | Promise 35 | .all([vertexSource, fragmentSource]) 36 | .then(([vertexSource, fragmentSource]) => { 37 | 38 | const gl = createContext(canvas) 39 | 40 | const vertexShader = createShader(gl, vertexSource, gl.VERTEX_SHADER) 41 | const fragmentShader = createShader(gl, fragmentSource, gl.FRAGMENT_SHADER) 42 | 43 | const program = createProgram(gl, vertexShader, fragmentShader) 44 | gl.useProgram(program) 45 | gl.clearColor(1.0, 0.0, 1.0, 1.0) 46 | gl.clear(gl.COLOR_BUFFER_BIT) 47 | 48 | const vertexBuffer = gl.createBuffer() 49 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 50 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW) 51 | 52 | const elementBuffer = gl.createBuffer() 53 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 54 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(elements), gl.STATIC_DRAW) 55 | 56 | const vertexPositionAttribute = gl.getAttribLocation(program, 'a_vertexPosition') 57 | 58 | gl.enableVertexAttribArray(vertexPositionAttribute) 59 | 60 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 61 | gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0) 62 | 63 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 64 | gl.drawElements(gl.TRIANGLES, elements.length, gl.UNSIGNED_SHORT, 0) 65 | }) 66 | -------------------------------------------------------------------------------- /part-4/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /part-4/vertex.glsl: -------------------------------------------------------------------------------- 1 | attribute vec3 a_vertexPosition; 2 | 3 | vec4 model_position; 4 | 5 | // Declare translation vector. 6 | vec4 translation; 7 | 8 | // Declare projection matrix. 9 | mat4 projection; 10 | 11 | /* 12 | Calculate a new projection matrix with perspective. 13 | float fovy - The vertical field of view in radians. 14 | float aspect - The aspect ratio of the canvas. 15 | float near - The distance to the near clipping plane. 16 | float far - The distance to the far clipping plane. 17 | */ 18 | mat4 perspective(float fovy, float aspect, float near, float far) { 19 | float f = 1.0 / tan(fovy / 2.0); 20 | float nf = 1.0 / (near - far); 21 | 22 | vec4 col0 = vec4(f / aspect, 0, 0, 0); 23 | vec4 col1 = vec4(0, f, 0, 0); 24 | vec4 col2 = vec4(0, 0, (far + near) * nf, -1.0); 25 | vec4 col3 = vec4(0, 0, 2.0 * far * near * nf, 0); 26 | 27 | return mat4(col0, col1, col2, col3); 28 | } 29 | 30 | void main() { 31 | model_position = vec4(a_vertexPosition, 1.0); 32 | 33 | // Create a new translation vector. 34 | translation = vec4(0.0, 0.0, -4.0, 1.0); 35 | 36 | // Create a new projection matrix. 37 | projection = perspective(0.8, 1.0, 1.0, 50.0); 38 | 39 | // Translate and project the position of the vertex. 40 | gl_Position = (model_position + translation) * projection; 41 | } 42 | -------------------------------------------------------------------------------- /part-5/fragment.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); 3 | } 4 | -------------------------------------------------------------------------------- /part-5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /part-5/main.js: -------------------------------------------------------------------------------- 1 | const positions = [ 2 | -1.0, -1.0, -1.0, 3 | -1.0, -1.0, 1.0, 4 | -1.0, 1.0, -1.0, 5 | -1.0, 1.0, 1.0, 6 | 1.0, -1.0, -1.0, 7 | 1.0, -1.0, 1.0, 8 | 1.0, 1.0, -1.0, 9 | 1.0, 1.0, 1.0 10 | ] 11 | 12 | const elements = [ 13 | 0, 1, 3, 14 | 0, 2, 3, 15 | 5, 4, 6, 16 | 5, 7, 6, 17 | 4, 0, 2, 18 | 4, 6, 2, 19 | 1, 5, 7, 20 | 1, 3, 7, 21 | 2, 6, 7, 22 | 2, 3, 7, 23 | 0, 4, 5, 24 | 0, 1, 5 25 | ] 26 | 27 | const canvas = document.getElementById('canvas') 28 | canvas.width = 1024 29 | canvas.height = 1024 30 | 31 | const vertexSource = request('vertex.glsl') 32 | const fragmentSource = request('fragment.glsl') 33 | 34 | Promise 35 | .all([vertexSource, fragmentSource]) 36 | .then(([vertexSource, fragmentSource]) => { 37 | 38 | const gl = createContext(canvas) 39 | 40 | const vertexShader = createShader(gl, vertexSource, gl.VERTEX_SHADER) 41 | const fragmentShader = createShader(gl, fragmentSource, gl.FRAGMENT_SHADER) 42 | 43 | const program = createProgram(gl, vertexShader, fragmentShader) 44 | gl.useProgram(program) 45 | gl.clearColor(1.0, 0.0, 1.0, 1.0) 46 | gl.clear(gl.COLOR_BUFFER_BIT) 47 | 48 | const vertexBuffer = gl.createBuffer() 49 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 50 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW) 51 | 52 | const elementBuffer = gl.createBuffer() 53 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 54 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(elements), gl.STATIC_DRAW) 55 | 56 | const vertexPositionAttribute = gl.getAttribLocation(program, 'a_vertexPosition') 57 | 58 | gl.enableVertexAttribArray(vertexPositionAttribute) 59 | 60 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 61 | gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0) 62 | 63 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 64 | gl.drawElements(gl.TRIANGLES, elements.length, gl.UNSIGNED_SHORT, 0) 65 | }) 66 | -------------------------------------------------------------------------------- /part-5/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /part-5/vertex.glsl: -------------------------------------------------------------------------------- 1 | attribute vec3 a_vertexPosition; 2 | 3 | vec4 model_position; 4 | vec4 translation; 5 | mat4 projection; 6 | 7 | // Declare a rotation matrix. 8 | mat4 rotation; 9 | 10 | mat4 perspective(float fovy, float aspect, float near, float far) { 11 | float f = 1.0 / tan(fovy / 2.0); 12 | float nf = 1.0 / (near - far); 13 | 14 | vec4 col0 = vec4(f / aspect, 0, 0, 0); 15 | vec4 col1 = vec4(0, f, 0, 0); 16 | vec4 col2 = vec4(0, 0, (far + near) * nf, -1.0); 17 | vec4 col3 = vec4(0, 0, 2.0 * far * near * nf, 0); 18 | 19 | return mat4(col0, col1, col2, col3); 20 | } 21 | 22 | /* 23 | Calculate a new rotation matrix around the Y axis. 24 | float rad - The radians to rotate by. 25 | */ 26 | mat4 rotateY(float rad) { 27 | float s = sin(rad); 28 | float c = cos(rad); 29 | 30 | vec4 col0 = vec4(c, 0, -s, 0); 31 | vec4 col1 = vec4(0, 1, 0, 0); 32 | vec4 col2 = vec4(s, 0, c, 0); 33 | vec4 col3 = vec4(0, 0, 0, 1); 34 | 35 | return mat4(col0, col1, col2, col3); 36 | } 37 | 38 | void main() { 39 | model_position = vec4(a_vertexPosition, 1.0); 40 | translation = vec4(0.0, 0.0, -4.0, 1.0); 41 | projection = perspective(0.8, 1.0, 1.0, 50.0); 42 | 43 | // Create a new rotation matrix. 44 | rotation = rotateY(1.0); 45 | 46 | // Rotate the original model vertices. 47 | gl_Position = ((model_position * rotation) + translation) * projection; 48 | } 49 | -------------------------------------------------------------------------------- /part-6/fragment.glsl: -------------------------------------------------------------------------------- 1 | void main() { 2 | gl_FragColor = vec4(0.0, 1.0, 1.0, 1.0); 3 | } 4 | -------------------------------------------------------------------------------- /part-6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /part-6/main.js: -------------------------------------------------------------------------------- 1 | const positions = [ 2 | -1.0, -1.0, -1.0, 3 | -1.0, -1.0, 1.0, 4 | -1.0, 1.0, -1.0, 5 | -1.0, 1.0, 1.0, 6 | 1.0, -1.0, -1.0, 7 | 1.0, -1.0, 1.0, 8 | 1.0, 1.0, -1.0, 9 | 1.0, 1.0, 1.0 10 | ] 11 | 12 | const elements = [ 13 | 0, 1, 3, 14 | 0, 2, 3, 15 | 5, 4, 6, 16 | 5, 7, 6, 17 | 4, 0, 2, 18 | 4, 6, 2, 19 | 1, 5, 7, 20 | 1, 3, 7, 21 | 2, 6, 7, 22 | 2, 3, 7, 23 | 0, 4, 5, 24 | 0, 1, 5 25 | ] 26 | 27 | const canvas = document.getElementById('canvas') 28 | canvas.width = 1024 29 | canvas.height = 1024 30 | 31 | const vertexSource = request('vertex.glsl') 32 | const fragmentSource = request('fragment.glsl') 33 | 34 | Promise 35 | .all([vertexSource, fragmentSource]) 36 | .then(([vertexSource, fragmentSource]) => { 37 | 38 | const gl = createContext(canvas) 39 | 40 | const vertexShader = createShader(gl, vertexSource, gl.VERTEX_SHADER) 41 | const fragmentShader = createShader(gl, fragmentSource, gl.FRAGMENT_SHADER) 42 | 43 | const program = createProgram(gl, vertexShader, fragmentShader) 44 | gl.useProgram(program) 45 | gl.clearColor(1.0, 0.0, 1.0, 1.0) 46 | gl.clear(gl.COLOR_BUFFER_BIT) 47 | 48 | const vertexBuffer = gl.createBuffer() 49 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 50 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW) 51 | 52 | const elementBuffer = gl.createBuffer() 53 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 54 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(elements), gl.STATIC_DRAW) 55 | 56 | const vertexPositionAttribute = gl.getAttribLocation(program, 'a_vertexPosition') 57 | 58 | // Get the memory location allocated to our time uniform. 59 | const timeUniform = gl.getUniformLocation(program, 'u_time') 60 | 61 | /** 62 | * Handle animation frame callbacks. 63 | */ 64 | function draw(t) { 65 | 66 | // Write the current time to the location provided by our time uniform. 67 | gl.uniform1f(timeUniform, t) 68 | 69 | gl.enableVertexAttribArray(vertexPositionAttribute) 70 | 71 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 72 | gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0) 73 | 74 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 75 | gl.drawElements(gl.TRIANGLES, elements.length, gl.UNSIGNED_SHORT, 0) 76 | 77 | // Request a new animation frame. 78 | requestAnimationFrame(draw) 79 | } 80 | 81 | // Start the animation frame loop at the current time. 82 | draw(performance.now()) 83 | }) 84 | -------------------------------------------------------------------------------- /part-6/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /part-6/vertex.glsl: -------------------------------------------------------------------------------- 1 | attribute vec3 a_vertexPosition; 2 | 3 | // Declare a new uniform for the global time. 4 | uniform float u_time; 5 | 6 | vec4 model_position; 7 | vec4 translation; 8 | mat4 projection; 9 | mat4 rotation; 10 | 11 | mat4 perspective(float fovy, float aspect, float near, float far) { 12 | float f = 1.0 / tan(fovy / 2.0); 13 | float nf = 1.0 / (near - far); 14 | 15 | vec4 col0 = vec4(f / aspect, 0, 0, 0); 16 | vec4 col1 = vec4(0, f, 0, 0); 17 | vec4 col2 = vec4(0, 0, (far + near) * nf, -1.0); 18 | vec4 col3 = vec4(0, 0, 2.0 * far * near * nf, 0); 19 | 20 | return mat4(col0, col1, col2, col3); 21 | } 22 | 23 | mat4 rotateY(float rad) { 24 | float s = sin(rad); 25 | float c = cos(rad); 26 | 27 | vec4 col0 = vec4(c, 0, -s, 0); 28 | vec4 col1 = vec4(0, 1, 0, 0); 29 | vec4 col2 = vec4(s, 0, c, 0); 30 | vec4 col3 = vec4(0, 0, 0, 1); 31 | 32 | return mat4(col0, col1, col2, col3); 33 | } 34 | 35 | void main() { 36 | model_position = vec4(a_vertexPosition, 1.0); 37 | translation = vec4(0.0, 0.0, -4.0, 1.0); 38 | projection = perspective(0.8, 1.0, 1.0, 50.0); 39 | 40 | // Calculate our rotation based on the current time. 41 | rotation = rotateY(u_time * 0.001); 42 | 43 | gl_Position = ((model_position * rotation) + translation) * projection; 44 | } 45 | -------------------------------------------------------------------------------- /part-7/fragment.glsl: -------------------------------------------------------------------------------- 1 | // Set the precision. 2 | precision mediump float; 3 | 4 | // Get the color varying from the vertex shader. 5 | varying vec4 v_color; 6 | 7 | void main() { 8 | 9 | // Set the fragment color to the color given by the surrounding vertices. 10 | gl_FragColor = v_color; 11 | } -------------------------------------------------------------------------------- /part-7/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /part-7/main.js: -------------------------------------------------------------------------------- 1 | const positions = [ 2 | -1.0, -1.0, -1.0, 3 | -1.0, -1.0, 1.0, 4 | -1.0, 1.0, -1.0, 5 | -1.0, 1.0, 1.0, 6 | 1.0, -1.0, -1.0, 7 | 1.0, -1.0, 1.0, 8 | 1.0, 1.0, -1.0, 9 | 1.0, 1.0, 1.0 10 | ] 11 | 12 | const elements = [ 13 | 0, 1, 3, 14 | 0, 2, 3, 15 | 5, 4, 6, 16 | 5, 7, 6, 17 | 4, 0, 2, 18 | 4, 6, 2, 19 | 1, 5, 7, 20 | 1, 3, 7, 21 | 2, 6, 7, 22 | 2, 3, 7, 23 | 0, 4, 5, 24 | 0, 1, 5 25 | ] 26 | 27 | const canvas = document.getElementById('canvas') 28 | canvas.width = 1024 29 | canvas.height = 1024 30 | 31 | const vertexSource = request('vertex.glsl') 32 | const fragmentSource = request('fragment.glsl') 33 | 34 | Promise 35 | .all([vertexSource, fragmentSource]) 36 | .then(([vertexSource, fragmentSource]) => { 37 | 38 | const gl = createContext(canvas) 39 | 40 | const vertexShader = createShader(gl, vertexSource, gl.VERTEX_SHADER) 41 | const fragmentShader = createShader(gl, fragmentSource, gl.FRAGMENT_SHADER) 42 | 43 | const program = createProgram(gl, vertexShader, fragmentShader) 44 | gl.useProgram(program) 45 | gl.clearColor(1.0, 0.0, 1.0, 1.0) 46 | gl.clear(gl.COLOR_BUFFER_BIT) 47 | 48 | // Enable depth testing to make sure our triangles overlap properly. 49 | gl.enable(gl.DEPTH_TEST) 50 | 51 | const vertexBuffer = gl.createBuffer() 52 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 53 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW) 54 | 55 | const elementBuffer = gl.createBuffer() 56 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 57 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(elements), gl.STATIC_DRAW) 58 | 59 | const vertexPositionAttribute = gl.getAttribLocation(program, 'a_vertexPosition') 60 | const timeUniform = gl.getUniformLocation(program, 'u_time') 61 | 62 | function draw(t) { 63 | gl.uniform1f(timeUniform, t) 64 | 65 | gl.enableVertexAttribArray(vertexPositionAttribute) 66 | 67 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 68 | gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0) 69 | 70 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 71 | gl.drawElements(gl.TRIANGLES, elements.length, gl.UNSIGNED_SHORT, 0) 72 | 73 | requestAnimationFrame(draw) 74 | } 75 | 76 | draw(performance.now()) 77 | }) 78 | -------------------------------------------------------------------------------- /part-7/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /part-7/vertex.glsl: -------------------------------------------------------------------------------- 1 | attribute vec3 a_vertexPosition; 2 | 3 | uniform float u_time; 4 | 5 | // Declare a new varying for the vertex color. 6 | varying vec4 v_color; 7 | 8 | vec4 model_position; 9 | vec4 translation; 10 | mat4 projection; 11 | mat4 rotation; 12 | 13 | mat4 perspective(float fovy, float aspect, float near, float far) { 14 | float f = 1.0 / tan(fovy / 2.0); 15 | float nf = 1.0 / (near - far); 16 | 17 | vec4 col0 = vec4(f / aspect, 0, 0, 0); 18 | vec4 col1 = vec4(0, f, 0, 0); 19 | vec4 col2 = vec4(0, 0, (far + near) * nf, -1.0); 20 | vec4 col3 = vec4(0, 0, 2.0 * far * near * nf, 0); 21 | 22 | return mat4(col0, col1, col2, col3); 23 | } 24 | 25 | mat4 rotateY(float rad) { 26 | float s = sin(rad); 27 | float c = cos(rad); 28 | 29 | vec4 col0 = vec4(c, 0, -s, 0); 30 | vec4 col1 = vec4(0, 1, 0, 0); 31 | vec4 col2 = vec4(s, 0, c, 0); 32 | vec4 col3 = vec4(0, 0, 0, 1); 33 | 34 | return mat4(col0, col1, col2, col3); 35 | } 36 | 37 | void main() { 38 | model_position = vec4(a_vertexPosition, 1.0); 39 | translation = vec4(0.0, 0.0, -4.0, 1.0); 40 | projection = perspective(0.8, 1.0, 1.0, 50.0); 41 | rotation = rotateY(u_time * 0.001); 42 | 43 | gl_Position = ((model_position * rotation) + translation) * projection; 44 | 45 | // Set the vertex color to the vertex position mapped to the range [0, 1]. 46 | v_color = vec4((a_vertexPosition * 0.5) + 0.5, 1.0); 47 | } 48 | -------------------------------------------------------------------------------- /part-8/fragment.glsl: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | 3 | uniform float u_time; 4 | 5 | varying vec4 v_color; 6 | 7 | // Classic Perlin 3D Noise by Stefan Gustavson 8 | vec4 permute(vec4 x) {return mod(((x*34.0)+1.0)*x, 289.0);} 9 | vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;} 10 | vec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);} 11 | 12 | float cnoise(vec3 P){ 13 | vec3 Pi0 = floor(P); 14 | vec3 Pi1 = Pi0 + vec3(1.0); 15 | Pi0 = mod(Pi0, 289.0); 16 | Pi1 = mod(Pi1, 289.0); 17 | vec3 Pf0 = fract(P); 18 | vec3 Pf1 = Pf0 - vec3(1.0); 19 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); 20 | vec4 iy = vec4(Pi0.yy, Pi1.yy); 21 | vec4 iz0 = Pi0.zzzz; 22 | vec4 iz1 = Pi1.zzzz; 23 | 24 | vec4 ixy = permute(permute(ix) + iy); 25 | vec4 ixy0 = permute(ixy + iz0); 26 | vec4 ixy1 = permute(ixy + iz1); 27 | 28 | vec4 gx0 = ixy0 / 7.0; 29 | vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5; 30 | gx0 = fract(gx0); 31 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); 32 | vec4 sz0 = step(gz0, vec4(0.0)); 33 | gx0 -= sz0 * (step(0.0, gx0) - 0.5); 34 | gy0 -= sz0 * (step(0.0, gy0) - 0.5); 35 | 36 | vec4 gx1 = ixy1 / 7.0; 37 | vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5; 38 | gx1 = fract(gx1); 39 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); 40 | vec4 sz1 = step(gz1, vec4(0.0)); 41 | gx1 -= sz1 * (step(0.0, gx1) - 0.5); 42 | gy1 -= sz1 * (step(0.0, gy1) - 0.5); 43 | 44 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); 45 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); 46 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); 47 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); 48 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); 49 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); 50 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); 51 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); 52 | 53 | vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); 54 | g000 *= norm0.x; 55 | g010 *= norm0.y; 56 | g100 *= norm0.z; 57 | g110 *= norm0.w; 58 | vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); 59 | g001 *= norm1.x; 60 | g011 *= norm1.y; 61 | g101 *= norm1.z; 62 | g111 *= norm1.w; 63 | 64 | float n000 = dot(g000, Pf0); 65 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); 66 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); 67 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); 68 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); 69 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); 70 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); 71 | float n111 = dot(g111, Pf1); 72 | 73 | vec3 fade_xyz = fade(Pf0); 74 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); 75 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); 76 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 77 | return 2.2 * n_xyz; 78 | } 79 | 80 | void main() { 81 | float r = cnoise(vec3(gl_FragCoord.xy * 0.006, u_time * 0.001)) * 0.5; 82 | float g = cnoise(vec3(gl_FragCoord.xy * 0.006, u_time * 0.002)) * 0.5; 83 | float b = cnoise(vec3(gl_FragCoord.xy * 0.006, u_time * 0.003)) * 0.5; 84 | gl_FragColor = v_color + vec4(r, g, b, 0); 85 | } -------------------------------------------------------------------------------- /part-8/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cube Shader 5 | 6 | 7 | 8 | 9 | Update your browser! 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /part-8/main.js: -------------------------------------------------------------------------------- 1 | const positions = [ 2 | -1.0, -1.0, -1.0, 3 | -1.0, -1.0, 1.0, 4 | -1.0, 1.0, -1.0, 5 | -1.0, 1.0, 1.0, 6 | 1.0, -1.0, -1.0, 7 | 1.0, -1.0, 1.0, 8 | 1.0, 1.0, -1.0, 9 | 1.0, 1.0, 1.0 10 | ] 11 | 12 | const elements = [ 13 | 0, 1, 3, 14 | 0, 2, 3, 15 | 5, 4, 6, 16 | 5, 7, 6, 17 | 4, 0, 2, 18 | 4, 6, 2, 19 | 1, 5, 7, 20 | 1, 3, 7, 21 | 2, 6, 7, 22 | 2, 3, 7, 23 | 0, 4, 5, 24 | 0, 1, 5 25 | ] 26 | 27 | const canvas = document.getElementById('canvas') 28 | canvas.width = 1024 29 | canvas.height = 1024 30 | 31 | const vertexSource = request('vertex.glsl') 32 | const fragmentSource = request('fragment.glsl') 33 | 34 | Promise 35 | .all([vertexSource, fragmentSource]) 36 | .then(([vertexSource, fragmentSource]) => { 37 | 38 | const gl = createContext(canvas) 39 | 40 | const vertexShader = createShader(gl, vertexSource, gl.VERTEX_SHADER) 41 | const fragmentShader = createShader(gl, fragmentSource, gl.FRAGMENT_SHADER) 42 | 43 | const program = createProgram(gl, vertexShader, fragmentShader) 44 | gl.useProgram(program) 45 | gl.clearColor(1.0, 0.0, 1.0, 1.0) 46 | gl.clear(gl.COLOR_BUFFER_BIT) 47 | 48 | // Enable depth testing to make sure our triangles overlap properly. 49 | gl.enable(gl.DEPTH_TEST) 50 | 51 | const vertexBuffer = gl.createBuffer() 52 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 53 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW) 54 | 55 | const elementBuffer = gl.createBuffer() 56 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 57 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(elements), gl.STATIC_DRAW) 58 | 59 | const vertexPositionAttribute = gl.getAttribLocation(program, 'a_vertexPosition') 60 | const timeUniform = gl.getUniformLocation(program, 'u_time') 61 | 62 | function draw(t) { 63 | gl.uniform1f(timeUniform, t) 64 | 65 | gl.enableVertexAttribArray(vertexPositionAttribute) 66 | 67 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) 68 | gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0) 69 | 70 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer) 71 | gl.drawElements(gl.TRIANGLES, elements.length, gl.UNSIGNED_SHORT, 0) 72 | 73 | requestAnimationFrame(draw) 74 | } 75 | 76 | draw(performance.now()) 77 | }) 78 | -------------------------------------------------------------------------------- /part-8/style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | body { 12 | display: flex; 13 | justify-content: center; 14 | align-items: center; 15 | overflow: hidden; 16 | } 17 | 18 | canvas { 19 | width: 1024px; 20 | height: 1024px; 21 | } -------------------------------------------------------------------------------- /part-8/vertex.glsl: -------------------------------------------------------------------------------- 1 | attribute vec3 a_vertexPosition; 2 | 3 | uniform float u_time; 4 | 5 | varying vec4 v_color; 6 | 7 | vec4 model_position; 8 | vec4 translation; 9 | mat4 projection; 10 | mat4 rotation; 11 | 12 | mat4 perspective(float fovy, float aspect, float near, float far) { 13 | float f = 1.0 / tan(fovy / 2.0); 14 | float nf = 1.0 / (near - far); 15 | 16 | vec4 col0 = vec4(f / aspect, 0, 0, 0); 17 | vec4 col1 = vec4(0, f, 0, 0); 18 | vec4 col2 = vec4(0, 0, (far + near) * nf, -1.0); 19 | vec4 col3 = vec4(0, 0, 2.0 * far * near * nf, 0); 20 | 21 | return mat4(col0, col1, col2, col3); 22 | } 23 | 24 | mat4 rotateY(float rad) { 25 | float s = sin(rad); 26 | float c = cos(rad); 27 | 28 | vec4 col0 = vec4(c, 0, -s, 0); 29 | vec4 col1 = vec4(0, 1, 0, 0); 30 | vec4 col2 = vec4(s, 0, c, 0); 31 | vec4 col3 = vec4(0, 0, 0, 1); 32 | 33 | return mat4(col0, col1, col2, col3); 34 | } 35 | 36 | void main() { 37 | model_position = vec4(a_vertexPosition, 1.0); 38 | translation = vec4(0.0, 0.0, -4.0, 1.0); 39 | projection = perspective(0.8, 1.0, 1.0, 50.0); 40 | rotation = rotateY(u_time * 0.001); 41 | 42 | gl_Position = ((model_position * rotation) + translation) * projection; 43 | 44 | // Set the vertex color to the vertex position mapped to the range [0, 1]. 45 | v_color = vec4((a_vertexPosition * 0.5) + 0.5, 1.0); 46 | } 47 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | 6 | html, body { 7 | width: 100%; 8 | height: 100%; 9 | } 10 | 11 | html { 12 | font-family: -apple-system, BlinkMacSystemFont, helvetica neue, helvetica, roboto, noto, segoe ui, arial, sans-serif; 13 | font-size: 16px; 14 | line-height: 1.5; 15 | } 16 | 17 | section { 18 | box-sizing: border-box; 19 | display: flex; 20 | flex-direction: column; 21 | justify-content: center; 22 | width: 100%; 23 | min-height: 100vh; 24 | padding: 15vh 5vw; 25 | border-top: solid 2px rgba(0, 0, 0, 0.1); 26 | } 27 | 28 | section:first-of-type { 29 | border-top: none; 30 | } 31 | 32 | aside { 33 | border-style: solid; 34 | border-radius: 0.2em; 35 | border-color: rgba(0, 0, 0, 0.1); 36 | border-width: 0.08em; 37 | padding: 1em; 38 | margin-bottom: 1em; 39 | } 40 | 41 | body.is-presentation-mode aside { 42 | display: none; 43 | } 44 | 45 | img { 46 | width: 100%; 47 | max-width: 1000px; 48 | height: auto; 49 | margin: 1em 0; 50 | } 51 | 52 | img.is-small { 53 | width: 60%; 54 | } 55 | 56 | img.is-very-small { 57 | width: 40%; 58 | } 59 | 60 | a { 61 | color: inherit; 62 | text-decoration: none; 63 | border-bottom: solid rgba(0, 0, 0, 0.2) 0.08em; 64 | padding-bottom: 0.08em; 65 | } 66 | 67 | a:hover { 68 | border-bottom-color: black; 69 | } 70 | 71 | h1 { 72 | font-size: 3rem; 73 | font-weight: 500; 74 | } 75 | 76 | h2 { 77 | font-weight: 500; 78 | font-size: 1.6rem; 79 | } 80 | 81 | h1, h2, h3, p, li { 82 | margin: 0; 83 | margin-bottom: 1em; 84 | max-width: 40em; 85 | } 86 | 87 | p:last-child, 88 | li:last-child { 89 | margin: 0; 90 | } 91 | 92 | ul, ol { 93 | margin-left: 2rem; 94 | } 95 | 96 | code { 97 | font-family: 'SF Mono', 'Menlo', 'Monaco', 'Inconsolata', 'Deja Vu', monospace; 98 | font-size: 0.9em; 99 | } 100 | 101 | .title { 102 | display: flex; 103 | align-items: center; 104 | justify-content: space-between; 105 | margin-bottom: 2em; 106 | } 107 | 108 | .title > * { 109 | margin: 0; 110 | } 111 | 112 | .steps { 113 | background-color: rgba(0, 0, 0, 0.1); 114 | border-radius: 0.2em; 115 | padding: 1em; 116 | margin: 1em 0; 117 | list-style: none; 118 | overflow: hidden; 119 | } 120 | 121 | .steps li { 122 | display: flex; 123 | flex-direction: column; 124 | width: 100%; 125 | } 126 | 127 | .steps li div:first-child { 128 | width: 8em; 129 | flex-shrink: 0; 130 | margin-bottom: 1em; 131 | } 132 | 133 | .steps li div:last-child { 134 | margin-left: 1em; 135 | } 136 | 137 | .table { 138 | margin-left: 0; 139 | margin-bottom: 3em; 140 | } 141 | 142 | .table li { 143 | display: flex; 144 | width: 100%; 145 | } 146 | 147 | .table li div { 148 | width: 50%; 149 | padding-right: 2em; 150 | } 151 | 152 | @media (min-width: 700px) { 153 | html { 154 | font-size: 22px; 155 | } 156 | 157 | section { 158 | padding: 20vh 10vw; 159 | } 160 | 161 | .steps li { 162 | flex-direction: row; 163 | } 164 | 165 | .steps li div:first-child, 166 | .steps li div:last-child { 167 | margin: 0; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Create a new WebGL context. Throws an error if WebGL is not supported. 3 | */ 4 | function createContext(canvas) { 5 | const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl') 6 | if (gl) { 7 | return gl 8 | } 9 | else { 10 | throw new Error('WebGL is not supported.') 11 | } 12 | } 13 | 14 | /** 15 | * Create a new WebGL program from a vertex and fragment shader pair. 16 | */ 17 | function createProgram(gl, vertexShader, fragmentShader) { 18 | const program = gl.createProgram() 19 | gl.attachShader(program, vertexShader) 20 | gl.attachShader(program, fragmentShader) 21 | gl.linkProgram(program) 22 | 23 | if (gl.getProgramParameter(program, gl.LINK_STATUS)) { 24 | return program 25 | } 26 | else { 27 | throw new Error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program)) 28 | } 29 | } 30 | 31 | /** 32 | * Create a new WebGL shader object. 33 | */ 34 | function createShader(gl, source, type) { 35 | const shader = gl.createShader(type) 36 | gl.shaderSource(shader, source) 37 | gl.compileShader(shader) 38 | 39 | if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 40 | return shader 41 | } 42 | else { 43 | const message = gl.getShaderInfoLog(shader) 44 | gl.deleteShader(shader) 45 | throw new Error('Shader compilation failed: ' + message) 46 | } 47 | } 48 | 49 | /** 50 | * Request the file at the given path. Returns a promise that resolves to 51 | * a string of the file's contents. 52 | */ 53 | function request(path) { 54 | return new Promise((resolve, reject) => { 55 | const request = new XMLHttpRequest() 56 | request.open('GET', path) 57 | request.responseType = 'text' 58 | 59 | request.onload = () => { 60 | if (request.status === 200) { 61 | resolve(request.response) 62 | } 63 | else { 64 | reject(new Error('Unable to load path: ' + path)) 65 | } 66 | } 67 | 68 | request.send() 69 | }) 70 | } --------------------------------------------------------------------------------