WebGL打开 3D 世界的大门(一):基础概念

用GPU的强大性能WebGL(Web Graphics Library)是在浏览器中渲染交互的3D图形引擎,使得前端程序员能够利用GPU的强大性能,创造出丰富的视觉体验。

渲染流程

它利用GLSL语言代码片段,在GPU上运行,代码块里面必须包括顶点着色器和片元着色器,顶点着色器用来计算一系列顶点的位置,片元着色器用来计算每个像素点的颜色插值。然后调用gl.drawArrays或gl.drawElements在GPU上执行。GPU通过以下四种方式获取数据:

  1. 属性(Attributes)和缓冲:属性用来指明怎么从缓冲中获取所需数据并将它提供给顶点着色器。 例如你可能在缓冲中用三个32位的浮点型数据存储一个位置值。
  2. 全局变量(Uniforms)
  3. 纹理(Textures):多数情况存放的是图像数据
  4. 变量(Varyings):线还是三角形,顶点着色器中设置的可变量会在片段着色器运行中获取不同的插值。

说了这么多不如直接上代码先画一个简单的正方形如图:

gitee.com/feng-lianxi... 这里面的代码不用全部都懂,只要注意哪里是片元着色器,那个是顶点着色器,怎么传递个GPU的,然后调用什么方法渲染出来就行了。

css 复制代码
     // Set clear color to black, fully opaque
     gl.clearColor(0.0, 0.0, 0.0, 1.0);
     // Clear the color buffer with specified clear color
     gl.clear(gl.COLOR_BUFFER_BIT);

这两句话的意思是将画布用黑色初始化。顶点着色器:

ini 复制代码
      const vsSource = `
        attribute vec4 a_position; //一个4维的向量的顶点属性变量
        void main() {
          gl_Position = a_position; //gl_Position为系统默认变量,
        }
    `;

片元着色器:

ini 复制代码
       const fsSource = `
        void main() {
          gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); //白色,gl_FragColor为系统默认变量
        }
      `;

接下来就是怎么给顶点着色器定义值和赋值的问题了:GLSL通过定义缓冲区,向缓冲区绑定数据,然后规定顶点着色器以什么样的方式取缓冲区的数据。最后规定以什么样的方式在画布上画出这些点。

  1. 建立缓冲区并且缓冲区绑定数据

    javascript 复制代码
       // Create a buffer and put three 2d clip space points in it
       var positionBuffer = gl.createBuffer();
       // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
       gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
       var positions = [0.5, 0.5, -0.5, 0.5, 0.5, -0.5, -0.5, -0.5];
       //gl.bufferData(target, sizeOrData, usage);
       //target:gl.ARRAY_BUFFER:存储顶点数据,gl.ELEMENT_ARRAY_BUFFER:存储索引数据
       //usage:`gl.STATIC_DRAW`: 数据内容不会或很少更改。`gl.DYNAMIC_DRAW`: 数据内容会频繁更改。`l.STREAM_DRAW`: 数据内容每次绘制时都会更改
       gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); 
       
  2. 规定顶点着色器以什么样的方式取缓冲区的数据

    go 复制代码
      // look up where the vertex data needs to go.
      var positionAttributeLocation = gl.getAttribLocation(program, "a_position");
    
      // Turn on the attribute
      gl.enableVertexAttribArray(positionAttributeLocation);
    
      // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
      var size = 2;          // 2 components per iteration
      var type = gl.FLOAT;   // the data is 32bit floats
      var normalize = false; // don't normalize the data
      var stride = 0;        // 0 = move forward size * sizeof(type) each iteration to get the next position
      var offset = 0;        // start at the beginning of the buffer
      gl.vertexAttribPointer(
          positionAttributeLocation, size, type, normalize, stride, offset);

    这里面规定的是从缓冲区的头部开始,一次取两个,不进行归一化处理。

  3. 规定以什么样的方式在画布上画出这些点

    ini 复制代码
      var primitiveType = gl.TRIANGLE_STRIP;
      var offset = 0;
      var count = 4;
      gl.drawArrays(gl.TRIANGLE_STRIP, offset, count);
      

    有必要单独讲一下 drawArrays 方法:gl.drawArrays(mode, first, count):
    mode: 指定要绘制的图元类型。常见的模式包括:

    markdown 复制代码
    -   `gl.POINTS`: 绘制一系列点。
    -   `gl.LINES`: 绘制一系列单独的线段。
    -   `gl.LINE_STRIP`: 绘制一条连续的折线。
    -   `gl.LINE_LOOP`: 绘制一条闭合的折线。
    -   `gl.TRIANGLES`: 绘制一系列单独的三角形。
    -   `gl.TRIANGLE_STRIP`: 绘制一系列连续的三角形(共享顶点)。
    -   `gl.TRIANGLE_FAN`: 绘制一系列以第一个顶点为中心的三角形。

    first:

    diff 复制代码
    -   指定从哪个顶点开始绘制。它是一个偏移量,表示从顶点数组的第一个顶点开始跳过的顶点数。

    count:

    diff 复制代码
    -   指定要绘制的顶点数量。

