使用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>

总结

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

相关推荐
jyl_sh8 分钟前
WebKit(适用2024年11月份版本)
前端·浏览器·客户端·webkit
zhanghaisong_20151 小时前
Caused by: org.attoparser.ParseException:
前端·javascript·html·thymeleaf
Eric_见嘉1 小时前
真的能无限试(白)用(嫖)cursor 吗?
前端·visual studio code
DK七七1 小时前
多端校园圈子论坛小程序,多个学校同时代理,校园小程序分展示后台管理源码
开发语言·前端·微信小程序·小程序·php
老赵的博客2 小时前
QSS 设置bug
前端·bug·音视频
Chikaoya2 小时前
项目中用户数据获取遇到bug
前端·typescript·vue·bug
南城夏季2 小时前
蓝领招聘二期笔记
前端·javascript·笔记
Huazie2 小时前
来花个几分钟,轻松掌握 Hexo Diversity 主题配置内容
前端·javascript·hexo
NoloveisGod2 小时前
Vue的基础使用
前端·javascript·vue.js
GISer_Jing2 小时前
前端系统设计面试题(二)Javascript\Vue
前端·javascript·vue.js