Facebook
From Cute Crane, 4 Years ago, written in JavaScript.
Embed
Download Paste or View Raw
Hits: 129
  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.  
  20.   const shaderProgramFactory = new ShaderProgramFactory(gl);
  21.   const shaderProgram = shaderProgramFactory.createShaderProgram();
  22.  
  23.   // Collect all the info needed to use the shader program.
  24.   // Look up which attributes our shader program is using
  25.   // for aVertexPosition, aVertexNormal, aTextureCoord,
  26.   // and look up uniform locations.
  27.   const programInfo = {
  28.     program: shaderProgram,
  29.     attribLocations: {
  30.       vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
  31.       vertexNormal: gl.getAttribLocation(shaderProgram, 'aVertexNormal'),
  32.       textureCoord: gl.getAttribLocation(shaderProgram, 'aTextureCoord'),
  33.     },
  34.     uniformLocations: {
  35.       projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
  36.       modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
  37.       normalMatrix: gl.getUniformLocation(shaderProgram, 'uNormalMatrix'),
  38.       uSampler: gl.getUniformLocation(shaderProgram, 'uSampler'),
  39.     },
  40.   };
  41.  
  42.   // Here's where we call the routine that builds all the
  43.   // objects we'll be drawing.
  44.   const buffers = initBuffers(gl);
  45.  
  46.   const texture = loadTexture(gl, 'cubetexture.png');
  47.  
  48.   var then = 0;
  49.  
  50.   // Draw the scene repeatedly
  51.   function render(now) {
  52.     now *= 0.001;  // convert to seconds
  53.     const deltaTime = now - then;
  54.     then = now;
  55.  
  56.     drawScene(gl, programInfo, buffers, texture, deltaTime);
  57.  
  58.     requestAnimationFrame(render);
  59.   }
  60.   requestAnimationFrame(render);
  61. }
  62.  
  63. //
  64. // initBuffers
  65. //
  66. // Initialize the buffers we'll need. For this demo, we just
  67. // have one object -- a simple three-dimensional cube.
  68. //
  69. function initBuffers(gl) {
  70.  
  71.   // Create a buffer for the cube's vertex positions.
  72.  
  73.   const positionBuffer = gl.createBuffer();
  74.  
  75.   // Select the positionBuffer as the one to apply buffer
  76.   // operations to from here out.
  77.  
  78.   gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  79.  
  80.   // Now create an array of positions for the cube.
  81.  
  82.   const positions = [
  83.     // Front face
  84.     -1.0, -1.0,  1.0,
  85.      1.0, -1.0,  1.0,
  86.      1.0,  1.0,  1.0,
  87.     -1.0,  1.0,  1.0,
  88.  
  89.     // Back face
  90.     -1.0, -1.0, -1.0,
  91.     -1.0,  1.0, -1.0,
  92.      1.0,  1.0, -1.0,
  93.      1.0, -1.0, -1.0,
  94.  
  95.     // Top face
  96.     -1.0,  1.0, -1.0,
  97.     -1.0,  1.0,  1.0,
  98.      1.0,  1.0,  1.0,
  99.      1.0,  1.0, -1.0,
  100.  
  101.     // Bottom face
  102.     -1.0, -1.0, -1.0,
  103.      1.0, -1.0, -1.0,
  104.      1.0, -1.0,  1.0,
  105.     -1.0, -1.0,  1.0,
  106.  
  107.     // Right face
  108.      1.0, -1.0, -1.0,
  109.      1.0,  1.0, -1.0,
  110.      1.0,  1.0,  1.0,
  111.      1.0, -1.0,  1.0,
  112.  
  113.     // Left face
  114.     -1.0, -1.0, -1.0,
  115.     -1.0, -1.0,  1.0,
  116.     -1.0,  1.0,  1.0,
  117.     -1.0,  1.0, -1.0,
  118.   ];
  119.  
  120.   // Now pass the list of positions into WebGL to build the
  121.   // shape. We do this by creating a Float32Array from the
  122.   // JavaScript array, then use it to fill the current buffer.
  123.  
  124.   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
  125.  
  126.   // Set up the normals for the vertices, so that we can compute lighting.
  127.  
  128.   const normalBuffer = gl.createBuffer();
  129.   gl.bindBuffer(gl.ARRAY_BUFFER, normalBuffer);
  130.  
  131.   const vertexNormals = [
  132.     // Front
  133.      0.0,  0.0,  1.0,
  134.      0.0,  0.0,  1.0,
  135.      0.0,  0.0,  1.0,
  136.      0.0,  0.0,  1.0,
  137.  
  138.     // Back
  139.      0.0,  0.0, -1.0,
  140.      0.0,  0.0, -1.0,
  141.      0.0,  0.0, -1.0,
  142.      0.0,  0.0, -1.0,
  143.  
  144.     // Top
  145.      0.0,  1.0,  0.0,
  146.      0.0,  1.0,  0.0,
  147.      0.0,  1.0,  0.0,
  148.      0.0,  1.0,  0.0,
  149.  
  150.     // Bottom
  151.      0.0, -1.0,  0.0,
  152.      0.0, -1.0,  0.0,
  153.      0.0, -1.0,  0.0,
  154.      0.0, -1.0,  0.0,
  155.  
  156.     // Right
  157.      1.0,  0.0,  0.0,
  158.      1.0,  0.0,  0.0,
  159.      1.0,  0.0,  0.0,
  160.      1.0,  0.0,  0.0,
  161.  
  162.     // Left
  163.     -1.0,  0.0,  0.0,
  164.     -1.0,  0.0,  0.0,
  165.     -1.0,  0.0,  0.0,
  166.     -1.0,  0.0,  0.0
  167.   ];
  168.  
  169.   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals),
  170.                 gl.STATIC_DRAW);
  171.  
  172.   // Now set up the texture coordinates for the faces.
  173.  
  174.   const textureCoordBuffer = gl.createBuffer();
  175.   gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
  176.  
  177.   const textureCoordinates = [
  178.     // Front
  179.     0.0,  0.0,
  180.     1.0,  0.0,
  181.     1.0,  1.0,
  182.     0.0,  1.0,
  183.     // Back
  184.     0.0,  0.0,
  185.     1.0,  0.0,
  186.     1.0,  1.0,
  187.     0.0,  1.0,
  188.     // Top
  189.     0.0,  0.0,
  190.     1.0,  0.0,
  191.     1.0,  1.0,
  192.     0.0,  1.0,
  193.     // Bottom
  194.     0.0,  0.0,
  195.     1.0,  0.0,
  196.     1.0,  1.0,
  197.     0.0,  1.0,
  198.     // Right
  199.     0.0,  0.0,
  200.     1.0,  0.0,
  201.     1.0,  1.0,
  202.     0.0,  1.0,
  203.     // Left
  204.     0.0,  0.0,
  205.     1.0,  0.0,
  206.     1.0,  1.0,
  207.     0.0,  1.0,
  208.   ];
  209.  
  210.   gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
  211.                 gl.STATIC_DRAW);
  212.  
  213.   // Build the element array buffer; this specifies the indices
  214.   // into the vertex arrays for each face's vertices.
  215.  
  216.   const indexBuffer = gl.createBuffer();
  217.   gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
  218.  
  219.   // This array defines each face as two triangles, using the
  220.   // indices into the vertex array to specify each triangle's
  221.   // position.
  222.  
  223.   const indices = [
  224.     0,  1,  2,      0,  2,  3,    // front
  225.     4,  5,  6,      4,  6,  7,    // back
  226.     8,  9,  10,     8,  10, 11,   // top
  227.     12, 13, 14,     12, 14, 15,   // bottom
  228.     16, 17, 18,     16, 18, 19,   // right
  229.     20, 21, 22,     20, 22, 23,   // left
  230.   ];
  231.  
  232.   // Now send the element array to GL
  233.  
  234.   gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
  235.       new Uint16Array(indices), gl.STATIC_DRAW);
  236.  
  237.   return {
  238.     position: positionBuffer,
  239.     normal: normalBuffer,
  240.     textureCoord: textureCoordBuffer,
  241.     indices: indexBuffer,
  242.   };
  243. }
  244.  
  245. //
  246. // Initialize a texture and load an image.
  247. // When the image finished loading copy it into the texture.
  248. //
  249. function loadTexture(gl, url) {
  250.   const texture = gl.createTexture();
  251.   gl.bindTexture(gl.TEXTURE_2D, texture);
  252.  
  253.   // Because images have to be download over the internet
  254.   // they might take a moment until they are ready.
  255.   // Until then put a single pixel in the texture so we can
  256.   // use it immediately. When the image has finished downloading
  257.   // we'll update the texture with the contents of the image.
  258.   const level = 0;
  259.   const internalFormat = gl.RGBA;
  260.   const width = 1;
  261.   const height = 1;
  262.   const border = 0;
  263.   const srcFormat = gl.RGBA;
  264.   const srcType = gl.UNSIGNED_BYTE;
  265.   const pixel = new Uint8Array([0, 0, 255, 255]);  // opaque blue
  266.   gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
  267.                 width, height, border, srcFormat, srcType,
  268.                 pixel);
  269.  
  270.   const image = new Image();
  271.   image.onload = function() {
  272.     gl.bindTexture(gl.TEXTURE_2D, texture);
  273.     gl.texImage2D(gl.TEXTURE_2D, level, internalFormat,
  274.                   srcFormat, srcType, image);
  275.  
  276.     // WebGL1 has different requirements for power of 2 images
  277.     // vs non power of 2 images so check if the image is a
  278.     // power of 2 in both dimensions.
  279.     if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
  280.        // Yes, it's a power of 2. Generate mips.
  281.        gl.generateMipmap(gl.TEXTURE_2D);
  282.     } else {
  283.        // No, it's not a power of 2. Turn of mips and set
  284.        // wrapping to clamp to edge
  285.        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
  286.        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  287.        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
  288.     }
  289.   };
  290.   image.src = url;
  291.  
  292.   return texture;
  293. }
  294.  
  295. function isPowerOf2(value) {
  296.   return (value & (value - 1)) == 0;
  297. }
  298.  
  299. //
  300. // Draw the scene.
  301. //
  302. function drawScene(gl, programInfo, buffers, texture, deltaTime) {
  303.   gl.clearColor(0.0, 0.0, 0.0, 1.0);  // Clear to black, fully opaque
  304.   gl.clearDepth(1.0);                 // Clear everything
  305.   gl.enable(gl.DEPTH_TEST);           // Enable depth testing
  306.   gl.depthFunc(gl.LEQUAL);            // Near things obscure far things
  307.  
  308.   // Clear the canvas before we start drawing on it.
  309.  
  310.   gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  311.  
  312.   // Create a perspective matrix, a special matrix that is
  313.   // used to simulate the distortion of perspective in a camera.
  314.   // Our field of view is 45 degrees, with a width/height
  315.   // ratio that matches the display size of the canvas
  316.   // and we only want to see objects between 0.1 units
  317.   // and 100 units away from the camera.
  318.  
  319.   const fieldOfView = 45 * Math.PI / 180;   // in radians
  320.   const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
  321.   const zNear = 0.1;
  322.   const zFar = 100.0;
  323.   const projectionMatrix = mat4.create();
  324.  
  325.   // note: glmatrix.js always has the first argument
  326.   // as the destination to receive the result.
  327.   mat4.perspective(projectionMatrix,
  328.                    fieldOfView,
  329.                    aspect,
  330.                    zNear,
  331.                    zFar);
  332.  
  333.   // Set the drawing position to the "identity" point, which is
  334.   // the center of the scene.
  335.   const modelViewMatrix = mat4.create();
  336.  
  337.   // Now move the drawing position a bit to where we want to
  338.   // start drawing the square.
  339.  
  340.   mat4.translate(modelViewMatrix,     // destination matrix
  341.                  modelViewMatrix,     // matrix to translate
  342.                  [-0.0, 0.0, -6.0]);  // amount to translate
  343.   mat4.rotate(modelViewMatrix,  // destination matrix
  344.               modelViewMatrix,  // matrix to rotate
  345.               cubeRotation,     // amount to rotate in radians
  346.               [0, 0, 1]);       // axis to rotate around (Z)
  347.   mat4.rotate(modelViewMatrix,  // destination matrix
  348.               modelViewMatrix,  // matrix to rotate
  349.               cubeRotation * .7,// amount to rotate in radians
  350.               [0, 1, 0]);       // axis to rotate around (X)
  351.  
  352.   const normalMatrix = mat4.create();
  353.   mat4.invert(normalMatrix, modelViewMatrix);
  354.   mat4.transpose(normalMatrix, normalMatrix);
  355.  
  356.   // Tell WebGL how to pull out the positions from the position
  357.   // buffer into the vertexPosition attribute
  358.   {
  359.     const numComponents = 3;
  360.     const type = gl.FLOAT;
  361.     const normalize = false;
  362.     const stride = 0;
  363.     const offset = 0;
  364.     gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
  365.     gl.vertexAttribPointer(
  366.         programInfo.attribLocations.vertexPosition,
  367.         numComponents,
  368.         type,
  369.         normalize,
  370.         stride,
  371.         offset);
  372.     gl.enableVertexAttribArray(
  373.         programInfo.attribLocations.vertexPosition);
  374.   }
  375.  
  376.   // Tell WebGL how to pull out the texture coordinates from
  377.   // the texture coordinate buffer into the textureCoord attribute.
  378.   {
  379.     const numComponents = 2;
  380.     const type = gl.FLOAT;
  381.     const normalize = false;
  382.     const stride = 0;
  383.     const offset = 0;
  384.     gl.bindBuffer(gl.ARRAY_BUFFER, buffers.textureCoord);
  385.     gl.vertexAttribPointer(
  386.         programInfo.attribLocations.textureCoord,
  387.         numComponents,
  388.         type,
  389.         normalize,
  390.         stride,
  391.         offset);
  392.     gl.enableVertexAttribArray(
  393.         programInfo.attribLocations.textureCoord);
  394.   }
  395.  
  396.   // Tell WebGL how to pull out the normals from
  397.   // the normal buffer into the vertexNormal attribute.
  398.   {
  399.     const numComponents = 3;
  400.     const type = gl.FLOAT;
  401.     const normalize = false;
  402.     const stride = 0;
  403.     const offset = 0;
  404.     gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normal);
  405.     gl.vertexAttribPointer(
  406.         programInfo.attribLocations.vertexNormal,
  407.         numComponents,
  408.         type,
  409.         normalize,
  410.         stride,
  411.         offset);
  412.     gl.enableVertexAttribArray(
  413.         programInfo.attribLocations.vertexNormal);
  414.   }
  415.  
  416.   // Tell WebGL which indices to use to index the vertices
  417.   gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
  418.  
  419.   // Tell WebGL to use our program when drawing
  420.  
  421.   gl.useProgram(programInfo.program);
  422.  
  423.   // Set the shader uniforms
  424.  
  425.   gl.uniformMatrix4fv(
  426.       programInfo.uniformLocations.projectionMatrix,
  427.       false,
  428.       projectionMatrix);
  429.   gl.uniformMatrix4fv(
  430.       programInfo.uniformLocations.modelViewMatrix,
  431.       false,
  432.       modelViewMatrix);
  433.   gl.uniformMatrix4fv(
  434.       programInfo.uniformLocations.normalMatrix,
  435.       false,
  436.       normalMatrix);
  437.  
  438.   // Specify the texture to map onto the faces.
  439.  
  440.   // Tell WebGL we want to affect texture unit 0
  441.   gl.activeTexture(gl.TEXTURE0);
  442.  
  443.   // Bind the texture to texture unit 0
  444.   gl.bindTexture(gl.TEXTURE_2D, texture);
  445.  
  446.   // Tell the shader we bound the texture to texture unit 0
  447.   gl.uniform1i(programInfo.uniformLocations.uSampler, 0);
  448.  
  449.   {
  450.     const vertexCount = 36;
  451.     const type = gl.UNSIGNED_SHORT;
  452.     const offset = 0;
  453.     gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
  454.   }
  455.  
  456.   // Update the rotation for the next draw
  457.  
  458.   cubeRotation += deltaTime;
  459. }
  460.