使用webgl动态绘制多个点

前言

动态绘制多个点,相对于绘制单个点而言,只是多了个监听事件而已。下面,我们来看看,对于绘制多个点,我们是如何使用 webgl 来处理的。

方法展示

初始化 webgl,和初始化 shader 的方法和绘制单个点的是一样的。有需要的小伙伴可以去前面的绘制单个点的文章中去寻找。这里,我们主要来说说,动态绘制点与绘制单个点的不同处理之处。

一 坐标位置不再是固定的,而是需要动态去获取

绘制单个点中,我们的坐标位置是固定的,即我们固定在某个位置绘制一个点。而现在我们需要动态绘制点,自然我们的位置也需要动态去获取了。接下来我们便看看,如何去动态获取位置。 先直接给出代码,然后再解释,为什么需要这样做:

scss 复制代码
 function initBuffer() {
        // getAttribLocation() 方法返回给定WebGLProgram对象中某属性的下标指向位置
        // 第一个参数为WebGLProgram,第二个参数为需要获取下标指向位置的GLSL变量名
        let aPosition = webgl.getAttribLocation(webgl.program, "a_position");

        //监听鼠标点击事件
        document.addEventListener("mousedown", function (e) {
          // debugger;
          //屏幕坐标
          let x = e.clientX;
          let y = e.clientY;
          //返回元素大小相对于视口的位置信息
          let rect = e.target.getBoundingClientRect();

          // 将屏幕坐标转换为webgl坐标
          // 思路:屏幕坐标->canvas坐标系坐标->webgl坐标系坐标
          // webgl坐标系:x:[-1,1],y:[-1,1],z:[-1,1]
          let pointX = (x - rect.left - 250) / 250;
          let pointy = (250 - (y - rect.top)) / 250;
          console.log(pointX, pointy);

          // 将该点放入数组中
          points.push(pointX, pointy, 0.0, 1.0);

          //创建32位浮点型数组
          let pointPosition = new Float32Array(points);

          //创建缓冲区
          let pointBuffer = webgl.createBuffer();
          //绑定缓冲区
          webgl.bindBuffer(webgl.ARRAY_BUFFER, pointBuffer);
          //绑定缓冲区对象
          webgl.bufferData(
            webgl.ARRAY_BUFFER,
            pointPosition,
            webgl.STATIC_DRAW
          );
          // 启用位置信息数组
          webgl.enableVertexAttribArray(aPosition);

          // 规定位置信息数组如何渲染
          webgl.vertexAttribPointer(aPosition, 4, webgl.FLOAT, false, 0, 0);

          // 清除颜色
          webgl.clearColor(0.0, 0.0, 0.0, 1.0);
          // 清除深度缓冲区和颜色缓冲区
          webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
          // 绘制,规定绘制数组,以及绘制方式
          webgl.drawArrays(webgl.POINTS, 0, points.length / 4);
        });

        // getUniformLocation()方法用于获取指定WebGLProgram对象中uniform变量的位置。
        // 第一个参数为要获取uniform变量的WebGLProgram对象,第二个参数为要获取位置的uniform变量的名称。
        let uniforproj = webgl.getUniformLocation(webgl.program, "proj");

        // uniformMatrix4fv()用于设置一个4*4的矩阵类型的uniform变量值。接收4个参数
        // 第一个参数为 要设置值的uniform变量的地址
        // 第二个参数为 是否将矩阵转置,默认false
        // 第三个参数为  要设置的值,应该是一个4*4的矩阵
        // 第四个参数为 矩阵在数组中的偏移量,默认为0
        webgl.uniformMatrix4fv(uniforproj, false, projMat4,0);
      }

这段代码中最值得关注的是几句坐标变换的代码:

ini 复制代码
    //屏幕坐标
    let x = e.clientX;
    let y = e.clientY;
    //返回元素大小相对于视口的位置信息
    let rect = e.target.getBoundingClientRect();

    // 将屏幕坐标转换为webgl坐标
    // 思路:屏幕坐标->canvas坐标系坐标->webgl坐标系坐标
    // webgl坐标系:x:[-1,1],y:[-1,1],z:[-1,1]
    let pointX = (x - rect.left - 250) / 250;
    let pointy = (250 - (y - rect.top)) / 250;

为什么要这样变换呢?

该图中,我标出了几种坐标系的范围,我们可以看到:canvas 坐标=屏幕坐标-rect,webgl 坐标=[canvas 坐标-(width 或者 height)/2]/(width 或者 height 的一半)

二 点的绘制

绘制单个点时,是在 draw 函数中绘制的。动态绘制点时,我们实在每次点击之后,重新绘制的。每次都重新绘制了所有点。

