基于webgl的简易时钟实现

38 阅读3分钟

案例中使用到了一个库glMatrix。这个库的下载链接在下面。在项目下放一个glMatrix.js,然后在html标签中引入即可。webgl glMatrix-0.9.6.js 未混淆版_glmatrix-0.9.6.min.js-CSDN博客

项目的运行效果如下

`UGN65%XN{7I(U9@MDZ$5.png

下面是完整代码

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>webgl时钟</title>
     <script src="./glMatrix.js "></script>
  </head>
  <body>
    <canvas id="canvas" width="900" height="900"></canvas>
  </body>
  <script>
    /** @type {HTMLCanvasElement} */
    const canvas = document.getElementById("canvas");
    const gl = canvas.getContext("webgl");
    // var mat4=mat4.create();
    var textureLoc = 0;
    var attribOutUV = 0;
    var uniformTexture0 = 0;
    var uniformTexture1 = 0;
    var texture0 = null;
    var texture1 = null;
    var count = 0;
    var clock = {
      HOUR_HAND: "HOUR_HAND",
      MINUTE_HAND: "MINUTE_HAND",
      SECOND_HAND: "SECOND_HAND",
    };
​
    var vsString = `
    attribute vec4 a_position;
        attribute vec2 outUV;
        varying vec2 inUV;
        uniform mat4  u_formMatrix;
        void main(void){
            gl_Position= u_formMatrix * a_position;
            gl_PointSize=10.0;
            inUV = outUV;
        }
    `;
    var fsString = `
