├── README.md └── webgl-examples ├── README.md └── tutorial ├── sample1 ├── index.html └── webgl.css ├── sample2 ├── index.html ├── webgl-demo.js └── webgl.css ├── sample3 ├── index.html ├── webgl-demo.js └── webgl.css ├── sample4 ├── index.html ├── webgl-demo.js └── webgl.css ├── sample5 ├── index.html ├── webgl-demo.js └── webgl.css ├── sample6 ├── cubetexture.png ├── index.html ├── webgl-demo.js └── webgl.css ├── sample7 ├── cubetexture.png ├── index.html ├── webgl-demo.js └── webgl.css ├── sample8 ├── Firefox.mp4 ├── index.html ├── webgl-demo.js └── webgl.css └── tetrahedron ├── index.html └── webgl-demo.js /README.md: -------------------------------------------------------------------------------- 1 | # MDN WebGL examples 2 | 3 | NOTE: This repository is archived and moved into the MDN Web Docs [dom-examples](https://github.com/mdn/dom-examples/tree/main/webgl-examples) repo. 4 | -------------------------------------------------------------------------------- /webgl-examples/README.md: -------------------------------------------------------------------------------- 1 | # MDN WebGL examples 2 | 3 | NOTE: This repository is archived and moved into the MDN Web Docs [dom-examples](https://github.com/mdn/dom-examples/tree/master/webgl-examples) repo. 4 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample1/webgl.css: -------------------------------------------------------------------------------- 1 | canvas { 2 | border: 2px solid black; 3 | background-color: black; 4 | } 5 | video { 6 | display: none; 7 | } -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Demo 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample2/webgl-demo.js: -------------------------------------------------------------------------------- 1 | main(); 2 | 3 | // 4 | // Start here 5 | // 6 | function main() { 7 | const canvas = document.querySelector('#glcanvas'); 8 | const gl = canvas.getContext('webgl'); 9 | 10 | // If we don't have a GL context, give up now 11 | 12 | if (!gl) { 13 | alert('Unable to initialize WebGL. Your browser or machine may not support it.'); 14 | return; 15 | } 16 | 17 | // Vertex shader program 18 | 19 | const vsSource = ` 20 | attribute vec4 aVertexPosition; 21 | 22 | uniform mat4 uModelViewMatrix; 23 | uniform mat4 uProjectionMatrix; 24 | 25 | void main() { 26 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 27 | } 28 | `; 29 | 30 | // Fragment shader program 31 | 32 | const fsSource = ` 33 | void main() { 34 | gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); 35 | } 36 | `; 37 | 38 | // Initialize a shader program; this is where all the lighting 39 | // for the vertices and so forth is established. 40 | const shaderProgram = initShaderProgram(gl, vsSource, fsSource); 41 | 42 | // Collect all the info needed to use the shader program. 43 | // Look up which attribute our shader program is using 44 | // for aVertexPosition and look up uniform locations. 45 | const programInfo = { 46 | program: shaderProgram, 47 | attribLocations: { 48 | vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), 49 | }, 50 | uniformLocations: { 51 | projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), 52 | modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), 53 | }, 54 | }; 55 | 56 | // Here's where we call the routine that builds all the 57 | // objects we'll be drawing. 58 | const buffers = initBuffers(gl); 59 | 60 | // Draw the scene 61 | drawScene(gl, programInfo, buffers); 62 | } 63 | 64 | // 65 | // initBuffers 66 | // 67 | // Initialize the buffers we'll need. For this demo, we just 68 | // have one object -- a simple two-dimensional square. 69 | // 70 | function initBuffers(gl) { 71 | 72 | // Create a buffer for the square's positions. 73 | 74 | const positionBuffer = gl.createBuffer(); 75 | 76 | // Select the positionBuffer as the one to apply buffer 77 | // operations to from here out. 78 | 79 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 80 | 81 | // Now create an array of positions for the square. 82 | 83 | const positions = [ 84 | 1.0, 1.0, 85 | -1.0, 1.0, 86 | 1.0, -1.0, 87 | -1.0, -1.0, 88 | ]; 89 | 90 | // Now pass the list of positions into WebGL to build the 91 | // shape. We do this by creating a Float32Array from the 92 | // JavaScript array, then use it to fill the current buffer. 93 | 94 | gl.bufferData(gl.ARRAY_BUFFER, 95 | new Float32Array(positions), 96 | gl.STATIC_DRAW); 97 | 98 | return { 99 | position: positionBuffer, 100 | }; 101 | } 102 | 103 | // 104 | // Draw the scene. 105 | // 106 | function drawScene(gl, programInfo, buffers) { 107 | gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque 108 | gl.clearDepth(1.0); // Clear everything 109 | gl.enable(gl.DEPTH_TEST); // Enable depth testing 110 | gl.depthFunc(gl.LEQUAL); // Near things obscure far things 111 | 112 | // Clear the canvas before we start drawing on it. 113 | 114 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 115 | 116 | // Create a perspective matrix, a special matrix that is 117 | // used to simulate the distortion of perspective in a camera. 118 | // Our field of view is 45 degrees, with a width/height 119 | // ratio that matches the display size of the canvas 120 | // and we only want to see objects between 0.1 units 121 | // and 100 units away from the camera. 122 | 123 | const fieldOfView = 45 * Math.PI / 180; // in radians 124 | const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; 125 | const zNear = 0.1; 126 | const zFar = 100.0; 127 | const projectionMatrix = mat4.create(); 128 | 129 | // note: glmatrix.js always has the first argument 130 | // as the destination to receive the result. 131 | mat4.perspective(projectionMatrix, 132 | fieldOfView, 133 | aspect, 134 | zNear, 135 | zFar); 136 | 137 | // Set the drawing position to the "identity" point, which is 138 | // the center of the scene. 139 | const modelViewMatrix = mat4.create(); 140 | 141 | // Now move the drawing position a bit to where we want to 142 | // start drawing the square. 143 | 144 | mat4.translate(modelViewMatrix, // destination matrix 145 | modelViewMatrix, // matrix to translate 146 | [-0.0, 0.0, -6.0]); // amount to translate 147 | 148 | // Tell WebGL how to pull out the positions from the position 149 | // buffer into the vertexPosition attribute. 150 | { 151 | const numComponents = 2; 152 | const type = gl.FLOAT; 153 | const normalize = false; 154 | const stride = 0; 155 | const offset = 0; 156 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 157 | gl.vertexAttribPointer( 158 | programInfo.attribLocations.vertexPosition, 159 | numComponents, 160 | type, 161 | normalize, 162 | stride, 163 | offset); 164 | gl.enableVertexAttribArray( 165 | programInfo.attribLocations.vertexPosition); 166 | } 167 | 168 | // Tell WebGL to use our program when drawing 169 | 170 | gl.useProgram(programInfo.program); 171 | 172 | // Set the shader uniforms 173 | 174 | gl.uniformMatrix4fv( 175 | programInfo.uniformLocations.projectionMatrix, 176 | false, 177 | projectionMatrix); 178 | gl.uniformMatrix4fv( 179 | programInfo.uniformLocations.modelViewMatrix, 180 | false, 181 | modelViewMatrix); 182 | 183 | { 184 | const offset = 0; 185 | const vertexCount = 4; 186 | gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount); 187 | } 188 | } 189 | 190 | // 191 | // Initialize a shader program, so WebGL knows how to draw our data 192 | // 193 | function initShaderProgram(gl, vsSource, fsSource) { 194 | const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); 195 | const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); 196 | 197 | // Create the shader program 198 | 199 | const shaderProgram = gl.createProgram(); 200 | gl.attachShader(shaderProgram, vertexShader); 201 | gl.attachShader(shaderProgram, fragmentShader); 202 | gl.linkProgram(shaderProgram); 203 | 204 | // If creating the shader program failed, alert 205 | 206 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 207 | alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); 208 | return null; 209 | } 210 | 211 | return shaderProgram; 212 | } 213 | 214 | // 215 | // creates a shader of the given type, uploads the source and 216 | // compiles it. 217 | // 218 | function loadShader(gl, type, source) { 219 | const shader = gl.createShader(type); 220 | 221 | // Send the source to the shader object 222 | 223 | gl.shaderSource(shader, source); 224 | 225 | // Compile the shader program 226 | 227 | gl.compileShader(shader); 228 | 229 | // See if it compiled successfully 230 | 231 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 232 | alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); 233 | gl.deleteShader(shader); 234 | return null; 235 | } 236 | 237 | return shader; 238 | } 239 | 240 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample2/webgl.css: -------------------------------------------------------------------------------- 1 | canvas { 2 | border: 2px solid black; 3 | background-color: black; 4 | } 5 | video { 6 | display: none; 7 | } -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Demo 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample3/webgl-demo.js: -------------------------------------------------------------------------------- 1 | main(); 2 | 3 | // 4 | // Start here 5 | // 6 | function main() { 7 | const canvas = document.querySelector('#glcanvas'); 8 | const gl = canvas.getContext('webgl'); 9 | 10 | // If we don't have a GL context, give up now 11 | 12 | if (!gl) { 13 | alert('Unable to initialize WebGL. Your browser or machine may not support it.'); 14 | return; 15 | } 16 | 17 | // Vertex shader program 18 | 19 | const vsSource = ` 20 | attribute vec4 aVertexPosition; 21 | attribute vec4 aVertexColor; 22 | 23 | uniform mat4 uModelViewMatrix; 24 | uniform mat4 uProjectionMatrix; 25 | 26 | varying lowp vec4 vColor; 27 | 28 | void main(void) { 29 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 30 | vColor = aVertexColor; 31 | } 32 | `; 33 | 34 | // Fragment shader program 35 | 36 | const fsSource = ` 37 | varying lowp vec4 vColor; 38 | 39 | void main(void) { 40 | gl_FragColor = vColor; 41 | } 42 | `; 43 | 44 | // Initialize a shader program; this is where all the lighting 45 | // for the vertices and so forth is established. 46 | const shaderProgram = initShaderProgram(gl, vsSource, fsSource); 47 | 48 | // Collect all the info needed to use the shader program. 49 | // Look up which attributes our shader program is using 50 | // for aVertexPosition, aVertexColor and also 51 | // look up uniform locations. 52 | const programInfo = { 53 | program: shaderProgram, 54 | attribLocations: { 55 | vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), 56 | vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'), 57 | }, 58 | uniformLocations: { 59 | projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), 60 | modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), 61 | }, 62 | }; 63 | 64 | // Here's where we call the routine that builds all the 65 | // objects we'll be drawing. 66 | const buffers = initBuffers(gl); 67 | 68 | // Draw the scene 69 | drawScene(gl, programInfo, buffers); 70 | } 71 | 72 | // 73 | // initBuffers 74 | // 75 | // Initialize the buffers we'll need. For this demo, we just 76 | // have one object -- a simple two-dimensional square. 77 | // 78 | function initBuffers(gl) { 79 | 80 | // Create a buffer for the square's positions. 81 | 82 | const positionBuffer = gl.createBuffer(); 83 | 84 | // Select the positionBuffer as the one to apply buffer 85 | // operations to from here out. 86 | 87 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 88 | 89 | // Now create an array of positions for the square. 90 | 91 | const positions = [ 92 | 1.0, 1.0, 93 | -1.0, 1.0, 94 | 1.0, -1.0, 95 | -1.0, -1.0, 96 | ]; 97 | 98 | // Now pass the list of positions into WebGL to build the 99 | // shape. We do this by creating a Float32Array from the 100 | // JavaScript array, then use it to fill the current buffer. 101 | 102 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); 103 | 104 | // Now set up the colors for the vertices 105 | 106 | var colors = [ 107 | 1.0, 1.0, 1.0, 1.0, // white 108 | 1.0, 0.0, 0.0, 1.0, // red 109 | 0.0, 1.0, 0.0, 1.0, // green 110 | 0.0, 0.0, 1.0, 1.0, // blue 111 | ]; 112 | 113 | const colorBuffer = gl.createBuffer(); 114 | gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); 115 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); 116 | 117 | return { 118 | position: positionBuffer, 119 | color: colorBuffer, 120 | }; 121 | } 122 | 123 | // 124 | // Draw the scene. 125 | // 126 | function drawScene(gl, programInfo, buffers) { 127 | gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque 128 | gl.clearDepth(1.0); // Clear everything 129 | gl.enable(gl.DEPTH_TEST); // Enable depth testing 130 | gl.depthFunc(gl.LEQUAL); // Near things obscure far things 131 | 132 | // Clear the canvas before we start drawing on it. 133 | 134 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 135 | 136 | // Create a perspective matrix, a special matrix that is 137 | // used to simulate the distortion of perspective in a camera. 138 | // Our field of view is 45 degrees, with a width/height 139 | // ratio that matches the display size of the canvas 140 | // and we only want to see objects between 0.1 units 141 | // and 100 units away from the camera. 142 | 143 | const fieldOfView = 45 * Math.PI / 180; // in radians 144 | const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; 145 | const zNear = 0.1; 146 | const zFar = 100.0; 147 | const projectionMatrix = mat4.create(); 148 | 149 | // note: glmatrix.js always has the first argument 150 | // as the destination to receive the result. 151 | mat4.perspective(projectionMatrix, 152 | fieldOfView, 153 | aspect, 154 | zNear, 155 | zFar); 156 | 157 | // Set the drawing position to the "identity" point, which is 158 | // the center of the scene. 159 | const modelViewMatrix = mat4.create(); 160 | 161 | // Now move the drawing position a bit to where we want to 162 | // start drawing the square. 163 | 164 | mat4.translate(modelViewMatrix, // destination matrix 165 | modelViewMatrix, // matrix to translate 166 | [-0.0, 0.0, -6.0]); // amount to translate 167 | 168 | // Tell WebGL how to pull out the positions from the position 169 | // buffer into the vertexPosition attribute 170 | { 171 | const numComponents = 2; 172 | const type = gl.FLOAT; 173 | const normalize = false; 174 | const stride = 0; 175 | const offset = 0; 176 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 177 | gl.vertexAttribPointer( 178 | programInfo.attribLocations.vertexPosition, 179 | numComponents, 180 | type, 181 | normalize, 182 | stride, 183 | offset); 184 | gl.enableVertexAttribArray( 185 | programInfo.attribLocations.vertexPosition); 186 | } 187 | 188 | // Tell WebGL how to pull out the colors from the color buffer 189 | // into the vertexColor attribute. 190 | { 191 | const numComponents = 4; 192 | const type = gl.FLOAT; 193 | const normalize = false; 194 | const stride = 0; 195 | const offset = 0; 196 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color); 197 | gl.vertexAttribPointer( 198 | programInfo.attribLocations.vertexColor, 199 | numComponents, 200 | type, 201 | normalize, 202 | stride, 203 | offset); 204 | gl.enableVertexAttribArray( 205 | programInfo.attribLocations.vertexColor); 206 | } 207 | 208 | // Tell WebGL to use our program when drawing 209 | 210 | gl.useProgram(programInfo.program); 211 | 212 | // Set the shader uniforms 213 | 214 | gl.uniformMatrix4fv( 215 | programInfo.uniformLocations.projectionMatrix, 216 | false, 217 | projectionMatrix); 218 | gl.uniformMatrix4fv( 219 | programInfo.uniformLocations.modelViewMatrix, 220 | false, 221 | modelViewMatrix); 222 | 223 | { 224 | const offset = 0; 225 | const vertexCount = 4; 226 | gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount); 227 | } 228 | } 229 | 230 | // 231 | // Initialize a shader program, so WebGL knows how to draw our data 232 | // 233 | function initShaderProgram(gl, vsSource, fsSource) { 234 | const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); 235 | const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); 236 | 237 | // Create the shader program 238 | 239 | const shaderProgram = gl.createProgram(); 240 | gl.attachShader(shaderProgram, vertexShader); 241 | gl.attachShader(shaderProgram, fragmentShader); 242 | gl.linkProgram(shaderProgram); 243 | 244 | // If creating the shader program failed, alert 245 | 246 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 247 | alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); 248 | return null; 249 | } 250 | 251 | return shaderProgram; 252 | } 253 | 254 | // 255 | // creates a shader of the given type, uploads the source and 256 | // compiles it. 257 | // 258 | function loadShader(gl, type, source) { 259 | const shader = gl.createShader(type); 260 | 261 | // Send the source to the shader object 262 | 263 | gl.shaderSource(shader, source); 264 | 265 | // Compile the shader program 266 | 267 | gl.compileShader(shader); 268 | 269 | // See if it compiled successfully 270 | 271 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 272 | alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); 273 | gl.deleteShader(shader); 274 | return null; 275 | } 276 | 277 | return shader; 278 | } 279 | 280 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample3/webgl.css: -------------------------------------------------------------------------------- 1 | canvas { 2 | border: 2px solid black; 3 | background-color: black; 4 | } 5 | video { 6 | display: none; 7 | } -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Demo 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample4/webgl-demo.js: -------------------------------------------------------------------------------- 1 | var squareRotation = 0.0; 2 | 3 | main(); 4 | 5 | // 6 | // Start here 7 | // 8 | function main() { 9 | const canvas = document.querySelector('#glcanvas'); 10 | const gl = canvas.getContext('webgl'); 11 | 12 | // If we don't have a GL context, give up now 13 | 14 | if (!gl) { 15 | alert('Unable to initialize WebGL. Your browser or machine may not support it.'); 16 | return; 17 | } 18 | 19 | // Vertex shader program 20 | 21 | const vsSource = ` 22 | attribute vec4 aVertexPosition; 23 | attribute vec4 aVertexColor; 24 | 25 | uniform mat4 uModelViewMatrix; 26 | uniform mat4 uProjectionMatrix; 27 | 28 | varying lowp vec4 vColor; 29 | 30 | void main(void) { 31 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 32 | vColor = aVertexColor; 33 | } 34 | `; 35 | 36 | // Fragment shader program 37 | 38 | const fsSource = ` 39 | varying lowp vec4 vColor; 40 | 41 | void main(void) { 42 | gl_FragColor = vColor; 43 | } 44 | `; 45 | 46 | // Initialize a shader program; this is where all the lighting 47 | // for the vertices and so forth is established. 48 | const shaderProgram = initShaderProgram(gl, vsSource, fsSource); 49 | 50 | // Collect all the info needed to use the shader program. 51 | // Look up which attributes our shader program is using 52 | // for aVertexPosition, aVertexColor and also 53 | // look up uniform locations. 54 | const programInfo = { 55 | program: shaderProgram, 56 | attribLocations: { 57 | vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), 58 | vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'), 59 | }, 60 | uniformLocations: { 61 | projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), 62 | modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), 63 | }, 64 | }; 65 | 66 | // Here's where we call the routine that builds all the 67 | // objects we'll be drawing. 68 | const buffers = initBuffers(gl); 69 | 70 | var then = 0; 71 | 72 | // Draw the scene repeatedly 73 | function render(now) { 74 | now *= 0.001; // convert to seconds 75 | const deltaTime = now - then; 76 | then = now; 77 | 78 | drawScene(gl, programInfo, buffers, deltaTime); 79 | 80 | requestAnimationFrame(render); 81 | } 82 | requestAnimationFrame(render); 83 | } 84 | 85 | // 86 | // initBuffers 87 | // 88 | // Initialize the buffers we'll need. For this demo, we just 89 | // have one object -- a simple two-dimensional square. 90 | // 91 | function initBuffers(gl) { 92 | 93 | // Create a buffer for the square's positions. 94 | 95 | const positionBuffer = gl.createBuffer(); 96 | 97 | // Select the positionBuffer as the one to apply buffer 98 | // operations to from here out. 99 | 100 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 101 | 102 | // Now create an array of positions for the square. 103 | 104 | const positions = [ 105 | 1.0, 1.0, 106 | -1.0, 1.0, 107 | 1.0, -1.0, 108 | -1.0, -1.0, 109 | ]; 110 | 111 | // Now pass the list of positions into WebGL to build the 112 | // shape. We do this by creating a Float32Array from the 113 | // JavaScript array, then use it to fill the current buffer. 114 | 115 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); 116 | 117 | // Now set up the colors for the vertices 118 | 119 | const colors = [ 120 | 1.0, 1.0, 1.0, 1.0, // white 121 | 1.0, 0.0, 0.0, 1.0, // red 122 | 0.0, 1.0, 0.0, 1.0, // green 123 | 0.0, 0.0, 1.0, 1.0, // blue 124 | ]; 125 | 126 | const colorBuffer = gl.createBuffer(); 127 | gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); 128 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); 129 | 130 | return { 131 | position: positionBuffer, 132 | color: colorBuffer, 133 | }; 134 | } 135 | 136 | // 137 | // Draw the scene. 138 | // 139 | function drawScene(gl, programInfo, buffers, deltaTime) { 140 | gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque 141 | gl.clearDepth(1.0); // Clear everything 142 | gl.enable(gl.DEPTH_TEST); // Enable depth testing 143 | gl.depthFunc(gl.LEQUAL); // Near things obscure far things 144 | 145 | // Clear the canvas before we start drawing on it. 146 | 147 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 148 | 149 | // Create a perspective matrix, a special matrix that is 150 | // used to simulate the distortion of perspective in a camera. 151 | // Our field of view is 45 degrees, with a width/height 152 | // ratio that matches the display size of the canvas 153 | // and we only want to see objects between 0.1 units 154 | // and 100 units away from the camera. 155 | 156 | const fieldOfView = 45 * Math.PI / 180; // in radians 157 | const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; 158 | const zNear = 0.1; 159 | const zFar = 100.0; 160 | const projectionMatrix = mat4.create(); 161 | 162 | // note: glmatrix.js always has the first argument 163 | // as the destination to receive the result. 164 | mat4.perspective(projectionMatrix, 165 | fieldOfView, 166 | aspect, 167 | zNear, 168 | zFar); 169 | 170 | // Set the drawing position to the "identity" point, which is 171 | // the center of the scene. 172 | const modelViewMatrix = mat4.create(); 173 | 174 | // Now move the drawing position a bit to where we want to 175 | // start drawing the square. 176 | 177 | mat4.translate(modelViewMatrix, // destination matrix 178 | modelViewMatrix, // matrix to translate 179 | [-0.0, 0.0, -6.0]); // amount to translate 180 | mat4.rotate(modelViewMatrix, // destination matrix 181 | modelViewMatrix, // matrix to rotate 182 | squareRotation, // amount to rotate in radians 183 | [0, 0, 1]); // axis to rotate around 184 | 185 | // Tell WebGL how to pull out the positions from the position 186 | // buffer into the vertexPosition attribute 187 | { 188 | const numComponents = 2; 189 | const type = gl.FLOAT; 190 | const normalize = false; 191 | const stride = 0; 192 | const offset = 0; 193 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 194 | gl.vertexAttribPointer( 195 | programInfo.attribLocations.vertexPosition, 196 | numComponents, 197 | type, 198 | normalize, 199 | stride, 200 | offset); 201 | gl.enableVertexAttribArray( 202 | programInfo.attribLocations.vertexPosition); 203 | } 204 | 205 | // Tell WebGL how to pull out the colors from the color buffer 206 | // into the vertexColor attribute. 207 | { 208 | const numComponents = 4; 209 | const type = gl.FLOAT; 210 | const normalize = false; 211 | const stride = 0; 212 | const offset = 0; 213 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color); 214 | gl.vertexAttribPointer( 215 | programInfo.attribLocations.vertexColor, 216 | numComponents, 217 | type, 218 | normalize, 219 | stride, 220 | offset); 221 | gl.enableVertexAttribArray( 222 | programInfo.attribLocations.vertexColor); 223 | } 224 | 225 | // Tell WebGL to use our program when drawing 226 | 227 | gl.useProgram(programInfo.program); 228 | 229 | // Set the shader uniforms 230 | 231 | gl.uniformMatrix4fv( 232 | programInfo.uniformLocations.projectionMatrix, 233 | false, 234 | projectionMatrix); 235 | gl.uniformMatrix4fv( 236 | programInfo.uniformLocations.modelViewMatrix, 237 | false, 238 | modelViewMatrix); 239 | 240 | { 241 | const offset = 0; 242 | const vertexCount = 4; 243 | gl.drawArrays(gl.TRIANGLE_STRIP, offset, vertexCount); 244 | } 245 | 246 | // Update the rotation for the next draw 247 | 248 | squareRotation += deltaTime; 249 | } 250 | 251 | // 252 | // Initialize a shader program, so WebGL knows how to draw our data 253 | // 254 | function initShaderProgram(gl, vsSource, fsSource) { 255 | const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); 256 | const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); 257 | 258 | // Create the shader program 259 | 260 | const shaderProgram = gl.createProgram(); 261 | gl.attachShader(shaderProgram, vertexShader); 262 | gl.attachShader(shaderProgram, fragmentShader); 263 | gl.linkProgram(shaderProgram); 264 | 265 | // If creating the shader program failed, alert 266 | 267 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 268 | alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); 269 | return null; 270 | } 271 | 272 | return shaderProgram; 273 | } 274 | 275 | // 276 | // creates a shader of the given type, uploads the source and 277 | // compiles it. 278 | // 279 | function loadShader(gl, type, source) { 280 | const shader = gl.createShader(type); 281 | 282 | // Send the source to the shader object 283 | 284 | gl.shaderSource(shader, source); 285 | 286 | // Compile the shader program 287 | 288 | gl.compileShader(shader); 289 | 290 | // See if it compiled successfully 291 | 292 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 293 | alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); 294 | gl.deleteShader(shader); 295 | return null; 296 | } 297 | 298 | return shader; 299 | } 300 | 301 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample4/webgl.css: -------------------------------------------------------------------------------- 1 | canvas { 2 | border: 2px solid black; 3 | background-color: black; 4 | } 5 | video { 6 | display: none; 7 | } -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Demo 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample5/webgl-demo.js: -------------------------------------------------------------------------------- 1 | var cubeRotation = 0.0; 2 | 3 | main(); 4 | 5 | // 6 | // Start here 7 | // 8 | function main() { 9 | const canvas = document.querySelector('#glcanvas'); 10 | const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 11 | 12 | // If we don't have a GL context, give up now 13 | 14 | if (!gl) { 15 | alert('Unable to initialize WebGL. Your browser or machine may not support it.'); 16 | return; 17 | } 18 | 19 | // Vertex shader program 20 | 21 | const vsSource = ` 22 | attribute vec4 aVertexPosition; 23 | attribute vec4 aVertexColor; 24 | 25 | uniform mat4 uModelViewMatrix; 26 | uniform mat4 uProjectionMatrix; 27 | 28 | varying lowp vec4 vColor; 29 | 30 | void main(void) { 31 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 32 | vColor = aVertexColor; 33 | } 34 | `; 35 | 36 | // Fragment shader program 37 | 38 | const fsSource = ` 39 | varying lowp vec4 vColor; 40 | 41 | void main(void) { 42 | gl_FragColor = vColor; 43 | } 44 | `; 45 | 46 | // Initialize a shader program; this is where all the lighting 47 | // for the vertices and so forth is established. 48 | const shaderProgram = initShaderProgram(gl, vsSource, fsSource); 49 | 50 | // Collect all the info needed to use the shader program. 51 | // Look up which attributes our shader program is using 52 | // for aVertexPosition, aVertexColor and also 53 | // look up uniform locations. 54 | const programInfo = { 55 | program: shaderProgram, 56 | attribLocations: { 57 | vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), 58 | vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'), 59 | }, 60 | uniformLocations: { 61 | projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), 62 | modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), 63 | } 64 | }; 65 | 66 | // Here's where we call the routine that builds all the 67 | // objects we'll be drawing. 68 | const buffers = initBuffers(gl); 69 | 70 | var then = 0; 71 | 72 | // Draw the scene repeatedly 73 | function render(now) { 74 | now *= 0.001; // convert to seconds 75 | const deltaTime = now - then; 76 | then = now; 77 | 78 | drawScene(gl, programInfo, buffers, deltaTime); 79 | 80 | requestAnimationFrame(render); 81 | } 82 | requestAnimationFrame(render); 83 | } 84 | 85 | // 86 | // initBuffers 87 | // 88 | // Initialize the buffers we'll need. For this demo, we just 89 | // have one object -- a simple three-dimensional cube. 90 | // 91 | function initBuffers(gl) { 92 | 93 | // Create a buffer for the cube's vertex positions. 94 | 95 | const positionBuffer = gl.createBuffer(); 96 | 97 | // Select the positionBuffer as the one to apply buffer 98 | // operations to from here out. 99 | 100 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 101 | 102 | // Now create an array of positions for the cube. 103 | 104 | const positions = [ 105 | // Front face 106 | -1.0, -1.0, 1.0, 107 | 1.0, -1.0, 1.0, 108 | 1.0, 1.0, 1.0, 109 | -1.0, 1.0, 1.0, 110 | 111 | // Back face 112 | -1.0, -1.0, -1.0, 113 | -1.0, 1.0, -1.0, 114 | 1.0, 1.0, -1.0, 115 | 1.0, -1.0, -1.0, 116 | 117 | // Top face 118 | -1.0, 1.0, -1.0, 119 | -1.0, 1.0, 1.0, 120 | 1.0, 1.0, 1.0, 121 | 1.0, 1.0, -1.0, 122 | 123 | // Bottom face 124 | -1.0, -1.0, -1.0, 125 | 1.0, -1.0, -1.0, 126 | 1.0, -1.0, 1.0, 127 | -1.0, -1.0, 1.0, 128 | 129 | // Right face 130 | 1.0, -1.0, -1.0, 131 | 1.0, 1.0, -1.0, 132 | 1.0, 1.0, 1.0, 133 | 1.0, -1.0, 1.0, 134 | 135 | // Left face 136 | -1.0, -1.0, -1.0, 137 | -1.0, -1.0, 1.0, 138 | -1.0, 1.0, 1.0, 139 | -1.0, 1.0, -1.0, 140 | ]; 141 | 142 | // Now pass the list of positions into WebGL to build the 143 | // shape. We do this by creating a Float32Array from the 144 | // JavaScript array, then use it to fill the current buffer. 145 | 146 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); 147 | 148 | // Now set up the colors for the faces. We'll use solid colors 149 | // for each face. 150 | 151 | const faceColors = [ 152 | [1.0, 1.0, 1.0, 1.0], // Front face: white 153 | [1.0, 0.0, 0.0, 1.0], // Back face: red 154 | [0.0, 1.0, 0.0, 1.0], // Top face: green 155 | [0.0, 0.0, 1.0, 1.0], // Bottom face: blue 156 | [1.0, 1.0, 0.0, 1.0], // Right face: yellow 157 | [1.0, 0.0, 1.0, 1.0], // Left face: purple 158 | ]; 159 | 160 | // Convert the array of colors into a table for all the vertices. 161 | 162 | var colors = []; 163 | 164 | for (var j = 0; j < faceColors.length; ++j) { 165 | const c = faceColors[j]; 166 | 167 | // Repeat each color four times for the four vertices of the face 168 | colors = colors.concat(c, c, c, c); 169 | } 170 | 171 | const colorBuffer = gl.createBuffer(); 172 | gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); 173 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); 174 | 175 | // Build the element array buffer; this specifies the indices 176 | // into the vertex arrays for each face's vertices. 177 | 178 | const indexBuffer = gl.createBuffer(); 179 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 180 | 181 | // This array defines each face as two triangles, using the 182 | // indices into the vertex array to specify each triangle's 183 | // position. 184 | 185 | const indices = [ 186 | 0, 1, 2, 0, 2, 3, // front 187 | 4, 5, 6, 4, 6, 7, // back 188 | 8, 9, 10, 8, 10, 11, // top 189 | 12, 13, 14, 12, 14, 15, // bottom 190 | 16, 17, 18, 16, 18, 19, // right 191 | 20, 21, 22, 20, 22, 23, // left 192 | ]; 193 | 194 | // Now send the element array to GL 195 | 196 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 197 | new Uint16Array(indices), gl.STATIC_DRAW); 198 | 199 | return { 200 | position: positionBuffer, 201 | color: colorBuffer, 202 | indices: indexBuffer, 203 | }; 204 | } 205 | 206 | // 207 | // Draw the scene. 208 | // 209 | function drawScene(gl, programInfo, buffers, deltaTime) { 210 | gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque 211 | gl.clearDepth(1.0); // Clear everything 212 | gl.enable(gl.DEPTH_TEST); // Enable depth testing 213 | gl.depthFunc(gl.LEQUAL); // Near things obscure far things 214 | 215 | // Clear the canvas before we start drawing on it. 216 | 217 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 218 | 219 | // Create a perspective matrix, a special matrix that is 220 | // used to simulate the distortion of perspective in a camera. 221 | // Our field of view is 45 degrees, with a width/height 222 | // ratio that matches the display size of the canvas 223 | // and we only want to see objects between 0.1 units 224 | // and 100 units away from the camera. 225 | 226 | const fieldOfView = 45 * Math.PI / 180; // in radians 227 | const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; 228 | const zNear = 0.1; 229 | const zFar = 100.0; 230 | const projectionMatrix = mat4.create(); 231 | 232 | // note: glmatrix.js always has the first argument 233 | // as the destination to receive the result. 234 | mat4.perspective(projectionMatrix, 235 | fieldOfView, 236 | aspect, 237 | zNear, 238 | zFar); 239 | 240 | // Set the drawing position to the "identity" point, which is 241 | // the center of the scene. 242 | const modelViewMatrix = mat4.create(); 243 | 244 | // Now move the drawing position a bit to where we want to 245 | // start drawing the square. 246 | 247 | mat4.translate(modelViewMatrix, // destination matrix 248 | modelViewMatrix, // matrix to translate 249 | [-0.0, 0.0, -6.0]); // amount to translate 250 | mat4.rotate(modelViewMatrix, // destination matrix 251 | modelViewMatrix, // matrix to rotate 252 | cubeRotation, // amount to rotate in radians 253 | [0, 0, 1]); // axis to rotate around (Z) 254 | mat4.rotate(modelViewMatrix, // destination matrix 255 | modelViewMatrix, // matrix to rotate 256 | cubeRotation * .7,// amount to rotate in radians 257 | [0, 1, 0]); // axis to rotate around (Y) 258 | mat4.rotate(modelViewMatrix, // destination matrix 259 | modelViewMatrix, // matrix to rotate 260 | cubeRotation * .3,// amount to rotate in radians 261 | [1, 0, 0]); // axis to rotate around (X) 262 | 263 | // Tell WebGL how to pull out the positions from the position 264 | // buffer into the vertexPosition attribute 265 | { 266 | const numComponents = 3; 267 | const type = gl.FLOAT; 268 | const normalize = false; 269 | const stride = 0; 270 | const offset = 0; 271 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 272 | gl.vertexAttribPointer( 273 | programInfo.attribLocations.vertexPosition, 274 | numComponents, 275 | type, 276 | normalize, 277 | stride, 278 | offset); 279 | gl.enableVertexAttribArray( 280 | programInfo.attribLocations.vertexPosition); 281 | } 282 | 283 | // Tell WebGL how to pull out the colors from the color buffer 284 | // into the vertexColor attribute. 285 | { 286 | const numComponents = 4; 287 | const type = gl.FLOAT; 288 | const normalize = false; 289 | const stride = 0; 290 | const offset = 0; 291 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color); 292 | gl.vertexAttribPointer( 293 | programInfo.attribLocations.vertexColor, 294 | numComponents, 295 | type, 296 | normalize, 297 | stride, 298 | offset); 299 | gl.enableVertexAttribArray( 300 | programInfo.attribLocations.vertexColor); 301 | } 302 | 303 | // Tell WebGL which indices to use to index the vertices 304 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices); 305 | 306 | // Tell WebGL to use our program when drawing 307 | 308 | gl.useProgram(programInfo.program); 309 | 310 | // Set the shader uniforms 311 | 312 | gl.uniformMatrix4fv( 313 | programInfo.uniformLocations.projectionMatrix, 314 | false, 315 | projectionMatrix); 316 | gl.uniformMatrix4fv( 317 | programInfo.uniformLocations.modelViewMatrix, 318 | false, 319 | modelViewMatrix); 320 | 321 | { 322 | const vertexCount = 36; 323 | const type = gl.UNSIGNED_SHORT; 324 | const offset = 0; 325 | gl.drawElements(gl.TRIANGLES, vertexCount, type, offset); 326 | } 327 | 328 | // Update the rotation for the next draw 329 | 330 | cubeRotation += deltaTime; 331 | } 332 | 333 | // 334 | // Initialize a shader program, so WebGL knows how to draw our data 335 | // 336 | function initShaderProgram(gl, vsSource, fsSource) { 337 | const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); 338 | const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); 339 | 340 | // Create the shader program 341 | 342 | const shaderProgram = gl.createProgram(); 343 | gl.attachShader(shaderProgram, vertexShader); 344 | gl.attachShader(shaderProgram, fragmentShader); 345 | gl.linkProgram(shaderProgram); 346 | 347 | // If creating the shader program failed, alert 348 | 349 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 350 | alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); 351 | return null; 352 | } 353 | 354 | return shaderProgram; 355 | } 356 | 357 | // 358 | // creates a shader of the given type, uploads the source and 359 | // compiles it. 360 | // 361 | function loadShader(gl, type, source) { 362 | const shader = gl.createShader(type); 363 | 364 | // Send the source to the shader object 365 | 366 | gl.shaderSource(shader, source); 367 | 368 | // Compile the shader program 369 | 370 | gl.compileShader(shader); 371 | 372 | // See if it compiled successfully 373 | 374 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 375 | alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); 376 | gl.deleteShader(shader); 377 | return null; 378 | } 379 | 380 | return shader; 381 | } 382 | 383 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample5/webgl.css: -------------------------------------------------------------------------------- 1 | canvas { 2 | border: 2px solid black; 3 | background-color: black; 4 | } 5 | video { 6 | display: none; 7 | } -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample6/cubetexture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webgl-examples/17a7484b5bb5cddee1dce3eb93c8d48925453557/webgl-examples/tutorial/sample6/cubetexture.png -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Demo 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample6/webgl-demo.js: -------------------------------------------------------------------------------- 1 | var cubeRotation = 0.0; 2 | 3 | main(); 4 | 5 | // 6 | // Start here 7 | // 8 | function main() { 9 | const canvas = document.querySelector('#glcanvas'); 10 | const gl = canvas.getContext('webgl'); 11 | 12 | // If we don't have a GL context, give up now 13 | 14 | if (!gl) { 15 | alert('Unable to initialize WebGL. Your browser or machine may not support it.'); 16 | return; 17 | } 18 | 19 | // Vertex shader program 20 | 21 | const vsSource = ` 22 | attribute vec4 aVertexPosition; 23 | attribute vec2 aTextureCoord; 24 | 25 | uniform mat4 uModelViewMatrix; 26 | uniform mat4 uProjectionMatrix; 27 | 28 | varying highp vec2 vTextureCoord; 29 | 30 | void main(void) { 31 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 32 | vTextureCoord = aTextureCoord; 33 | } 34 | `; 35 | 36 | // Fragment shader program 37 | 38 | const fsSource = ` 39 | varying highp vec2 vTextureCoord; 40 | 41 | uniform sampler2D uSampler; 42 | 43 | void main(void) { 44 | gl_FragColor = texture2D(uSampler, vTextureCoord); 45 | } 46 | `; 47 | 48 | // Initialize a shader program; this is where all the lighting 49 | // for the vertices and so forth is established. 50 | const shaderProgram = initShaderProgram(gl, vsSource, fsSource); 51 | 52 | // Collect all the info needed to use the shader program. 53 | // Look up which attributes our shader program is using 54 | // for aVertexPosition, aTextureCoord and also 55 | // look up uniform locations. 56 | const programInfo = { 57 | program: shaderProgram, 58 | attribLocations: { 59 | vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), 60 | textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'), 61 | }, 62 | uniformLocations: { 63 | projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), 64 | modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), 65 | uSampler: gl.getUniformLocation(shaderProgram, 'uSampler'), 66 | } 67 | }; 68 | 69 | // Here's where we call the routine that builds all the 70 | // objects we'll be drawing. 71 | const buffers = initBuffers(gl); 72 | 73 | const texture = loadTexture(gl, 'cubetexture.png'); 74 | 75 | var then = 0; 76 | 77 | // Draw the scene repeatedly 78 | function render(now) { 79 | now *= 0.001; // convert to seconds 80 | const deltaTime = now - then; 81 | then = now; 82 | 83 | drawScene(gl, programInfo, buffers, texture, deltaTime); 84 | 85 | requestAnimationFrame(render); 86 | } 87 | requestAnimationFrame(render); 88 | } 89 | 90 | // 91 | // initBuffers 92 | // 93 | // Initialize the buffers we'll need. For this demo, we just 94 | // have one object -- a simple three-dimensional cube. 95 | // 96 | function initBuffers(gl) { 97 | 98 | // Create a buffer for the cube's vertex positions. 99 | 100 | const positionBuffer = gl.createBuffer(); 101 | 102 | // Select the positionBuffer as the one to apply buffer 103 | // operations to from here out. 104 | 105 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 106 | 107 | // Now create an array of positions for the cube. 108 | 109 | const positions = [ 110 | // Front face 111 | -1.0, -1.0, 1.0, 112 | 1.0, -1.0, 1.0, 113 | 1.0, 1.0, 1.0, 114 | -1.0, 1.0, 1.0, 115 | 116 | // Back face 117 | -1.0, -1.0, -1.0, 118 | -1.0, 1.0, -1.0, 119 | 1.0, 1.0, -1.0, 120 | 1.0, -1.0, -1.0, 121 | 122 | // Top face 123 | -1.0, 1.0, -1.0, 124 | -1.0, 1.0, 1.0, 125 | 1.0, 1.0, 1.0, 126 | 1.0, 1.0, -1.0, 127 | 128 | // Bottom face 129 | -1.0, -1.0, -1.0, 130 | 1.0, -1.0, -1.0, 131 | 1.0, -1.0, 1.0, 132 | -1.0, -1.0, 1.0, 133 | 134 | // Right face 135 | 1.0, -1.0, -1.0, 136 | 1.0, 1.0, -1.0, 137 | 1.0, 1.0, 1.0, 138 | 1.0, -1.0, 1.0, 139 | 140 | // Left face 141 | -1.0, -1.0, -1.0, 142 | -1.0, -1.0, 1.0, 143 | -1.0, 1.0, 1.0, 144 | -1.0, 1.0, -1.0, 145 | ]; 146 | 147 | // Now pass the list of positions into WebGL to build the 148 | // shape. We do this by creating a Float32Array from the 149 | // JavaScript array, then use it to fill the current buffer. 150 | 151 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); 152 | 153 | // Now set up the texture coordinates for the faces. 154 | 155 | const textureCoordBuffer = gl.createBuffer(); 156 | gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer); 157 | 158 | const textureCoordinates = [ 159 | // Front 160 | 0.0, 0.0, 161 | 1.0, 0.0, 162 | 1.0, 1.0, 163 | 0.0, 1.0, 164 | // Back 165 | 0.0, 0.0, 166 | 1.0, 0.0, 167 | 1.0, 1.0, 168 | 0.0, 1.0, 169 | // Top 170 | 0.0, 0.0, 171 | 1.0, 0.0, 172 | 1.0, 1.0, 173 | 0.0, 1.0, 174 | // Bottom 175 | 0.0, 0.0, 176 | 1.0, 0.0, 177 | 1.0, 1.0, 178 | 0.0, 1.0, 179 | // Right 180 | 0.0, 0.0, 181 | 1.0, 0.0, 182 | 1.0, 1.0, 183 | 0.0, 1.0, 184 | // Left 185 | 0.0, 0.0, 186 | 1.0, 0.0, 187 | 1.0, 1.0, 188 | 0.0, 1.0, 189 | ]; 190 | 191 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), 192 | gl.STATIC_DRAW); 193 | 194 | // Build the element array buffer; this specifies the indices 195 | // into the vertex arrays for each face's vertices. 196 | 197 | const indexBuffer = gl.createBuffer(); 198 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 199 | 200 | // This array defines each face as two triangles, using the 201 | // indices into the vertex array to specify each triangle's 202 | // position. 203 | 204 | const indices = [ 205 | 0, 1, 2, 0, 2, 3, // front 206 | 4, 5, 6, 4, 6, 7, // back 207 | 8, 9, 10, 8, 10, 11, // top 208 | 12, 13, 14, 12, 14, 15, // bottom 209 | 16, 17, 18, 16, 18, 19, // right 210 | 20, 21, 22, 20, 22, 23, // left 211 | ]; 212 | 213 | // Now send the element array to GL 214 | 215 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 216 | new Uint16Array(indices), gl.STATIC_DRAW); 217 | 218 | return { 219 | position: positionBuffer, 220 | textureCoord: textureCoordBuffer, 221 | indices: indexBuffer, 222 | }; 223 | } 224 | 225 | // 226 | // Initialize a texture and load an image. 227 | // When the image finished loading copy it into the texture. 228 | // 229 | function loadTexture(gl, url) { 230 | const texture = gl.createTexture(); 231 | gl.bindTexture(gl.TEXTURE_2D, texture); 232 | 233 | // Because images have to be download over the internet 234 | // they might take a moment until they are ready. 235 | // Until then put a single pixel in the texture so we can 236 | // use it immediately. When the image has finished downloading 237 | // we'll update the texture with the contents of the image. 238 | const level = 0; 239 | const internalFormat = gl.RGBA; 240 | const width = 1; 241 | const height = 1; 242 | const border = 0; 243 | const srcFormat = gl.RGBA; 244 | const srcType = gl.UNSIGNED_BYTE; 245 | const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue 246 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, 247 | width, height, border, srcFormat, srcType, 248 | pixel); 249 | 250 | const image = new Image(); 251 | image.onload = function() { 252 | gl.bindTexture(gl.TEXTURE_2D, texture); 253 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, 254 | srcFormat, srcType, image); 255 | 256 | // WebGL1 has different requirements for power of 2 images 257 | // vs non power of 2 images so check if the image is a 258 | // power of 2 in both dimensions. 259 | if (isPowerOf2(image.width) && isPowerOf2(image.height)) { 260 | // Yes, it's a power of 2. Generate mips. 261 | gl.generateMipmap(gl.TEXTURE_2D); 262 | } else { 263 | // No, it's not a power of 2. Turn of mips and set 264 | // wrapping to clamp to edge 265 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 266 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 267 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 268 | } 269 | }; 270 | image.src = url; 271 | 272 | return texture; 273 | } 274 | 275 | function isPowerOf2(value) { 276 | return (value & (value - 1)) == 0; 277 | } 278 | 279 | // 280 | // Draw the scene. 281 | // 282 | function drawScene(gl, programInfo, buffers, texture, deltaTime) { 283 | gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque 284 | gl.clearDepth(1.0); // Clear everything 285 | gl.enable(gl.DEPTH_TEST); // Enable depth testing 286 | gl.depthFunc(gl.LEQUAL); // Near things obscure far things 287 | 288 | // Clear the canvas before we start drawing on it. 289 | 290 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 291 | 292 | // Create a perspective matrix, a special matrix that is 293 | // used to simulate the distortion of perspective in a camera. 294 | // Our field of view is 45 degrees, with a width/height 295 | // ratio that matches the display size of the canvas 296 | // and we only want to see objects between 0.1 units 297 | // and 100 units away from the camera. 298 | 299 | const fieldOfView = 45 * Math.PI / 180; // in radians 300 | const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; 301 | const zNear = 0.1; 302 | const zFar = 100.0; 303 | const projectionMatrix = mat4.create(); 304 | 305 | // note: glmatrix.js always has the first argument 306 | // as the destination to receive the result. 307 | mat4.perspective(projectionMatrix, 308 | fieldOfView, 309 | aspect, 310 | zNear, 311 | zFar); 312 | 313 | // Set the drawing position to the "identity" point, which is 314 | // the center of the scene. 315 | const modelViewMatrix = mat4.create(); 316 | 317 | // Now move the drawing position a bit to where we want to 318 | // start drawing the square. 319 | 320 | mat4.translate(modelViewMatrix, // destination matrix 321 | modelViewMatrix, // matrix to translate 322 | [-0.0, 0.0, -6.0]); // amount to translate 323 | mat4.rotate(modelViewMatrix, // destination matrix 324 | modelViewMatrix, // matrix to rotate 325 | cubeRotation, // amount to rotate in radians 326 | [0, 0, 1]); // axis to rotate around (Z) 327 | mat4.rotate(modelViewMatrix, // destination matrix 328 | modelViewMatrix, // matrix to rotate 329 | cubeRotation * .7,// amount to rotate in radians 330 | [0, 1, 0]); // axis to rotate around (X) 331 | 332 | // Tell WebGL how to pull out the positions from the position 333 | // buffer into the vertexPosition attribute 334 | { 335 | const numComponents = 3; 336 | const type = gl.FLOAT; 337 | const normalize = false; 338 | const stride = 0; 339 | const offset = 0; 340 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 341 | gl.vertexAttribPointer( 342 | programInfo.attribLocations.vertexPosition, 343 | numComponents, 344 | type, 345 | normalize, 346 | stride, 347 | offset); 348 | gl.enableVertexAttribArray( 349 | programInfo.attribLocations.vertexPosition); 350 | } 351 | 352 | // Tell WebGL how to pull out the texture coordinates from 353 | // the texture coordinate buffer into the textureCoord attribute. 354 | { 355 | const numComponents = 2; 356 | const type = gl.FLOAT; 357 | const normalize = false; 358 | const stride = 0; 359 | const offset = 0; 360 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord); 361 | gl.vertexAttribPointer( 362 | programInfo.attribLocations.textureCoord, 363 | numComponents, 364 | type, 365 | normalize, 366 | stride, 367 | offset); 368 | gl.enableVertexAttribArray( 369 | programInfo.attribLocations.textureCoord); 370 | } 371 | 372 | // Tell WebGL which indices to use to index the vertices 373 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices); 374 | 375 | // Tell WebGL to use our program when drawing 376 | 377 | gl.useProgram(programInfo.program); 378 | 379 | // Set the shader uniforms 380 | 381 | gl.uniformMatrix4fv( 382 | programInfo.uniformLocations.projectionMatrix, 383 | false, 384 | projectionMatrix); 385 | gl.uniformMatrix4fv( 386 | programInfo.uniformLocations.modelViewMatrix, 387 | false, 388 | modelViewMatrix); 389 | 390 | // Specify the texture to map onto the faces. 391 | 392 | // Tell WebGL we want to affect texture unit 0 393 | gl.activeTexture(gl.TEXTURE0); 394 | 395 | // Bind the texture to texture unit 0 396 | gl.bindTexture(gl.TEXTURE_2D, texture); 397 | 398 | // Tell the shader we bound the texture to texture unit 0 399 | gl.uniform1i(programInfo.uniformLocations.uSampler, 0); 400 | 401 | { 402 | const vertexCount = 36; 403 | const type = gl.UNSIGNED_SHORT; 404 | const offset = 0; 405 | gl.drawElements(gl.TRIANGLES, vertexCount, type, offset); 406 | } 407 | 408 | // Update the rotation for the next draw 409 | 410 | cubeRotation += deltaTime; 411 | } 412 | 413 | // 414 | // Initialize a shader program, so WebGL knows how to draw our data 415 | // 416 | function initShaderProgram(gl, vsSource, fsSource) { 417 | const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); 418 | const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); 419 | 420 | // Create the shader program 421 | 422 | const shaderProgram = gl.createProgram(); 423 | gl.attachShader(shaderProgram, vertexShader); 424 | gl.attachShader(shaderProgram, fragmentShader); 425 | gl.linkProgram(shaderProgram); 426 | 427 | // If creating the shader program failed, alert 428 | 429 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 430 | alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); 431 | return null; 432 | } 433 | 434 | return shaderProgram; 435 | } 436 | 437 | // 438 | // creates a shader of the given type, uploads the source and 439 | // compiles it. 440 | // 441 | function loadShader(gl, type, source) { 442 | const shader = gl.createShader(type); 443 | 444 | // Send the source to the shader object 445 | 446 | gl.shaderSource(shader, source); 447 | 448 | // Compile the shader program 449 | 450 | gl.compileShader(shader); 451 | 452 | // See if it compiled successfully 453 | 454 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 455 | alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); 456 | gl.deleteShader(shader); 457 | return null; 458 | } 459 | 460 | return shader; 461 | } 462 | 463 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample6/webgl.css: -------------------------------------------------------------------------------- 1 | canvas { 2 | border: 2px solid black; 3 | background-color: black; 4 | } 5 | video { 6 | display: none; 7 | } -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample7/cubetexture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webgl-examples/17a7484b5bb5cddee1dce3eb93c8d48925453557/webgl-examples/tutorial/sample7/cubetexture.png -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample7/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Demo 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample7/webgl-demo.js: -------------------------------------------------------------------------------- 1 | var cubeRotation = 0.0; 2 | 3 | main(); 4 | 5 | // 6 | // Start here 7 | // 8 | function main() { 9 | const canvas = document.querySelector('#glcanvas'); 10 | const gl = canvas.getContext('webgl'); 11 | 12 | // If we don't have a GL context, give up now 13 | 14 | if (!gl) { 15 | alert('Unable to initialize WebGL. Your browser or machine may not support it.'); 16 | return; 17 | } 18 | 19 | // Vertex shader program 20 | 21 | const vsSource = ` 22 | attribute vec4 aVertexPosition; 23 | attribute vec3 aVertexNormal; 24 | attribute vec2 aTextureCoord; 25 | 26 | uniform mat4 uNormalMatrix; 27 | uniform mat4 uModelViewMatrix; 28 | uniform mat4 uProjectionMatrix; 29 | 30 | varying highp vec2 vTextureCoord; 31 | varying highp vec3 vLighting; 32 | 33 | void main(void) { 34 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 35 | vTextureCoord = aTextureCoord; 36 | 37 | // Apply lighting effect 38 | 39 | highp vec3 ambientLight = vec3(0.3, 0.3, 0.3); 40 | highp vec3 directionalLightColor = vec3(1, 1, 1); 41 | highp vec3 directionalVector = normalize(vec3(0.85, 0.8, 0.75)); 42 | 43 | highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0); 44 | 45 | highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0); 46 | vLighting = ambientLight + (directionalLightColor * directional); 47 | } 48 | `; 49 | 50 | // Fragment shader program 51 | 52 | const fsSource = ` 53 | varying highp vec2 vTextureCoord; 54 | varying highp vec3 vLighting; 55 | 56 | uniform sampler2D uSampler; 57 | 58 | void main(void) { 59 | highp vec4 texelColor = texture2D(uSampler, vTextureCoord); 60 | 61 | gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a); 62 | } 63 | `; 64 | 65 | // Initialize a shader program; this is where all the lighting 66 | // for the vertices and so forth is established. 67 | const shaderProgram = initShaderProgram(gl, vsSource, fsSource); 68 | 69 | // Collect all the info needed to use the shader program. 70 | // Look up which attributes our shader program is using 71 | // for aVertexPosition, aVertexNormal, aTextureCoord, 72 | // and look up uniform locations. 73 | const programInfo = { 74 | program: shaderProgram, 75 | attribLocations: { 76 | vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), 77 | vertexNormal: gl.getAttribLocation(shaderProgram, 'aVertexNormal'), 78 | textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'), 79 | }, 80 | uniformLocations: { 81 | projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), 82 | modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), 83 | normalMatrix: gl.getUniformLocation(shaderProgram, 'uNormalMatrix'), 84 | uSampler: gl.getUniformLocation(shaderProgram, 'uSampler'), 85 | } 86 | }; 87 | 88 | // Here's where we call the routine that builds all the 89 | // objects we'll be drawing. 90 | const buffers = initBuffers(gl); 91 | 92 | const texture = loadTexture(gl, 'cubetexture.png'); 93 | 94 | var then = 0; 95 | 96 | // Draw the scene repeatedly 97 | function render(now) { 98 | now *= 0.001; // convert to seconds 99 | const deltaTime = now - then; 100 | then = now; 101 | 102 | drawScene(gl, programInfo, buffers, texture, deltaTime); 103 | 104 | requestAnimationFrame(render); 105 | } 106 | requestAnimationFrame(render); 107 | } 108 | 109 | // 110 | // initBuffers 111 | // 112 | // Initialize the buffers we'll need. For this demo, we just 113 | // have one object -- a simple three-dimensional cube. 114 | // 115 | function initBuffers(gl) { 116 | 117 | // Create a buffer for the cube's vertex positions. 118 | 119 | const positionBuffer = gl.createBuffer(); 120 | 121 | // Select the positionBuffer as the one to apply buffer 122 | // operations to from here out. 123 | 124 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 125 | 126 | // Now create an array of positions for the cube. 127 | 128 | const positions = [ 129 | // Front face 130 | -1.0, -1.0, 1.0, 131 | 1.0, -1.0, 1.0, 132 | 1.0, 1.0, 1.0, 133 | -1.0, 1.0, 1.0, 134 | 135 | // Back face 136 | -1.0, -1.0, -1.0, 137 | -1.0, 1.0, -1.0, 138 | 1.0, 1.0, -1.0, 139 | 1.0, -1.0, -1.0, 140 | 141 | // Top face 142 | -1.0, 1.0, -1.0, 143 | -1.0, 1.0, 1.0, 144 | 1.0, 1.0, 1.0, 145 | 1.0, 1.0, -1.0, 146 | 147 | // Bottom face 148 | -1.0, -1.0, -1.0, 149 | 1.0, -1.0, -1.0, 150 | 1.0, -1.0, 1.0, 151 | -1.0, -1.0, 1.0, 152 | 153 | // Right face 154 | 1.0, -1.0, -1.0, 155 | 1.0, 1.0, -1.0, 156 | 1.0, 1.0, 1.0, 157 | 1.0, -1.0, 1.0, 158 | 159 | // Left face 160 | -1.0, -1.0, -1.0, 161 | -1.0, -1.0, 1.0, 162 | -1.0, 1.0, 1.0, 163 | -1.0, 1.0, -1.0, 164 | ]; 165 | 166 | // Now pass the list of positions into WebGL to build the 167 | // shape. We do this by creating a Float32Array from the 168 | // JavaScript array, then use it to fill the current buffer. 169 | 170 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); 171 | 172 | // Set up the normals for the vertices, so that we can compute lighting. 173 | 174 | const normalBuffer = gl.createBuffer(); 175 | gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer); 176 | 177 | const vertexNormals = [ 178 | // Front 179 | 0.0, 0.0, 1.0, 180 | 0.0, 0.0, 1.0, 181 | 0.0, 0.0, 1.0, 182 | 0.0, 0.0, 1.0, 183 | 184 | // Back 185 | 0.0, 0.0, -1.0, 186 | 0.0, 0.0, -1.0, 187 | 0.0, 0.0, -1.0, 188 | 0.0, 0.0, -1.0, 189 | 190 | // Top 191 | 0.0, 1.0, 0.0, 192 | 0.0, 1.0, 0.0, 193 | 0.0, 1.0, 0.0, 194 | 0.0, 1.0, 0.0, 195 | 196 | // Bottom 197 | 0.0, -1.0, 0.0, 198 | 0.0, -1.0, 0.0, 199 | 0.0, -1.0, 0.0, 200 | 0.0, -1.0, 0.0, 201 | 202 | // Right 203 | 1.0, 0.0, 0.0, 204 | 1.0, 0.0, 0.0, 205 | 1.0, 0.0, 0.0, 206 | 1.0, 0.0, 0.0, 207 | 208 | // Left 209 | -1.0, 0.0, 0.0, 210 | -1.0, 0.0, 0.0, 211 | -1.0, 0.0, 0.0, 212 | -1.0, 0.0, 0.0 213 | ]; 214 | 215 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals), 216 | gl.STATIC_DRAW); 217 | 218 | // Now set up the texture coordinates for the faces. 219 | 220 | const textureCoordBuffer = gl.createBuffer(); 221 | gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer); 222 | 223 | const textureCoordinates = [ 224 | // Front 225 | 0.0, 0.0, 226 | 1.0, 0.0, 227 | 1.0, 1.0, 228 | 0.0, 1.0, 229 | // Back 230 | 0.0, 0.0, 231 | 1.0, 0.0, 232 | 1.0, 1.0, 233 | 0.0, 1.0, 234 | // Top 235 | 0.0, 0.0, 236 | 1.0, 0.0, 237 | 1.0, 1.0, 238 | 0.0, 1.0, 239 | // Bottom 240 | 0.0, 0.0, 241 | 1.0, 0.0, 242 | 1.0, 1.0, 243 | 0.0, 1.0, 244 | // Right 245 | 0.0, 0.0, 246 | 1.0, 0.0, 247 | 1.0, 1.0, 248 | 0.0, 1.0, 249 | // Left 250 | 0.0, 0.0, 251 | 1.0, 0.0, 252 | 1.0, 1.0, 253 | 0.0, 1.0, 254 | ]; 255 | 256 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), 257 | gl.STATIC_DRAW); 258 | 259 | // Build the element array buffer; this specifies the indices 260 | // into the vertex arrays for each face's vertices. 261 | 262 | const indexBuffer = gl.createBuffer(); 263 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 264 | 265 | // This array defines each face as two triangles, using the 266 | // indices into the vertex array to specify each triangle's 267 | // position. 268 | 269 | const indices = [ 270 | 0, 1, 2, 0, 2, 3, // front 271 | 4, 5, 6, 4, 6, 7, // back 272 | 8, 9, 10, 8, 10, 11, // top 273 | 12, 13, 14, 12, 14, 15, // bottom 274 | 16, 17, 18, 16, 18, 19, // right 275 | 20, 21, 22, 20, 22, 23, // left 276 | ]; 277 | 278 | // Now send the element array to GL 279 | 280 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 281 | new Uint16Array(indices), gl.STATIC_DRAW); 282 | 283 | return { 284 | position: positionBuffer, 285 | normal: normalBuffer, 286 | textureCoord: textureCoordBuffer, 287 | indices: indexBuffer, 288 | }; 289 | } 290 | 291 | // 292 | // Initialize a texture and load an image. 293 | // When the image finished loading copy it into the texture. 294 | // 295 | function loadTexture(gl, url) { 296 | const texture = gl.createTexture(); 297 | gl.bindTexture(gl.TEXTURE_2D, texture); 298 | 299 | // Because images have to be download over the internet 300 | // they might take a moment until they are ready. 301 | // Until then put a single pixel in the texture so we can 302 | // use it immediately. When the image has finished downloading 303 | // we'll update the texture with the contents of the image. 304 | const level = 0; 305 | const internalFormat = gl.RGBA; 306 | const width = 1; 307 | const height = 1; 308 | const border = 0; 309 | const srcFormat = gl.RGBA; 310 | const srcType = gl.UNSIGNED_BYTE; 311 | const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue 312 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, 313 | width, height, border, srcFormat, srcType, 314 | pixel); 315 | 316 | const image = new Image(); 317 | image.onload = function() { 318 | gl.bindTexture(gl.TEXTURE_2D, texture); 319 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, 320 | srcFormat, srcType, image); 321 | 322 | // WebGL1 has different requirements for power of 2 images 323 | // vs non power of 2 images so check if the image is a 324 | // power of 2 in both dimensions. 325 | if (isPowerOf2(image.width) && isPowerOf2(image.height)) { 326 | // Yes, it's a power of 2. Generate mips. 327 | gl.generateMipmap(gl.TEXTURE_2D); 328 | } else { 329 | // No, it's not a power of 2. Turn of mips and set 330 | // wrapping to clamp to edge 331 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 332 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 333 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 334 | } 335 | }; 336 | image.src = url; 337 | 338 | return texture; 339 | } 340 | 341 | function isPowerOf2(value) { 342 | return (value & (value - 1)) == 0; 343 | } 344 | 345 | // 346 | // Draw the scene. 347 | // 348 | function drawScene(gl, programInfo, buffers, texture, deltaTime) { 349 | gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque 350 | gl.clearDepth(1.0); // Clear everything 351 | gl.enable(gl.DEPTH_TEST); // Enable depth testing 352 | gl.depthFunc(gl.LEQUAL); // Near things obscure far things 353 | 354 | // Clear the canvas before we start drawing on it. 355 | 356 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 357 | 358 | // Create a perspective matrix, a special matrix that is 359 | // used to simulate the distortion of perspective in a camera. 360 | // Our field of view is 45 degrees, with a width/height 361 | // ratio that matches the display size of the canvas 362 | // and we only want to see objects between 0.1 units 363 | // and 100 units away from the camera. 364 | 365 | const fieldOfView = 45 * Math.PI / 180; // in radians 366 | const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; 367 | const zNear = 0.1; 368 | const zFar = 100.0; 369 | const projectionMatrix = mat4.create(); 370 | 371 | // note: glmatrix.js always has the first argument 372 | // as the destination to receive the result. 373 | mat4.perspective(projectionMatrix, 374 | fieldOfView, 375 | aspect, 376 | zNear, 377 | zFar); 378 | 379 | // Set the drawing position to the "identity" point, which is 380 | // the center of the scene. 381 | const modelViewMatrix = mat4.create(); 382 | 383 | // Now move the drawing position a bit to where we want to 384 | // start drawing the square. 385 | 386 | mat4.translate(modelViewMatrix, // destination matrix 387 | modelViewMatrix, // matrix to translate 388 | [-0.0, 0.0, -6.0]); // amount to translate 389 | mat4.rotate(modelViewMatrix, // destination matrix 390 | modelViewMatrix, // matrix to rotate 391 | cubeRotation, // amount to rotate in radians 392 | [0, 0, 1]); // axis to rotate around (Z) 393 | mat4.rotate(modelViewMatrix, // destination matrix 394 | modelViewMatrix, // matrix to rotate 395 | cubeRotation * .7,// amount to rotate in radians 396 | [0, 1, 0]); // axis to rotate around (X) 397 | 398 | const normalMatrix = mat4.create(); 399 | mat4.invert(normalMatrix, modelViewMatrix); 400 | mat4.transpose(normalMatrix, normalMatrix); 401 | 402 | // Tell WebGL how to pull out the positions from the position 403 | // buffer into the vertexPosition attribute 404 | { 405 | const numComponents = 3; 406 | const type = gl.FLOAT; 407 | const normalize = false; 408 | const stride = 0; 409 | const offset = 0; 410 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 411 | gl.vertexAttribPointer( 412 | programInfo.attribLocations.vertexPosition, 413 | numComponents, 414 | type, 415 | normalize, 416 | stride, 417 | offset); 418 | gl.enableVertexAttribArray( 419 | programInfo.attribLocations.vertexPosition); 420 | } 421 | 422 | // Tell WebGL how to pull out the texture coordinates from 423 | // the texture coordinate buffer into the textureCoord attribute. 424 | { 425 | const numComponents = 2; 426 | const type = gl.FLOAT; 427 | const normalize = false; 428 | const stride = 0; 429 | const offset = 0; 430 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord); 431 | gl.vertexAttribPointer( 432 | programInfo.attribLocations.textureCoord, 433 | numComponents, 434 | type, 435 | normalize, 436 | stride, 437 | offset); 438 | gl.enableVertexAttribArray( 439 | programInfo.attribLocations.textureCoord); 440 | } 441 | 442 | // Tell WebGL how to pull out the normals from 443 | // the normal buffer into the vertexNormal attribute. 444 | { 445 | const numComponents = 3; 446 | const type = gl.FLOAT; 447 | const normalize = false; 448 | const stride = 0; 449 | const offset = 0; 450 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normal); 451 | gl.vertexAttribPointer( 452 | programInfo.attribLocations.vertexNormal, 453 | numComponents, 454 | type, 455 | normalize, 456 | stride, 457 | offset); 458 | gl.enableVertexAttribArray( 459 | programInfo.attribLocations.vertexNormal); 460 | } 461 | 462 | // Tell WebGL which indices to use to index the vertices 463 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices); 464 | 465 | // Tell WebGL to use our program when drawing 466 | 467 | gl.useProgram(programInfo.program); 468 | 469 | // Set the shader uniforms 470 | 471 | gl.uniformMatrix4fv( 472 | programInfo.uniformLocations.projectionMatrix, 473 | false, 474 | projectionMatrix); 475 | gl.uniformMatrix4fv( 476 | programInfo.uniformLocations.modelViewMatrix, 477 | false, 478 | modelViewMatrix); 479 | gl.uniformMatrix4fv( 480 | programInfo.uniformLocations.normalMatrix, 481 | false, 482 | normalMatrix); 483 | 484 | // Specify the texture to map onto the faces. 485 | 486 | // Tell WebGL we want to affect texture unit 0 487 | gl.activeTexture(gl.TEXTURE0); 488 | 489 | // Bind the texture to texture unit 0 490 | gl.bindTexture(gl.TEXTURE_2D, texture); 491 | 492 | // Tell the shader we bound the texture to texture unit 0 493 | gl.uniform1i(programInfo.uniformLocations.uSampler, 0); 494 | 495 | { 496 | const vertexCount = 36; 497 | const type = gl.UNSIGNED_SHORT; 498 | const offset = 0; 499 | gl.drawElements(gl.TRIANGLES, vertexCount, type, offset); 500 | } 501 | 502 | // Update the rotation for the next draw 503 | 504 | cubeRotation += deltaTime; 505 | } 506 | 507 | // 508 | // Initialize a shader program, so WebGL knows how to draw our data 509 | // 510 | function initShaderProgram(gl, vsSource, fsSource) { 511 | const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); 512 | const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); 513 | 514 | // Create the shader program 515 | 516 | const shaderProgram = gl.createProgram(); 517 | gl.attachShader(shaderProgram, vertexShader); 518 | gl.attachShader(shaderProgram, fragmentShader); 519 | gl.linkProgram(shaderProgram); 520 | 521 | // If creating the shader program failed, alert 522 | 523 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 524 | alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); 525 | return null; 526 | } 527 | 528 | return shaderProgram; 529 | } 530 | 531 | // 532 | // creates a shader of the given type, uploads the source and 533 | // compiles it. 534 | // 535 | function loadShader(gl, type, source) { 536 | const shader = gl.createShader(type); 537 | 538 | // Send the source to the shader object 539 | 540 | gl.shaderSource(shader, source); 541 | 542 | // Compile the shader program 543 | 544 | gl.compileShader(shader); 545 | 546 | // See if it compiled successfully 547 | 548 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 549 | alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); 550 | gl.deleteShader(shader); 551 | return null; 552 | } 553 | 554 | return shader; 555 | } 556 | 557 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample7/webgl.css: -------------------------------------------------------------------------------- 1 | canvas { 2 | border: 2px solid black; 3 | background-color: black; 4 | } 5 | video { 6 | display: none; 7 | } -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample8/Firefox.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdn/webgl-examples/17a7484b5bb5cddee1dce3eb93c8d48925453557/webgl-examples/tutorial/sample8/Firefox.mp4 -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample8/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Demo 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample8/webgl-demo.js: -------------------------------------------------------------------------------- 1 | var cubeRotation = 0.0; 2 | // will set to true when video can be copied to texture 3 | var copyVideo = false; 4 | 5 | main(); 6 | 7 | // 8 | // Start here 9 | // 10 | function main() { 11 | const canvas = document.querySelector('#glcanvas'); 12 | const gl = canvas.getContext('webgl'); 13 | 14 | // If we don't have a GL context, give up now 15 | 16 | if (!gl) { 17 | alert('Unable to initialize WebGL. Your browser or machine may not support it.'); 18 | return; 19 | } 20 | 21 | // Vertex shader program 22 | 23 | const vsSource = ` 24 | attribute vec4 aVertexPosition; 25 | attribute vec3 aVertexNormal; 26 | attribute vec2 aTextureCoord; 27 | 28 | uniform mat4 uNormalMatrix; 29 | uniform mat4 uModelViewMatrix; 30 | uniform mat4 uProjectionMatrix; 31 | 32 | varying highp vec2 vTextureCoord; 33 | varying highp vec3 vLighting; 34 | 35 | void main(void) { 36 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 37 | vTextureCoord = aTextureCoord; 38 | 39 | // Apply lighting effect 40 | 41 | highp vec3 ambientLight = vec3(0.3, 0.3, 0.3); 42 | highp vec3 directionalLightColor = vec3(1, 1, 1); 43 | highp vec3 directionalVector = normalize(vec3(0.85, 0.8, 0.75)); 44 | 45 | highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0); 46 | 47 | highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0); 48 | vLighting = ambientLight + (directionalLightColor * directional); 49 | } 50 | `; 51 | 52 | // Fragment shader program 53 | 54 | const fsSource = ` 55 | varying highp vec2 vTextureCoord; 56 | varying highp vec3 vLighting; 57 | 58 | uniform sampler2D uSampler; 59 | 60 | void main(void) { 61 | highp vec4 texelColor = texture2D(uSampler, vTextureCoord); 62 | 63 | gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a); 64 | } 65 | `; 66 | 67 | // Initialize a shader program; this is where all the lighting 68 | // for the vertices and so forth is established. 69 | const shaderProgram = initShaderProgram(gl, vsSource, fsSource); 70 | 71 | // Collect all the info needed to use the shader program. 72 | // Look up which attributes our shader program is using 73 | // for aVertexPosition, aVertexNormal, aTextureCoord, 74 | // and look up uniform locations. 75 | const programInfo = { 76 | program: shaderProgram, 77 | attribLocations: { 78 | vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), 79 | vertexNormal: gl.getAttribLocation(shaderProgram, 'aVertexNormal'), 80 | textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'), 81 | }, 82 | uniformLocations: { 83 | projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), 84 | modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), 85 | normalMatrix: gl.getUniformLocation(shaderProgram, 'uNormalMatrix'), 86 | uSampler: gl.getUniformLocation(shaderProgram, 'uSampler'), 87 | } 88 | }; 89 | 90 | // Here's where we call the routine that builds all the 91 | // objects we'll be drawing. 92 | const buffers = initBuffers(gl); 93 | 94 | const texture = initTexture(gl); 95 | 96 | const video = setupVideo('Firefox.mp4'); 97 | 98 | var then = 0; 99 | 100 | // Draw the scene repeatedly 101 | function render(now) { 102 | now *= 0.001; // convert to seconds 103 | const deltaTime = now - then; 104 | then = now; 105 | 106 | if (copyVideo) { 107 | updateTexture(gl, texture, video); 108 | } 109 | 110 | drawScene(gl, programInfo, buffers, texture, deltaTime); 111 | 112 | requestAnimationFrame(render); 113 | } 114 | requestAnimationFrame(render); 115 | } 116 | 117 | function setupVideo(url) { 118 | const video = document.createElement('video'); 119 | 120 | var playing = false; 121 | var timeupdate = false; 122 | 123 | video.autoplay = true; 124 | video.muted = true; 125 | video.loop = true; 126 | 127 | // Waiting for these 2 events ensures 128 | // there is data in the video 129 | 130 | video.addEventListener('playing', function() { 131 | playing = true; 132 | checkReady(); 133 | }, true); 134 | 135 | video.addEventListener('timeupdate', function() { 136 | timeupdate = true; 137 | checkReady(); 138 | }, true); 139 | 140 | video.src = url; 141 | video.play(); 142 | 143 | function checkReady() { 144 | if (playing && timeupdate) { 145 | copyVideo = true; 146 | } 147 | } 148 | 149 | return video; 150 | } 151 | 152 | // 153 | // initBuffers 154 | // 155 | // Initialize the buffers we'll need. For this demo, we just 156 | // have one object -- a simple three-dimensional cube. 157 | // 158 | function initBuffers(gl) { 159 | 160 | // Create a buffer for the cube's vertex positions. 161 | 162 | const positionBuffer = gl.createBuffer(); 163 | 164 | // Select the positionBuffer as the one to apply buffer 165 | // operations to from here out. 166 | 167 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 168 | 169 | // Now create an array of positions for the cube. 170 | 171 | const positions = [ 172 | // Front face 173 | -1.0, -1.0, 1.0, 174 | 1.0, -1.0, 1.0, 175 | 1.0, 1.0, 1.0, 176 | -1.0, 1.0, 1.0, 177 | 178 | // Back face 179 | -1.0, -1.0, -1.0, 180 | -1.0, 1.0, -1.0, 181 | 1.0, 1.0, -1.0, 182 | 1.0, -1.0, -1.0, 183 | 184 | // Top face 185 | -1.0, 1.0, -1.0, 186 | -1.0, 1.0, 1.0, 187 | 1.0, 1.0, 1.0, 188 | 1.0, 1.0, -1.0, 189 | 190 | // Bottom face 191 | -1.0, -1.0, -1.0, 192 | 1.0, -1.0, -1.0, 193 | 1.0, -1.0, 1.0, 194 | -1.0, -1.0, 1.0, 195 | 196 | // Right face 197 | 1.0, -1.0, -1.0, 198 | 1.0, 1.0, -1.0, 199 | 1.0, 1.0, 1.0, 200 | 1.0, -1.0, 1.0, 201 | 202 | // Left face 203 | -1.0, -1.0, -1.0, 204 | -1.0, -1.0, 1.0, 205 | -1.0, 1.0, 1.0, 206 | -1.0, 1.0, -1.0, 207 | ]; 208 | 209 | // Now pass the list of positions into WebGL to build the 210 | // shape. We do this by creating a Float32Array from the 211 | // JavaScript array, then use it to fill the current buffer. 212 | 213 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); 214 | 215 | // Set up the normals for the vertices, so that we can compute lighting. 216 | 217 | const normalBuffer = gl.createBuffer(); 218 | gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer); 219 | 220 | const vertexNormals = [ 221 | // Front 222 | 0.0, 0.0, 1.0, 223 | 0.0, 0.0, 1.0, 224 | 0.0, 0.0, 1.0, 225 | 0.0, 0.0, 1.0, 226 | 227 | // Back 228 | 0.0, 0.0, -1.0, 229 | 0.0, 0.0, -1.0, 230 | 0.0, 0.0, -1.0, 231 | 0.0, 0.0, -1.0, 232 | 233 | // Top 234 | 0.0, 1.0, 0.0, 235 | 0.0, 1.0, 0.0, 236 | 0.0, 1.0, 0.0, 237 | 0.0, 1.0, 0.0, 238 | 239 | // Bottom 240 | 0.0, -1.0, 0.0, 241 | 0.0, -1.0, 0.0, 242 | 0.0, -1.0, 0.0, 243 | 0.0, -1.0, 0.0, 244 | 245 | // Right 246 | 1.0, 0.0, 0.0, 247 | 1.0, 0.0, 0.0, 248 | 1.0, 0.0, 0.0, 249 | 1.0, 0.0, 0.0, 250 | 251 | // Left 252 | -1.0, 0.0, 0.0, 253 | -1.0, 0.0, 0.0, 254 | -1.0, 0.0, 0.0, 255 | -1.0, 0.0, 0.0, 256 | ]; 257 | 258 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals), 259 | gl.STATIC_DRAW); 260 | 261 | // Now set up the texture coordinates for the faces. 262 | 263 | const textureCoordBuffer = gl.createBuffer(); 264 | gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer); 265 | 266 | const textureCoordinates = [ 267 | // Front 268 | 0.0, 0.0, 269 | 1.0, 0.0, 270 | 1.0, 1.0, 271 | 0.0, 1.0, 272 | // Back 273 | 0.0, 0.0, 274 | 1.0, 0.0, 275 | 1.0, 1.0, 276 | 0.0, 1.0, 277 | // Top 278 | 0.0, 0.0, 279 | 1.0, 0.0, 280 | 1.0, 1.0, 281 | 0.0, 1.0, 282 | // Bottom 283 | 0.0, 0.0, 284 | 1.0, 0.0, 285 | 1.0, 1.0, 286 | 0.0, 1.0, 287 | // Right 288 | 0.0, 0.0, 289 | 1.0, 0.0, 290 | 1.0, 1.0, 291 | 0.0, 1.0, 292 | // Left 293 | 0.0, 0.0, 294 | 1.0, 0.0, 295 | 1.0, 1.0, 296 | 0.0, 1.0, 297 | ]; 298 | 299 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates), 300 | gl.STATIC_DRAW); 301 | 302 | // Build the element array buffer; this specifies the indices 303 | // into the vertex arrays for each face's vertices. 304 | 305 | const indexBuffer = gl.createBuffer(); 306 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 307 | 308 | // This array defines each face as two triangles, using the 309 | // indices into the vertex array to specify each triangle's 310 | // position. 311 | 312 | const indices = [ 313 | 0, 1, 2, 0, 2, 3, // front 314 | 4, 5, 6, 4, 6, 7, // back 315 | 8, 9, 10, 8, 10, 11, // top 316 | 12, 13, 14, 12, 14, 15, // bottom 317 | 16, 17, 18, 16, 18, 19, // right 318 | 20, 21, 22, 20, 22, 23, // left 319 | ]; 320 | 321 | // Now send the element array to GL 322 | 323 | gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 324 | new Uint16Array(indices), gl.STATIC_DRAW); 325 | 326 | return { 327 | position: positionBuffer, 328 | normal: normalBuffer, 329 | textureCoord: textureCoordBuffer, 330 | indices: indexBuffer, 331 | }; 332 | } 333 | 334 | // 335 | // Initialize a texture. 336 | // 337 | function initTexture(gl, url) { 338 | const texture = gl.createTexture(); 339 | gl.bindTexture(gl.TEXTURE_2D, texture); 340 | 341 | // Because video havs to be download over the internet 342 | // they might take a moment until it's ready so 343 | // put a single pixel in the texture so we can 344 | // use it immediately. 345 | const level = 0; 346 | const internalFormat = gl.RGBA; 347 | const width = 1; 348 | const height = 1; 349 | const border = 0; 350 | const srcFormat = gl.RGBA; 351 | const srcType = gl.UNSIGNED_BYTE; 352 | const pixel = new Uint8Array([0, 0, 255, 255]); // opaque blue 353 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, 354 | width, height, border, srcFormat, srcType, 355 | pixel); 356 | 357 | // Turn off mips and set wrapping to clamp to edge so it 358 | // will work regardless of the dimensions of the video. 359 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 360 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 361 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 362 | 363 | return texture; 364 | } 365 | 366 | // 367 | // copy the video texture 368 | // 369 | function updateTexture(gl, texture, video) { 370 | const level = 0; 371 | const internalFormat = gl.RGBA; 372 | const srcFormat = gl.RGBA; 373 | const srcType = gl.UNSIGNED_BYTE; 374 | gl.bindTexture(gl.TEXTURE_2D, texture); 375 | gl.texImage2D(gl.TEXTURE_2D, level, internalFormat, 376 | srcFormat, srcType, video); 377 | } 378 | 379 | function isPowerOf2(value) { 380 | return (value & (value - 1)) == 0; 381 | } 382 | 383 | // 384 | // Draw the scene. 385 | // 386 | function drawScene(gl, programInfo, buffers, texture, deltaTime) { 387 | gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque 388 | gl.clearDepth(1.0); // Clear everything 389 | gl.enable(gl.DEPTH_TEST); // Enable depth testing 390 | gl.depthFunc(gl.LEQUAL); // Near things obscure far things 391 | 392 | // Clear the canvas before we start drawing on it. 393 | 394 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 395 | 396 | // Create a perspective matrix, a special matrix that is 397 | // used to simulate the distortion of perspective in a camera. 398 | // Our field of view is 45 degrees, with a width/height 399 | // ratio that matches the display size of the canvas 400 | // and we only want to see objects between 0.1 units 401 | // and 100 units away from the camera. 402 | 403 | const fieldOfView = 45 * Math.PI / 180; // in radians 404 | const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; 405 | const zNear = 0.1; 406 | const zFar = 100.0; 407 | const projectionMatrix = mat4.create(); 408 | 409 | // note: glmatrix.js always has the first argument 410 | // as the destination to receive the result. 411 | mat4.perspective(projectionMatrix, 412 | fieldOfView, 413 | aspect, 414 | zNear, 415 | zFar); 416 | 417 | // Set the drawing position to the "identity" point, which is 418 | // the center of the scene. 419 | const modelViewMatrix = mat4.create(); 420 | 421 | // Now move the drawing position a bit to where we want to 422 | // start drawing the square. 423 | 424 | mat4.translate(modelViewMatrix, // destination matrix 425 | modelViewMatrix, // matrix to translate 426 | [-0.0, 0.0, -6.0]); // amount to translate 427 | mat4.rotate(modelViewMatrix, // destination matrix 428 | modelViewMatrix, // matrix to rotate 429 | cubeRotation, // amount to rotate in radians 430 | [0, 0, 1]); // axis to rotate around (Z) 431 | mat4.rotate(modelViewMatrix, // destination matrix 432 | modelViewMatrix, // matrix to rotate 433 | cubeRotation * .7,// amount to rotate in radians 434 | [0, 1, 0]); // axis to rotate around (X) 435 | 436 | const normalMatrix = mat4.create(); 437 | mat4.invert(normalMatrix, modelViewMatrix); 438 | mat4.transpose(normalMatrix, normalMatrix); 439 | 440 | // Tell WebGL how to pull out the positions from the position 441 | // buffer into the vertexPosition attribute 442 | { 443 | const numComponents = 3; 444 | const type = gl.FLOAT; 445 | const normalize = false; 446 | const stride = 0; 447 | const offset = 0; 448 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); 449 | gl.vertexAttribPointer( 450 | programInfo.attribLocations.vertexPosition, 451 | numComponents, 452 | type, 453 | normalize, 454 | stride, 455 | offset); 456 | gl.enableVertexAttribArray( 457 | programInfo.attribLocations.vertexPosition); 458 | } 459 | 460 | // Tell WebGL how to pull out the texture coordinates from 461 | // the texture coordinate buffer into the textureCoord attribute. 462 | { 463 | const numComponents = 2; 464 | const type = gl.FLOAT; 465 | const normalize = false; 466 | const stride = 0; 467 | const offset = 0; 468 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord); 469 | gl.vertexAttribPointer( 470 | programInfo.attribLocations.textureCoord, 471 | numComponents, 472 | type, 473 | normalize, 474 | stride, 475 | offset); 476 | gl.enableVertexAttribArray( 477 | programInfo.attribLocations.textureCoord); 478 | } 479 | 480 | // Tell WebGL how to pull out the normals from 481 | // the normal buffer into the vertexNormal attribute. 482 | { 483 | const numComponents = 3; 484 | const type = gl.FLOAT; 485 | const normalize = false; 486 | const stride = 0; 487 | const offset = 0; 488 | gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normal); 489 | gl.vertexAttribPointer( 490 | programInfo.attribLocations.vertexNormal, 491 | numComponents, 492 | type, 493 | normalize, 494 | stride, 495 | offset); 496 | gl.enableVertexAttribArray( 497 | programInfo.attribLocations.vertexNormal); 498 | } 499 | 500 | // Tell WebGL which indices to use to index the vertices 501 | gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices); 502 | 503 | // Tell WebGL to use our program when drawing 504 | 505 | gl.useProgram(programInfo.program); 506 | 507 | // Set the shader uniforms 508 | 509 | gl.uniformMatrix4fv( 510 | programInfo.uniformLocations.projectionMatrix, 511 | false, 512 | projectionMatrix); 513 | gl.uniformMatrix4fv( 514 | programInfo.uniformLocations.modelViewMatrix, 515 | false, 516 | modelViewMatrix); 517 | gl.uniformMatrix4fv( 518 | programInfo.uniformLocations.normalMatrix, 519 | false, 520 | normalMatrix); 521 | 522 | // Specify the texture to map onto the faces. 523 | 524 | // Tell WebGL we want to affect texture unit 0 525 | gl.activeTexture(gl.TEXTURE0); 526 | 527 | // Bind the texture to texture unit 0 528 | gl.bindTexture(gl.TEXTURE_2D, texture); 529 | 530 | // Tell the shader we bound the texture to texture unit 0 531 | gl.uniform1i(programInfo.uniformLocations.uSampler, 0); 532 | 533 | { 534 | const vertexCount = 36; 535 | const type = gl.UNSIGNED_SHORT; 536 | const offset = 0; 537 | gl.drawElements(gl.TRIANGLES, vertexCount, type, offset); 538 | } 539 | 540 | // Update the rotation for the next draw 541 | 542 | cubeRotation += deltaTime; 543 | } 544 | 545 | // 546 | // Initialize a shader program, so WebGL knows how to draw our data 547 | // 548 | function initShaderProgram(gl, vsSource, fsSource) { 549 | const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); 550 | const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); 551 | 552 | // Create the shader program 553 | 554 | const shaderProgram = gl.createProgram(); 555 | gl.attachShader(shaderProgram, vertexShader); 556 | gl.attachShader(shaderProgram, fragmentShader); 557 | gl.linkProgram(shaderProgram); 558 | 559 | // If creating the shader program failed, alert 560 | 561 | if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) { 562 | alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram)); 563 | return null; 564 | } 565 | 566 | return shaderProgram; 567 | } 568 | 569 | // 570 | // creates a shader of the given type, uploads the source and 571 | // compiles it. 572 | // 573 | function loadShader(gl, type, source) { 574 | const shader = gl.createShader(type); 575 | 576 | // Send the source to the shader object 577 | 578 | gl.shaderSource(shader, source); 579 | 580 | // Compile the shader program 581 | 582 | gl.compileShader(shader); 583 | 584 | // See if it compiled successfully 585 | 586 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 587 | alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); 588 | gl.deleteShader(shader); 589 | return null; 590 | } 591 | 592 | return shader; 593 | } 594 | 595 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/sample8/webgl.css: -------------------------------------------------------------------------------- 1 | canvas { 2 | border: 2px solid black; 3 | background-color: black; 4 | } 5 | video { 6 | display: none; 7 | } -------------------------------------------------------------------------------- /webgl-examples/tutorial/tetrahedron/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WebGL Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /webgl-examples/tutorial/tetrahedron/webgl-demo.js: -------------------------------------------------------------------------------- 1 | var tetrahedronRotation = 0.0; 2 | 3 | main(); 4 | 5 | // 6 | // Start here 7 | // 8 | function main() { 9 | const canvas = document.querySelector('#glcanvas'); 10 | const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); 11 | 12 | // If we don't have a GL context, give up now 13 | 14 | if (!gl) { 15 | alert('Unable to initialize WebGL. Your browser or machine may not support it.'); 16 | return; 17 | } 18 | 19 | // Vertex shader program 20 | 21 | const vsSource = ` 22 | attribute vec4 aVertexPosition; 23 | attribute vec4 aVertexColor; 24 | 25 | uniform mat4 uModelViewMatrix; 26 | uniform mat4 uProjectionMatrix; 27 | 28 | varying lowp vec4 vColor; 29 | 30 | void main(void) { 31 | gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; 32 | vColor = aVertexColor; 33 | } 34 | `; 35 | 36 | // Fragment shader program 37 | 38 | const fsSource = ` 39 | varying lowp vec4 vColor; 40 | 41 | void main(void) { 42 | gl_FragColor = vColor; 43 | } 44 | `; 45 | 46 | // Initialize a shader program; this is where all the lighting 47 | // for the vertices and so forth is established. 48 | const shaderProgram = initShaderProgram(gl, vsSource, fsSource); 49 | 50 | // Collect all the info needed to use the shader program. 51 | // Look up which attributes our shader program is using 52 | // for aVertexPosition, aVevrtexColor and also 53 | // look up uniform locations. 54 | const programInfo = { 55 | program: shaderProgram, 56 | attribLocations: { 57 | vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), 58 | vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'), 59 | }, 60 | uniformLocations: { 61 | projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), 62 | modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), 63 | }, 64 | }; 65 | 66 | // Here's where we call the routine that builds all the 67 | // objects we'll be drawing. 68 | const buffers = initBuffers(gl); 69 | 70 | var then = 0; 71 | 72 | // Draw the scene repeatedly 73 | function render(now) { 74 | now *= 0.001; // convert to seconds 75 | const deltaTime = now - then; 76 | then = now; 77 | 78 | drawScene(gl, programInfo, buffers, deltaTime); 79 | 80 | requestAnimationFrame(render); 81 | } 82 | 83 | requestAnimationFrame(render); 84 | } 85 | 86 | // 87 | // initBuffers 88 | // 89 | // Initialize the buffers we'll need. For this demo, we just 90 | // have one object -- a simple three-dimensional tetrahedron. 91 | // 92 | function initBuffers(gl) { 93 | 94 | // Create a buffer for the tetrahedron's vertex positions. 95 | 96 | const positionBuffer = gl.createBuffer(); 97 | 98 | // Select the positionBuffer as the one to apply buffer 99 | // operations to from here out. 100 | 101 | gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); 102 | 103 | // Now create an array of positions for the tetrahedron. 104 | // A equilateral triangle is needed ( well 4 of them ) 105 | // Point `O` is where the height is projected 106 | // The tetrahedron is rotated around point `M` 107 | // Height from vertex `A` to the edge `BC` is `H` 108 | // The edge of the tetrahedron is 2 units long 109 | // |AH| = 1.7320508075688772935274463415059 110 | // The median and a height AH divides itself by 111 | // the other medians into 1x and 2x ( one part and two parts ) 112 | // |AH|/3 = 0.57735026918962576450914878050197 113 | // Find the tetrahedron height by argument sine (