ini 复制代码
  webgl.drawArrays(webgl.POINTS, 0, points.length / 4);

完整的代码

xml 复制代码
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>绘制点</title>
    <script src="./glMatrix.js"></script>
    <script>
      var projMat4 = mat4.create(); // 初始化一个4*4的矩阵
      var webgl; // 声明全局变量
      /**
       * GLSL代码
       * 声明一个顶点着色器
       * vec4:四维向量 ,具有xyzw四个分量,分量是浮点数
       * mat4: 4*4矩阵
       * **/
      var vertexString = `
        attribute vec4 a_position;
        uniform mat4 proj;
        void main(){
            gl_Position =a_position;
            gl_PointSize = 20.0;
        }
        `;
      var fragmentString = `
        void main(){
            gl_FragColor = vec4(0,0,1.0,1.0);
        }
        `; // 片元着色器

      // 入口函数
      function init() {
        initWebgl();
        initShader();
        initBuffer();
        draw();
      }
      // webgl初始化函数
      function initWebgl() {
        // 获取canvas容器
        let webglDiv = document.querySelector("#webglCanvas");
        // 设置webgl上下文
        webgl = webglDiv.getContext("webgl");
        // 设置可视范围 https://developer.mozilla.org/zh-CN/docs/Web/API/WebGLRenderingContext/viewport
        webgl.viewport(0, 0, webglDiv.width, webglDiv.height);
        // 定义阴影范围
        mat4.ortho(
          0,
          webglDiv.clientWidth,
          webglDiv.clientHeight,
          0,
          -1,
          1,
          projMat4
        );
      }
      // shder初始化函数
      function initShader() {
        // createShader() 用于创建一个 WebGLShader 着色器对象,该对象可以使用 shaderSource()和 compileShader() 方法配置着色器代码。
        // 参数为gl.VERTEX_SHADER 或 gl.FRAGMENT_SHADER两者中的一个。
        let vsshader = webgl.createShader(webgl.VERTEX_SHADER);
        let fssagder = webgl.createShader(webgl.FRAGMENT_SHADER);

        // 用于将我们创建的 WebGLShader着色器对象和GLSL程序中定义的着色器相关联。
        // 第一个参数为webglShader对象,第二个参数为GLSL中定义的着色器
        webgl.shaderSource(vsshader, vertexString);
        webgl.shaderSource(fssagder, fragmentString);

        // 编译WebGLShader着色器,使其成为为二进制数据,然后就可以被WebGLProgram对象所使用。
        // 参数为一个片元着色器或顶点着色器
        webgl.compileShader(vsshader);
        webgl.compileShader(fssagder);

        // 创建一个webglProgram对象,该对象由两个编译过后的 WebGLShader 组成 - 顶点着色器和片段着色器(均由 GLSL 语言所写)
        let program = webgl.createProgram();

        // attachShader() 方法负责往 WebGLProgram 添加一个片段或者顶点着色器。
        // 第一个参数为webglProgram对象,第二个参数为片段或者顶点的 WebGLShader
        webgl.attachShader(program, vsshader);
        webgl.attachShader(program, fssagder);

        // linkProgram()方法链接给定的WebGLProgram,从而完成为程序的片元和顶点着色器准备 GPU 代码的过程。参数为一个用于链接的WebGLProgram对象
        webgl.linkProgram(program);

        // useProgram() 方法将定义好的WebGLProgram 对象添加到当前的渲染状态中。
        webgl.useProgram(program);

        webgl.program = program;
      }
      var points = [];
      // 数据缓冲区初始化函数
      function initBuffer() {
        //Float32Array 类型数组代表的是平JS内置的标准对象,为 32 位的浮点数型数组,其内容初始化为 0。一旦建立起来,你可以使用这个对象的方法对其元素进行操作,或者使用标准数组索引语法 (使用方括号)。https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Float32Array
        // let pointPosition = new Float32Array([100.0, 100.0, 0.0, 1.0]);

        // getAttribLocation() 方法返回给定WebGLProgram对象中某属性的下标指向位置
        // 第一个参数为WebGLProgram,第二个参数为需要获取下标指向位置的GLSL变量名
        let aPosition = webgl.getAttribLocation(webgl.program, "a_position");

        // vertexAttrib4fv()方法可以为顶点 attibute 变量赋值。
        // 第一个参数为指定了待修改顶点 attribute 变量的存储位置。第二个参数为用于设置顶点 attibute 变量的向量值。
        // webgl.vertexAttrib4fv(aPosition, pointPosition);
        document.addEventListener("mousedown", function (e) {
          // debugger;
          //屏幕坐标
          let x = e.clientX;
          let y = e.clientY;
          let rect = e.target.getBoundingClientRect();
          let pointX = (x - rect.left - 250) / 250;
          let pointy = (250 - (y - rect.top)) / 250;
          console.log(pointX, pointy);

          points.push(pointX, pointy, 0.0, 1.0);

          let pointPosition = new Float32Array(points);
          let pointBuffer = webgl.createBuffer();
          webgl.bindBuffer(webgl.ARRAY_BUFFER, pointBuffer);
          webgl.bufferData(
            webgl.ARRAY_BUFFER,
            pointPosition,
            webgl.STATIC_DRAW
          );
          webgl.enableVertexAttribArray(aPosition);
          webgl.vertexAttribPointer(aPosition, 4, webgl.FLOAT, false, 0, 0);

          webgl.clearColor(0.0, 0.0, 0.0, 1.0);
          webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
          webgl.drawArrays(webgl.POINTS, 0, points.length / 4);
        });

        // getUniformLocation()方法用于获取指定WebGLProgram对象中uniform变量的位置。
        // 第一个参数为要获取uniform变量的WebGLProgram对象,第二个参数为要获取位置的uniform变量的名称。
        let uniforproj = webgl.getUniformLocation(webgl.program, "proj");

        // uniformMatrix4fv()用于设置一个4*4的矩阵类型的uniform变量值。接收4个参数
        // 第一个参数为 要设置值的uniform变量的地址
        // 第二个参数为 是否将矩阵转置,默认false
        // 第三个参数为  要设置的值,应该是一个4*4的矩阵
        // 第四个参数为 矩阵在数组中的偏移量,默认为0
        webgl.uniformMatrix4fv(uniforproj, false, projMat4);
      }
      // webgl的绘制函数
      function draw() {
        // clearColor ()方法指定在清除颜色缓冲区时使用的颜色值。接收的4个参数分别表示 r,g,b,a。取值均在0和1之间。
        webgl.clearColor(0.0, 0.0, 0.0, 1.0);
        // clear() 方法使用预设值来清空缓冲。预设值可以使用 clearColor() 、 clearDepth() 或 clearStencil() 设置。裁剪、抖动处理和缓冲写入遮罩会影响 clear() 方法。参数为一个用于指定需要清除的缓冲区的 GLbitfield (en-US) 。可能的值有:gl.COLOR_BUFFER_BIT(颜色缓冲区);gl.DEPTH_BUFFER_BIT (深度缓冲区)       gl.STENCIL_BUFFER_BIT(模板缓冲区)  没有返回值
        webgl.clear(webgl.COLOR_BUFFER_BIT);

        // drawArrays() 方法用于从向量数组中绘制图元。接收3个参数
        // 第一个参数为指定绘制图元的方式,可能值如下:
        // gl.POINTS: 绘制一系列点。
        // gl.LINE_STRIP: 绘制一个线条。即,绘制一系列线段,上一点连接下一点。
        // gl.LINE_LOOP: 绘制一个线圈。即,绘制一系列线段,上一点连接下一点,并且最后一点与第一个点相连。
        // gl.LINES: 绘制一系列单独线段。每两个点作为端点,线段之间不连接。
        // gl.TRIANGLE_STRIP:绘制一个三角带。
        // gl.TRIANGLE_FAN:绘制一个三角扇。
        // gl.TRIANGLES: 绘制一系列三角形。每三个点作为顶点。
        // 第二个参数为指定从哪个点开始绘制。
        // 第三个参数为指定绘制需要使用到多少个点。
        // webgl.drawArrays(webgl.POINTS, 0, 1);
      }
    </script>
  </head>

  <body onload="init()">
    <canvas id="webglCanvas" width="500" height="500"></canvas>
  </body>
</html>

总结

以上便是动态绘制多个点的所有内容,如有错误之处,还请大家留言指出,谢谢大家了。

相关推荐
糕冷小美n1 分钟前
jeecgbootvue3列表数据状态为数字时,手动赋值的三种方法
前端·javascript·vue.js
mqiqe3 分钟前
Nginx 配置前端后端服务
运维·前端·nginx
小羊小羊,遇事不难1 小时前
Error: near “112136084“: syntax
java·服务器·前端
Domain-zhuo2 小时前
CSS实现一个自定义的滚动条
前端·javascript·css·vue.js·git·node.js
autumn8682 小时前
css的长度单位有那些?
前端·css
李贺梖梖2 小时前
CSS2笔记
前端
张丹 新叶之扉2 小时前
vue的整理
前端·javascript·vue.js
鱼大大博客2 小时前
选择Edge Scdn时应考虑哪些因素?
前端·edge·ddos
️○-2 小时前
安装Node.js和npm
前端·npm·node.js
猫咪钓鱼2 小时前
npm istall 卡住的结解决方法
前端·npm·node.js