precision mediump float;
uniform float flag;
uniform sampler2D texture0;
varying vec4 v_Color;
varying vec2 inUV;
void main(){
     if(flag==1.){
          gl_FragColor=vec4(1.,0.,0.,1.);
     }else if(flag==2.){
          gl_FragColor=vec4(0.,0.,0.,1);
     }else if(flag==3.){
          gl_FragColor=vec4(.4824,.4863,.2078,1.);
     }
     else{
          vec4 color0=texture2D(texture0,inUV);
          gl_FragColor=color0;
     }
}
    `;
    // 入口函数
    async function main() {
      initWebgl();
      initShader();
      await drawColckBackground();
      clear();
      draw();
      // 此处的绘制必须异步的,等待纹理加载完毕后,才进行绘制。否则会出现部分纹理不显示的情况
      // draw();
      // window.requestAnimationFrame(animation);
      setInterval(() => {
        //清除上一帧的画面  
        clear();
        // 绘制表盘的中心点
        drawCenter();
        // 绘制秒针
        drawHand(clock.SECOND_HAND);
        // 绘制分针
        drawHand(clock.MINUTE_HAND);
        // 绘制时针
        drawHand(clock.HOUR_HAND);
        // 绘制表盘纹理
        drawColckBackground();
        count++;
      }, 1000);
    }
    main();
    // webgl初始化函数
    function initWebgl() {
      // 确定可视域范围
      gl.viewport(0, 0, canvas.clientWidth, canvas.clientHeight);
    }
    // shader初始化函数
    function initShader() {
      const vsShader = gl.createShader(gl.VERTEX_SHADER);
      const fsShader = gl.createShader(gl.FRAGMENT_SHADER);
​
      gl.shaderSource(vsShader, vsString);
      gl.shaderSource(fsShader, fsString);
​
      gl.compileShader(vsShader);
      if (!gl.getShaderParameter(vsShader, gl.COMPILE_STATUS)) {
        throw Error("顶点着色器错误:" + gl.getShaderInfoLog(vsShader));
      }
      gl.compileShader(fsShader);
      if (!gl.getShaderParameter(fsShader, gl.COMPILE_STATUS)) {
        throw Error("片元着色器错误:" + gl.getShaderInfoLog(fsShader));
      }
      const program = gl.createProgram();
      gl.attachShader(program, vsShader);
      gl.attachShader(program, fsShader);
​
      gl.linkProgram(program);
      if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        throw Error("program连接错误:" + gl.getShaderInfoLog(vsShader));
      }
      gl.useProgram(program);
      gl.program = program;
    }
    //   数据缓存区的初始化函数
    async function drawColckBackground() {
      const pointPosition = new Float32Array([
            -1, -1, 0, 1,  0, 0,
            1, 1, 0, 1,   1, 1,
            -1, 1, 0, 1,  0, 1,
            1, -1, 0, 1,  1, 0,
      ]);
      const index=new Uint8Array([
        2,0,1,
        1,0,3
      ])
      // 顶点坐标缓存的创建与传入数据
      const aPosition = gl.getAttribLocation(gl.program, "a_position");
      const buffer = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
      gl.bufferData(gl.ARRAY_BUFFER, pointPosition, gl.STATIC_DRAW);
      gl.enableVertexAttribArray(aPosition);
      gl.vertexAttribPointer(aPosition, 4, gl.FLOAT, false, 6 * 4, 0);
​
      // 纹理坐标缓存的创建与传入数据
      attribOutUV = gl.getAttribLocation(gl.program, "outUV");
      gl.enableVertexAttribArray(attribOutUV);
      gl.vertexAttribPointer(attribOutUV, 2, gl.FLOAT, false, 6 * 4, 4 * 4);
​
​
      // 索引缓存
      const indexBuffer=gl.createBuffer()
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer)
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,index,gl.STATIC_DRAW)
​
      uniformTexture0 = gl.getUniformLocation(gl.program, "texture0");
​
​
      const flag=gl.getUniformLocation(gl.program,'flag')
      gl.uniform1f(flag,0.5)
​
​
      let modelView1 = mat4.create();
      // 设置为单位矩阵
      mat4.identity(modelView1);
      let uniformMatrix = gl.getUniformLocation(gl.program, "u_formMatrix");
      gl.uniformMatrix4fv(uniformMatrix, false, modelView1);
​
      
      if(!texture0){
          texture0 = await initTexture("http://localhost:3000/clock-bg.png");//时钟表盘背景图
      }
​
      draw()
    }
    // webgl绘制函数
    function draw() {
      // 初始化画布
      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, texture0);
      gl.uniform1i(uniformTexture0, 0);
      gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE,0);
    }
    async function initTexture(src) {
      return new Promise((reslove, reject) => {
        const image = new Image();
        //解决读取远程图片链接的跨域问题
        image.crossOrigin = "Anonymous";
        image.src = src;
        image.onload = function () {
          console.log('图片加载完毕')
          reslove(handleLoadTexture(image));
        };
        image.onerror = reject;
      });
    }
    function handleLoadTexture(img) {
      const texture = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, texture);
      //将图片坐标系转化为纹理坐标系。图标坐标系与纹理坐标系x轴方向相同,y轴相反。这个方法就是把y坐标取负值
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,666);
​
      
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
      return texture;
    }
    function drawHand(type) {
          let arr = [];
          let angle = 0;
          let handType=2.0
          if (type == clock.HOUR_HAND) {
            arr = [
              -0.1, -0.025, 0, 1, 
                -0.1, 0.025, 0, 1, 
                0.3, 0, 0, 1,
            ];
            angle = ((-Math.PI / 1800) * count * 5) / 60;
          } else if (type == clock.MINUTE_HAND) {
            arr = [
              -0.1, -0.025, 0, 1, 
              -0.1, 0.025, 0, 1, 
              0.5, 0, 0, 1,
            ];
            angle = (-Math.PI / 1800) * count;
          } else {
            arr = [
              -0.1, -0.025, 0, 1,
               -0.1, 0.025, 0, 1,
                0.7, 0, 0, 1,
            ];
            angle = (-Math.PI / 30) * count;
            handType=1.0
          }
          let modelView1 = mat4.create();
          mat4.identity(modelView1);
          mat4.scale(modelView1, [1, 1, 1])
          mat4.rotate(modelView1, angle, [0, 0, 1]);
​
​
          let pointPosition = new Float32Array(arr);
          let aPsotion = gl.getAttribLocation(gl.program, "a_position");
          let triangleBuffer = gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
          gl.bufferData(gl.ARRAY_BUFFER, pointPosition, gl.STATIC_DRAW);
          gl.enableVertexAttribArray(aPsotion);
          gl.vertexAttribPointer(aPsotion, 4, gl.FLOAT, false, 4 * 4, 0);
    
          let uniformMatrix = gl.getUniformLocation(gl.program, "u_formMatrix");
          gl.uniformMatrix4fv(uniformMatrix, false, modelView1);
​
          const flag=gl.getUniformLocation(gl.program,'flag')
          gl.uniform1f(flag,handType)
    
          gl.drawArrays(gl.TRIANGLES, 0, 3);
        }
        //清屏
        function clear(){
                gl.clearColor(0.0, 0.0, 0.0, 1.0);
                gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
                gl.enable(gl.DEPTH_TEST);
        }
​
        function drawCenter(){
          let pointPosition = new Float32Array([0,0,0,1]);
          let aPsotion = gl.getAttribLocation(gl.program, "a_position");
          let triangleBuffer = gl.createBuffer();
          gl.bindBuffer(gl.ARRAY_BUFFER, triangleBuffer);
          gl.bufferData(gl.ARRAY_BUFFER, pointPosition, gl.STATIC_DRAW);
          gl.enableVertexAttribArray(aPsotion);
          gl.vertexAttribPointer(aPsotion, 4, gl.FLOAT, false, 4 * 4, 0);
    
​
          let modelView1 = mat4.create();
          mat4.identity(modelView1);
          let uniformMatrix = gl.getUniformLocation(gl.program, "u_formMatrix");
          gl.uniformMatrix4fv(uniformMatrix, false, modelView1);
​
          const flag=gl.getUniformLocation(gl.program,'flag')
          gl.uniform1f(flag,3.0)
          gl.drawArrays(gl.POINTS, 0, 1);
        }
  </script>
</html>
​