7 <canvas id=
"glCanvas" width=
"640" height=
"480"></canvas>
11 <script src=
"gl-matrix.js"></script>
13 var cubeRotation =
0.0;
21 const canvas = document.querySelector('#glcanvas');
22 const gl = canvas.getContext('webgl');
24 // If we don't have a GL context, give up now
27 alert('Unable to initialize WebGL. Your browser or machine may not support it.');
31 // Vertex shader program
34 attribute vec4 aVertexPosition;
35 attribute vec2 aTextureCoord;
36 uniform mat4 uModelViewMatrix;
37 uniform mat4 uProjectionMatrix;
38 varying highp vec2 vTextureCoord;
40 gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
41 vTextureCoord = aTextureCoord;
45 // Fragment shader program
48 varying highp vec2 vTextureCoord;
49 uniform sampler2D uSampler;
51 gl_FragColor = texture2D(uSampler, vTextureCoord);
55 // Initialize a shader program; this is where all the lighting
56 // for the vertices and so forth is established.
57 const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
59 // Collect all the info needed to use the shader program.
60 // Look up which attributes our shader program is using
61 // for aVertexPosition, aTextureCoord and also
62 // look up uniform locations.
64 program: shaderProgram,
66 vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
67 textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'),
70 projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
71 modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
72 uSampler: gl.getUniformLocation(shaderProgram, 'uSampler'),
76 // Here's where we call the routine that builds all the
77 // objects we'll be drawing.
78 const buffers = initBuffers(gl);
80 const texture = loadTexture(gl, 'cubetexture.png');
84 // Draw the scene repeatedly
85 function render(now) {
86 now *=
0.001; // convert to seconds
87 const deltaTime = now - then;
90 drawScene(gl, programInfo, buffers, texture, deltaTime);
92 requestAnimationFrame(render);
94 requestAnimationFrame(render);
100 // Initialize the buffers we'll need. For this demo, we just
101 // have one object -- a simple three-dimensional cube.
103 function initBuffers(gl) {
105 // Create a buffer for the cube's vertex positions.
107 const positionBuffer = gl.createBuffer();
109 // Select the positionBuffer as the one to apply buffer
110 // operations to from here out.
112 gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
114 // Now create an array of positions for the cube.
154 // Now pass the list of positions into WebGL to build the
155 // shape. We do this by creating a Float32Array from the
156 // JavaScript array, then use it to fill the current buffer.
158 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
160 // Now set up the texture coordinates for the faces.
162 const textureCoordBuffer = gl.createBuffer();
163 gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
165 const textureCoordinates = [
198 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
201 // Build the element array buffer; this specifies the indices
202 // into the vertex arrays for each face's vertices.
204 const indexBuffer = gl.createBuffer();
205 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
207 // This array defines each face as two triangles, using the
208 // indices into the vertex array to specify each triangle's
212 0,
1,
2,
0,
2,
3, // front
213 4,
5,
6,
4,
6,
7, // back
214 8,
9,
10,
8,
10,
11, // top
215 12,
13,
14,
12,
14,
15, // bottom
216 16,
17,
18,
16,
18,
19, // right
217 20,
21,
22,
20,
22,
23, // left
220 // Now send the element array to GL
222 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
223 new Uint16Array(indices), gl.STATIC_DRAW);
226 position: positionBuffer,
227 textureCoord: textureCoordBuffer,
228 indices: indexBuffer,
233 // Initialize a texture and load an image.
234 // When the image finished loading copy it into the texture.
236 function loadTexture(gl, url) {
237 const texture = gl.createTexture();
238 gl.bindTexture(gl.TEXTURE_2D, texture);
240 // Because images have to be download over the internet
241 // they might take a moment until they are ready.
242 // Until then put a single pixel in the texture so we can
243 // use it immediately. When the image has finished downloading
244 // we'll update the texture with the contents of the image.
246 const internalFormat = gl.RGBA;
250 const srcFormat = gl.RGBA;
251 const srcType = gl.UNSIGNED_BYTE;
252 const pixel = new Uint8Array([
0,
0,
255,
255]); // opaque blue
253 gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
254 width, height, border, srcFormat, srcType,
257 const image = new Image();
258 image.onload = function() {
259 gl.bindTexture(gl.TEXTURE_2D, texture);
260 gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
261 srcFormat, srcType, image);
263 // WebGL1 has different requirements for power of
2 images
264 // vs non power of
2 images so check if the image is a
265 // power of
2 in both dimensions.
266 if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
267 // Yes, it's a power of
2. Generate mips.
268 gl.generateMipmap(gl.TEXTURE_2D);
270 // No, it's not a power of
2. Turn of mips and set
271 // wrapping to clamp to edge
272 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
273 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
274 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
282 function isPowerOf2(value) {
283 return (value & (value -
1)) ==
0;
289 function drawScene(gl, programInfo, buffers, texture, deltaTime) {
290 gl.clearColor(
0.0,
0.0,
0.0,
1.0); // Clear to black, fully opaque
291 gl.clearDepth(
1.0); // Clear everything
292 gl.enable(gl.DEPTH_TEST); // Enable depth testing
293 gl.depthFunc(gl.LEQUAL); // Near things obscure far things
295 // Clear the canvas before we start drawing on it.
297 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
299 // Create a perspective matrix, a special matrix that is
300 // used to simulate the distortion of perspective in a camera.
301 // Our field of view is
45 degrees, with a width/height
302 // ratio that matches the display size of the canvas
303 // and we only want to see objects between
0.1 units
304 // and
100 units away from the camera.
306 const fieldOfView =
45 * Math.PI /
180; // in radians
307 const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
310 const projectionMatrix = mat4.create();
312 // note: glmatrix.js always has the first argument
313 // as the destination to receive the result.
314 mat4.perspective(projectionMatrix,
320 // Set the drawing position to the
"identity" point, which is
321 // the center of the scene.
322 const modelViewMatrix = mat4.create();
324 // Now move the drawing position a bit to where we want to
325 // start drawing the square.
327 mat4.translate(modelViewMatrix, // destination matrix
328 modelViewMatrix, // matrix to translate
329 [-
0.0,
0.0, -
6.0]); // amount to translate
330 mat4.rotate(modelViewMatrix, // destination matrix
331 modelViewMatrix, // matrix to rotate
332 cubeRotation, // amount to rotate in radians
333 [
0,
0,
1]); // axis to rotate around (Z)
334 mat4.rotate(modelViewMatrix, // destination matrix
335 modelViewMatrix, // matrix to rotate
336 cubeRotation *
.7,// amount to rotate in radians
337 [
0,
1,
0]); // axis to rotate around (X)
339 // Tell WebGL how to pull out the positions from the position
340 // buffer into the vertexPosition attribute
342 const numComponents =
3;
343 const type = gl.FLOAT;
344 const normalize = false;
347 gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
348 gl.vertexAttribPointer(
349 programInfo.attribLocations.vertexPosition,
355 gl.enableVertexAttribArray(
356 programInfo.attribLocations.vertexPosition);
359 // Tell WebGL how to pull out the texture coordinates from
360 // the texture coordinate buffer into the textureCoord attribute.
362 const numComponents =
2;
363 const type = gl.FLOAT;
364 const normalize = false;
367 gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord);
368 gl.vertexAttribPointer(
369 programInfo.attribLocations.textureCoord,
375 gl.enableVertexAttribArray(
376 programInfo.attribLocations.textureCoord);
379 // Tell WebGL which indices to use to index the vertices
380 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
382 // Tell WebGL to use our program when drawing
384 gl.useProgram(programInfo.program);
386 // Set the shader uniforms
389 programInfo.uniformLocations.projectionMatrix,
393 programInfo.uniformLocations.modelViewMatrix,
397 // Specify the texture to map onto the faces.
399 // Tell WebGL we want to affect texture unit
0
400 gl.activeTexture(gl.TEXTURE0);
402 // Bind the texture to texture unit
0
403 gl.bindTexture(gl.TEXTURE_2D, texture);
405 // Tell the shader we bound the texture to texture unit
0
406 gl.uniform1i(programInfo.uniformLocations.uSampler,
0);
409 const vertexCount =
36;
410 const type = gl.UNSIGNED_SHORT;
412 gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
415 // Update the rotation for the next draw
417 cubeRotation += deltaTime;
421 // Initialize a shader program, so WebGL knows how to draw our data
423 function initShaderProgram(gl, vsSource, fsSource) {
424 const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
425 const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
427 // Create the shader program
429 const shaderProgram = gl.createProgram();
430 gl.attachShader(shaderProgram, vertexShader);
431 gl.attachShader(shaderProgram, fragmentShader);
432 gl.linkProgram(shaderProgram);
434 // If creating the shader program failed, alert
436 if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
437 alert('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
441 return shaderProgram;
445 // creates a shader of the given type, uploads the source and
448 function loadShader(gl, type, source) {
449 const shader = gl.createShader(type);
451 // Send the source to the shader object
453 gl.shaderSource(shader, source);
455 // Compile the shader program
457 gl.compileShader(shader);
459 // See if it compiled successfully
461 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
462 alert('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
463 gl.deleteShader(shader);