├── 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 |
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 |
20 | If you've done any graphics programming before — with a library like Processing or HTML5 Canvas — you're probably used to drawing geometric shapes to the screen to compose an image. This is a very intuitive way of drawing but it makes organic looking drawings difficult, and can be slow if you have a lot of layers and effects at once.
21 |
22 |
23 |
24 | Using a drawing library is really just an abstraction of a lower-level technology called shaders.
25 |
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 |
30 | The GPU draws things by calculating the color for every pixel simultaneously, which makes it very fast. The shader itself is the function the GPU uses to perform the calculation. Shaders are typically written in a language called GLSL.
31 |
32 |
33 |
34 |
35 | WebGL is the interface for the GPU.
36 |
37 |
38 | WebGl and similar libraries like OpenGL are how we can tell the GPU to run the shader programs that we wrote in GLSL. Some frameworks like Three , p5 , or Pixi use WebGL under the hood and provide a more streamlined API.
39 |
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 |
44 | It also needs to have very strict constraints so it can run so fast. This means that our program using the WebGL library needs to have a lot of configuration and boilerplate code to even get the GPU to start drawing.
45 |
46 |
47 |
48 | In this tutorial I want to demystify the process of getting a shader program running from scratch. There are some very creative ways to use WebGL that aren't as easy if you happen to be using a framework.
49 |
50 |
51 | Drawing with WebGL and shaders is a fundamentally different technique from most drawing libraries.
52 |
53 |
54 | Regardless of whether you are using WebGL or not, shaders aren't just useful for their speed. They enable a less mechanical-looking way of drawing. We'll see some examples of that later.
55 |
56 |
57 |
58 | Threads
59 |
60 | All processors work like a group of pipes.
61 |
62 |
63 | Programs running on the processor feed inputs into one end of the pipe, and the output comes out the other, but a pipe can only process one input at a time.
64 |
65 |
66 | The GPU is a processor with thousands of small pipes, or threads that work asynchronously.
67 |
68 |
69 | Shaders are the function that turns input into output for a single thread.
70 |
71 |
72 | Individual threads can't know what their neighbors are up to.
73 |
74 |
75 |
76 |
77 | Rasterization
78 |
79 | WebGL uses a rasterization algorithm to turn our model into pixels on the screen.
80 |
81 |
82 |
83 |
84 | It requires that all geometry be composed of triangles.
85 |
86 |
87 | Triangles are great because, mathematically, any three points defines exactly one.
88 |
89 |
90 |
91 | It runs a vertex shader on each vertex of each triangle to position it on the screen.
92 |
93 |
94 |
95 | It removes all triangles that are covered or off-screen.
96 |
97 |
98 | This way we can avoid drawing anything that isn't visible.
99 |
100 |
101 |
102 | It runs a fragment shader on every pixel contained in each of the remaining triangles.
103 |
104 |
105 |
106 |
107 |
108 |
109 | In our demo, we will have three programs:
110 |
111 |
112 |
113 | A JavaScript program running in a browser. It initializes WebGL with programs 2 and 3.
114 |
115 |
116 | A vertex shader.
117 |
118 |
119 | A fragment shader.
120 |
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 |
168 |
169 |
172 |
173 |
174 | Create a new WebGl context for the canvas element called gl
.
175 |
176 |
177 | Create new shader objects from the vertex and fragment shader sources, and a program that uses the two shaders.
178 |
179 |
180 | Clear the canvas with gl.clear
.
181 |
182 |
183 |
184 |
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 |
204 |
205 |
208 |
209 |
210 | Create arrays for the vertices and indices called positions
and elements
211 |
212 |
213 | Create a vertexBuffer
and elementBuffer
, and populate them with the vertex positions and indices.
214 |
215 |
216 |
217 |
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 |
283 | There is an entire discipline of mathematics dedicated partially to matrix multiplication called linear algebra . You can read more about matrix multiplication and how the perspective matrix is constructed here .
284 |
285 |
286 |
287 |
290 |
291 |
292 | Add a function perspective
that produces a new perspective matrix.
293 |
294 |
295 | Translate the vertex position away from the origin of the scene using vector addition.
296 |
297 |
298 | Project the 3d vertex into 2d space by multiplying it by the perspective matrix.
299 |
300 |
301 |
302 |
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 |
316 |
317 |
320 |
321 |
322 | Add a function rotateY
that returns a new rotation matrix.
323 |
324 |
325 | Rotate the model by multiplying each vertex by the rotation matrix.
326 |
327 |
328 |
329 |
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 |
414 |
415 |
416 | WebGL API
417 |
418 |
421 |
422 |
423 |
424 | Rasterization
425 |
426 |
429 |
430 |
431 |
432 | Fragment shaders
433 |
434 |
437 |
438 |
439 |
440 | Matrix math in JavaScript
441 |
442 |
445 |
446 |
447 |
448 | Advanced rendering
449 |
450 |
453 |
454 |
455 |
456 | Web demos
457 |
458 |
461 |
462 |
463 |
464 | Demoscene
465 |
466 |
469 |
470 |
471 |
472 | Marbled Paper
473 |
474 |
477 |
478 |
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 | }
--------------------------------------------------------------------------------