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的基本原理。

相关推荐
玄魂16 小时前
报表优化实战:组件库Table升级VTable
前端·开源·数据可视化
百度地图开放平台2 天前
LBS 开发微课堂|全新track轨迹插件:轻松实现轨迹可视化效果
前端·javascript·数据可视化
Sharewinfo_BJ2 天前
关系图:赋能数据可视化的动态扩展
数据分析·数据可视化·powerbi
Heorine2 天前
数学建模 绘图 图表 可视化(3)
python·数据可视化
Mapmost2 天前
告别PPT内卷!3D黑科技方案让你秒杀竞标现场!
数据可视化
Mapmost3 天前
【数据可视化艺术·进阶篇】热力图探秘:用色彩演绎场馆和景区的人流奥秘
前端·人工智能·数据可视化
yaoganjili3 天前
WebGL打开 3D 世界的大门(三):着色器和GLSL语言
前端·数据可视化
十一月二十二3 天前
leaflet+天地图+更换地图主题
前端·数据可视化
小白的高手之路4 天前
Pytorch中Tensorboard的学习
人工智能·pytorch·python·深度学习·学习·数据可视化
永洪科技5 天前
从“制造”到“智造”,看中集“灯塔”生产线与永洪“数据技术”的紧密融合
大数据·数据分析·制造·数据可视化·bi