在我们这里的意思就是,从数据开始进行绘制,绘制一系列三角形。并使用片元着色器进行填充。

坐标系统

上面注意到我们设定的坐标都是-1到1之间的数,webgl的坐标系统是右手坐标系,正中心是0点,最小值是-1,最大值是1,但是我们写的时候,大部分想基于屏幕的像素,这就需要对顶点坐标进行处理:

java 复制代码
  // an attribute will receive data from a buffer
  attribute vec2 a_position;
  uniform vec2 u_resolution;
  // attribute vec4 a_position;
  // all shaders have a main function
  void main() {

    // gl_Position is a special variable a vertex shader
    // is responsible for setting
    vec2 zeroToOne = a_position - u_resolution / 2.0;
    vec2 zeroToTwo = zeroToOne / u_resolution * 2.0;
    gl_Position = vec4(zeroToTwo, 0, 1);
  }

给属性赋值:

ini 复制代码
      var resolutionUniformLocation = gl.getUniformLocation(program, "u_resolution");
      gl.uniform2f(resolutionUniformLocation, gl.canvas.width, gl.canvas.height);
      

这样就可以使用屏幕坐标系赋值了:

ini 复制代码
    var positions = [320, 240, 0, 240, 320, 0, 0, 0];

修改正方形颜色

首先定义个全局的颜色变量,片元着色器如下:

csharp 复制代码
  precision mediump float;
  uniform vec4 u_color;
  void main() {
    // gl_FragColor is a special variable a fragment shader
    // is responsible for setting
    gl_FragColor = u_color; // return redish-purple
  }

然后给片元着色器赋随机值:

javascript 复制代码
  // get the color
  var colorUniformLocation = gl.getUniformLocation(program, "u_color");
  // random color
  gl.uniform4fv(colorUniformLocation, [Math.random(), Math.random(), Math.random(), 1]);

这样阵正方形每次都被渲染成不同的颜色。

通过点击事件画点

接下来我们可以思考怎么通过点击事件在画布上画点了。无非就是点击后获取屏幕坐标,转换成canvas上的坐标位置,然后赋值给bufferData,然后调用gl.drawArrays方法,将这些这个点画出来就行了:

ini 复制代码
  canvas.addEventListener('click', function(e) {
    let x = e.clientX;
    let y = e.clientY;
    let rect = e.target.getBoundingClientRect();
    x = x - rect.left;
    y = rect.bottom - y;
    console.log(x, y);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([x, y, 0, 1]), gl.STATIC_DRAW);
    gl.drawArrays(gl.POINTS, 0, 1);
  });
  

总结

你可能已经体会到webgl的工作其实比较简单,就是根据顶点着色器和片元着色器,去绘制点、线、三角形。虽然三维图形很复杂,只是程序员的计算复杂了。webgl的api只做光栅化处理,这个概念还是比较容易理解的。这一节有了大概的认识后,我们下一节将深入讲解webgl的基本原理。

相关推荐
森哥的歌15 小时前
SVG 知识详解:从入门到精通
svg·数据可视化·前端开发·矢量图形·web图形
李恒-聆机智能专精数采2 天前
从零开始了解数据采集(二十七)——什么IIOT平台
大数据·人工智能·云计算·制造·数据采集·数据可视化
HsuHeinrich3 天前
利用散点图探索宇航员特征与太空任务之间的关系
python·数据可视化
前端小崔4 天前
从零开始学习three.js(15):一文详解three.js中的纹理映射UV
前端·javascript·学习·3d·webgl·数据可视化·uv
郭不耐5 天前
DeepSeek智能时空数据分析(八):NL2SQL绘制河流-轨迹缓冲区如何生成
大数据·数据分析·云计算·aigc·数据可视化
枝上棉蛮5 天前
智慧医院的可视化变革:可视化工具助力数字化转型
信息可视化·数据挖掘·数据分析·数字孪生·数据可视化·智慧医院
zhanghongyi_cpp6 天前
当当网Top500书籍信息爬取与分析
python·网络爬虫·数据可视化
How_doyou_do7 天前
项目实战-25年美赛MCM/ICM-基于数学建模与数据可视化的动态系统模型
python·数学建模·数据可视化
郭不耐7 天前
DeepSeek智能时空数据分析(九):NL2SQL绘制河流名字-如何给轨迹添加说明文字
信息可视化·数据分析·aigc·数据可视化·大屏端
前端小崔8 天前
从零开始学习three.js(14):一文详解three.js中的粒子系统Points
开发语言·前端·javascript·学习·3d·webgl·数据可视化