Three.js一起学-对比WebGL和Three.js的渲染流程

前言

大家好,我是一拳~
Three.js一起学-对比WebGL和Three.js的渲染流程_第1张图片

对web3D感兴趣的同学一定对WebGL和Three.js不陌生了,前者是web端实现3D场景的不二之选,后者也是业界应用最广泛,认可度最高的web端3D渲染引擎之一。
Three.js说白了就是一个封装了WebGL大量繁琐底层操作的3D渲染库,他可以降低我们着手web3D开发的门槛,提高web3D开发的效率。
下面我们就通过一个旋转立方体的例子对比使用Three.js和直接使用WebGL两者的实现流程,通过这个简单的例子,直观的理解WebGL和Three.js的核心渲染流程。
本文大纲:

  • 使用WebGL创建一个3D场景
  • 使用Three.js创建一个3D场景

本文在PC端网页食用效果更佳,因代码量较大。

WebGL实现

下面我们直接使用WebGL实现一个立方体旋转的效果。
首先代码主要分为两部分:着色器代码和js代码。

着色器代码

通常一个webgl程序会有了两个着色器组成一个完整的着色器程序:顶点着色器和片段着色器。

它们使用类似于C或者C++的GLSL强类型编程语言进行编写,分别用于处理顶点数据和颜色数据,下面是本程序的顶点着色器和片段着色器。

//顶点着色器,用于处理顶点数据

//片段着色器,用于处理颜色数据

因为本文旨在比较WebGL与Three.js的绘制流程,因此不详细介绍GLSL的语法,各位同学可以参考代码中的注释简单理解一下。

javascript实现

我们把WebGL在js端的实现分为初始化和渲染两部分。
初始化流程在代码的onMounted中执行,核心流程如下:

  • 创建着色器程序
  • 获取着色器程序中属性和全局变量的地址
  • 创建顶点数据缓冲并绑定顶点数据到缓冲
  • 创建颜色数据缓冲并绑定颜色数据到缓冲
  • 初始化完成调用drawScence渲染

渲染我们在drawSence这个函数中执行,核心流程如下:

  • 清空画布
  • 设置绘制上下文
  • 应用着色器程序
  • 读取缓冲中的顶点数据和颜色数据,传递给顶点着色器对应的属性和全局变量
  • 计算投影矩阵、相机矩阵、转换矩阵等,把最终的矩阵传递给顶点着色器中的全局变量
  • 执行绘制方法
  • 使用requestAnimationFrame实现动画效果

看一下运行效果:
代码片段

下面是全部的WebGL实现这个3D场景的代码,可以直接看onMounted函数和drawSence函数的实现即可,其他的代码可以选择性跳过,一些工具方法不必理解,知道是做什么的即可。我把代码都列在下面,各位同学可以先体会一下使用原生的WebGL实现一个3D场景是多么的繁琐。。。

    //属性和全局变量
    let positionAttributeLocation;
    let colorLocation;
    let matrixLocation;
    //位置顶点缓冲
    let positionBuffer;
    let colorBuffer;
    //着色器程序对象
    let program;
    let gl;
    let fieldOfViewRadians = degToRad(60);
    let radius = 200;
    let cameraAngleRadians=0;
    let rotationSpeed = 0.01;
    onMounted(() => {
      /* 初始化工作开始-------- */
      const canvas = document.getElementById("c");
      gl = canvas.getContext("webgl");
      if (!gl) {
        console.log('你不能使用WebGL');
        return;
      }
      //创建着色器
      const vertexShaderSource = document.querySelector("#vertex-shader-2d")?.textContent ;
      const fragmentShaderSource = document.querySelector("#fragment-shader-2d")?.textContent ;
      const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource) ;
      const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource) ;
      program = createProgram(gl, vertexShader, fragmentShader) ;
      
      //获取着色器程序中属性和全局变量的地址
      positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
      colorLocation = gl.getAttribLocation(program, "a_color");
      matrixLocation = gl.getUniformLocation(program, 'u_matrix') ;
      console.log(matrixLocation);
      
      //创建顶点缓冲
      positionBuffer = gl.createBuffer() ;
      //绑定顶点缓冲
      gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
      //设置图形顶点数据
      setGeometry(gl);//做bufferData操作
      //创建颜色缓冲
      colorBuffer = gl.createBuffer() ;
      gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
      setColors(gl);//做bufferData操作
      
      /* 初始化工作结束------- */
      /* 开始渲染------- */
      drawScence(gl)
    });
    //绘制场景
    function drawScence(gl) {
      cameraAngleRadians+= rotationSpeed;  // 
      resize(gl.canvas);
      //这样就告诉WebGL裁剪空间的 -1 -> +1 分别对应到x轴的 0 -> gl.canvas.width 和y轴的 0 -> gl.canvas.height。
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
      //清空画布
      gl.clearColor(0, 0, 0, 0);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
      //开启这个特性后WebGL默认“剔除”背面三角形
      // gl.enable(gl.CULL_FACE);
      //启用深度缓冲
      gl.enable(gl.DEPTH_TEST);
      // 告诉它用我们之前写好的着色程序(一个着色器对)
      gl.useProgram(program);
      //启用属性
      gl.enableVertexAttribArray(positionAttributeLocation);
      gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
      //WebGL可以通过绑定点操控全局范围内的许多数据,你可以把绑定点想象成一个WebGL内部的全局变量。 首先绑定一个数据源到绑定点,然后可以引用绑定点指向该数据源。 所以让我们来绑定位置信息缓冲(下面的绑定点就是ARRAY_BUFFER)。
      let size = 3;// 每次迭代运行提取3个单位数据
      let type = gl.FLOAT;// 每个单位的数据类型是32位浮点型
      let normalize = false;// 不需要规范化的数据
      let stride = 0;// 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))
      // 每次迭代运行运动多少内存到下一个数据开始点
      let offset = 0;  // 从缓冲起始位置开始读取
      gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);

      // 启用颜色属性
      gl.enableVertexAttribArray(colorLocation);
      // Bind the color buffer.
      gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
      let size1 = 3;                 // 每次迭代运行提取3个单位数据
      let type1 = gl.UNSIGNED_BYTE;  // 每个单位的数据类型是8位无符号值
      let normalize1 = true;         // 规范化的数据 (把 0-255 转换为 0-1)
      let stride1 = 0;               // 0 = 移动单位数量 * 每个单位占用内存(sizeof(type))
      // 每次迭代运行运动多少内存到下一个数据开始点
      let offset1 = 0;               // 从缓冲起始位置开始读取
      gl.vertexAttribPointer(colorLocation, size1, type1, normalize1, stride1, offset1);
      /* 矩阵计算开始 */
      //计算投影矩阵
      let aspect = gl.canvas.width / gl.canvas.height;
      let zNear = 0.1;
      let zFar = 1500;
      // 计算立方体中心的位置
      let fPosition = [75, 75, 75];
      let projectionMatrix = m4.perspective(fieldOfViewRadians, aspect, zNear, zFar);
      // 计算相机的矩阵
      //平移
      let cameraMatrix = m4.translation(200, 300, radius);
      // 获得矩阵中相机的位置
      let cameraPosition = [
        cameraMatrix[12],
        cameraMatrix[13],
        cameraMatrix[14],
      ];
      let up = [0, 1, 0];
      // 计算相机的朝向矩阵
      cameraMatrix = m4.lookAt(cameraPosition, fPosition, up);
      // 通过相机矩阵计算视图矩阵,利用相对论的概念,视图的运动是相机的反方向运动,因此求出逆矩阵就可
      let viewMatrix = m4.inverse(cameraMatrix);
      // 计算组合矩阵
      let matrix = m4.multiply(projectionMatrix, viewMatrix);
      matrix=m4.yRotate(matrix,cameraAngleRadians)
      matrix=m4.zRotate(matrix,cameraAngleRadians)
      matrix=m4.translate(matrix,-75,-75,-75)
      /* 矩阵计算结束 */

      // 设置矩阵
      gl.uniformMatrix4fv(matrixLocation, false, matrix);
      // 绘制图形
      gl.drawArrays(gl.TRIANGLES, 0, 6*6);
      requestAnimationFrame(()=>{drawScence(gl)});
    }
    //编译着色器代码
    function createShader(gl, type, source) {
      let shader = gl.createShader(type);//创建着色器对象
      gl.shaderSource(shader, source);//提供数据源
      gl.compileShader(shader);//编译->生成着色器
      const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
      if (success) {
        return shader;
      } else {
        console.log(gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
      }
    }
    //创建着色器程序
    function createProgram(gl, vertexShader, fragmentShader) {
      const program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
      const success = gl.getProgramParameter(program, gl.LINK_STATUS);
      if (success) {
        return program;
      }
      gl.deleteProgram(program);
      return null;
    }
    //响应window尺寸变化
    function resize(canvas) {
      // 获取浏览器中画布的显示尺寸
      var displayWidth = canvas.clientWidth;
      var displayHeight = canvas.clientHeight;

      // 检尺寸是否相同
      if (canvas.width != displayWidth ||
        canvas.height != displayHeight) {
        // 设置为相同的尺寸
        canvas.width = displayWidth;
        canvas.height = displayHeight;
      }
    }
    //单位化向量
    function normalize(v) {
      var length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
      // 确定不会除以 0
      if (length > 0.00001) {
        return [v[0] / length, v[1] / length, v[2] / length];
      } else {
        return [0, 0, 0];
      }
    }
    //向量相减
    function subtractVectors(a, b) {
      return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
    }
    //这是计算叉乘的代码
    function cross(a, b) {
      return [a[1] * b[2] - a[2] * b[1],
              a[2] * b[0] - a[0] * b[2],
              a[0] * b[1] - a[1] * b[0]];
    }
    function degToRad(d) {
      return d * Math.PI / 180;
    }
    // 在缓冲存储构成 立方体 的值
    function setGeometry(gl) {
      //一个面由两个三角形六个顶点组成
      gl.bufferData(
        gl.ARRAY_BUFFER,
        new Float32Array([
          //前面
          0, 0, 0,
          150, 150, 0,
          0, 150, 0,
          150, 0, 0,
          150, 150, 0,
          0, 0, 0,
        
          // 后面
          0, 150, 150,
          0, 0, 150,
          150, 150, 150,
          150, 0, 150,
          150, 150, 150,
          0, 0, 150,

          // 上面
        
          0, 150, 150,
          0, 150, 0,
          150, 150, 150,
          150, 150, 0,
          150, 150, 150,
          0, 150, 0,

          // 下面
          0, 0, 0,
          0, 0, 150,
          150, 0, 150,
          150, 0, 150,
          150, 0, 0,
          0, 0, 0,

          // 左面
          0, 150, 0,
          0, 0, 0,
          0, 150, 150,
          0, 0, 0,
          0, 0, 150,
          0, 150, 150,
          

          // 右面
          150, 150, 0,
          150, 0, 0,
          150, 150, 150,
          150, 0, 150,
          150, 150, 150,
          150, 0, 0,
        ]),
        gl.STATIC_DRAW);
    }
    // Fill the buffer with colors for the 'F'.
    function setColors(gl) {
      gl.bufferData(
        gl.ARRAY_BUFFER,
        new Uint8Array([
          70, 70, 120,
          70, 70, 120,
          70, 70, 120,
          70, 70, 120,
          70, 70, 120,
          70, 70, 120,

          200, 70, 120,
          200, 70, 120,
          200, 70, 120,
          200, 70, 120,
          200, 70, 120,
          200, 70, 120,

          200, 120, 120,
          200, 120, 120,
          200, 120, 120,
          200, 120, 120,
          200, 120, 120,
          200, 120, 120,

          120, 70, 200,
          120, 70, 200,
          120, 70, 200,
          120, 70, 200,
          120, 70, 200,
          120, 70, 200,

          60, 70, 200,
          60, 70, 200,
          60, 70, 200,
          60, 70, 200,
          60, 70, 200,
          60, 70, 200,

          80, 150, 200,
          80, 150, 200,
          80, 150, 200,
          80, 150, 200,
          80, 150, 200,
          80, 150, 200,
        ]),
        gl.STATIC_DRAW);
    }
    //三维矩阵操作工具
    let m4 = {
      //相机朝向矩阵
      lookAt: function(cameraPosition, target, up) {
        var zAxis = normalize(
            subtractVectors(cameraPosition, target));
        var xAxis = normalize(cross(up, zAxis));
        var yAxis = normalize(cross(zAxis, xAxis));

        return [
          xAxis[0], xAxis[1], xAxis[2], 0,
          yAxis[0], yAxis[1], yAxis[2], 0,
          zAxis[0], zAxis[1], zAxis[2], 0,
          cameraPosition[0],
          cameraPosition[1],
          cameraPosition[2],
          1,
        ];
      },
      //透视矩阵
      perspective: function (fieldOfViewInRadians, aspect, near, far) {
        var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians);
        var rangeInv = 1.0 / (near - far);

        return [
          f / aspect, 0, 0, 0,
          0, f, 0, 0,
          0, 0, (near + far) * rangeInv, -1,
          0, 0, near * far * rangeInv * 2, 0
        ];
      },
      //视图矩阵,处理了分辨率适配
      projection: function (width, height, depth) {
        // 注意:这个矩阵翻转了 Y 轴,所以 0 在上方
        return [
          2 / width, 0, 0, 0,
          0, -2 / height, 0, 0,
          0, 0, 2 / depth, 0,
          -1, 1, 0, 1,
        ];
      },
      translation: function (tx, ty, tz) {
        return [
          1, 0, 0, 0,
          0, 1, 0, 0,
          0, 0, 1, 0,
          tx, ty, tz, 1,
        ];
      },

      xRotation: function (angleInRadians) {
        var c = Math.cos(angleInRadians);
        var s = Math.sin(angleInRadians);

        return [
          1, 0, 0, 0,
          0, c, s, 0,
          0, -s, c, 0,
          0, 0, 0, 1,
        ];
      },

      yRotation: function (angleInRadians) {
        var c = Math.cos(angleInRadians);
        var s = Math.sin(angleInRadians);

        return [
          c, 0, -s, 0,
          0, 1, 0, 0,
          s, 0, c, 0,
          0, 0, 0, 1,
        ];
      },

      zRotation: function (angleInRadians) {
        var c = Math.cos(angleInRadians);
        var s = Math.sin(angleInRadians);

        return [
          c, s, 0, 0,
          -s, c, 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1,
        ];
      },
      scaling: function (sx, sy, sz) {
        return [
          sx, 0, 0, 0,
          0, sy, 0, 0,
          0, 0, sz, 0,
          0, 0, 0, 1,
        ];
      },
      translate: function (m, tx, ty, tz) {
        return m4.multiply(m, m4.translation(tx, ty, tz));
      },

      xRotate: function (m, angleInRadians) {
        return m4.multiply(m, m4.xRotation(angleInRadians));
      },

      yRotate: function (m, angleInRadians) {
        return m4.multiply(m, m4.yRotation(angleInRadians));
      },

      zRotate: function (m, angleInRadians) {
        return m4.multiply(m, m4.zRotation(angleInRadians));
      },
      scale: function (m, sx, sy, sz) {
        return m4.multiply(m, m4.scaling(sx, sy, sz));
      },
      //矩阵相乘
      multiply: function (a, b) {
        var a00 = a[0 * 4 + 0];
        var a01 = a[0 * 4 + 1];
        var a02 = a[0 * 4 + 2];
        var a03 = a[0 * 4 + 3];
        var a10 = a[1 * 4 + 0];
        var a11 = a[1 * 4 + 1];
        var a12 = a[1 * 4 + 2];
        var a13 = a[1 * 4 + 3];
        var a20 = a[2 * 4 + 0];
        var a21 = a[2 * 4 + 1];
        var a22 = a[2 * 4 + 2];
        var a23 = a[2 * 4 + 3];
        var a30 = a[3 * 4 + 0];
        var a31 = a[3 * 4 + 1];
        var a32 = a[3 * 4 + 2];
        var a33 = a[3 * 4 + 3];
        var b00 = b[0 * 4 + 0];
        var b01 = b[0 * 4 + 1];
        var b02 = b[0 * 4 + 2];
        var b03 = b[0 * 4 + 3];
        var b10 = b[1 * 4 + 0];
        var b11 = b[1 * 4 + 1];
        var b12 = b[1 * 4 + 2];
        var b13 = b[1 * 4 + 3];
        var b20 = b[2 * 4 + 0];
        var b21 = b[2 * 4 + 1];
        var b22 = b[2 * 4 + 2];
        var b23 = b[2 * 4 + 3];
        var b30 = b[3 * 4 + 0];
        var b31 = b[3 * 4 + 1];
        var b32 = b[3 * 4 + 2];
        var b33 = b[3 * 4 + 3];
        return [
          b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
          b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
          b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
          b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
          b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
          b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
          b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
          b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
          b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
          b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
          b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
          b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
          b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
          b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
          b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
          b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
        ];
      },
      orthographic: function (left, right, bottom, top, near, far) {
        return [
          2 / (right - left), 0, 0, 0,
          0, 2 / (top - bottom), 0, 0,
          0, 0, 2 / (near - far), 0,

          (left + right) / (left - right),
          (bottom + top) / (bottom - top),
          (near + far) / (near - far),
          1,
        ];
      },
      //逆矩阵
      inverse: function(m) {
        var m00 = m[0 * 4 + 0];
        var m01 = m[0 * 4 + 1];
        var m02 = m[0 * 4 + 2];
        var m03 = m[0 * 4 + 3];
        var m10 = m[1 * 4 + 0];
        var m11 = m[1 * 4 + 1];
        var m12 = m[1 * 4 + 2];
        var m13 = m[1 * 4 + 3];
        var m20 = m[2 * 4 + 0];
        var m21 = m[2 * 4 + 1];
        var m22 = m[2 * 4 + 2];
        var m23 = m[2 * 4 + 3];
        var m30 = m[3 * 4 + 0];
        var m31 = m[3 * 4 + 1];
        var m32 = m[3 * 4 + 2];
        var m33 = m[3 * 4 + 3];
        var tmp_0  = m22 * m33;
        var tmp_1  = m32 * m23;
        var tmp_2  = m12 * m33;
        var tmp_3  = m32 * m13;
        var tmp_4  = m12 * m23;
        var tmp_5  = m22 * m13;
        var tmp_6  = m02 * m33;
        var tmp_7  = m32 * m03;
        var tmp_8  = m02 * m23;
        var tmp_9  = m22 * m03;
        var tmp_10 = m02 * m13;
        var tmp_11 = m12 * m03;
        var tmp_12 = m20 * m31;
        var tmp_13 = m30 * m21;
        var tmp_14 = m10 * m31;
        var tmp_15 = m30 * m11;
        var tmp_16 = m10 * m21;
        var tmp_17 = m20 * m11;
        var tmp_18 = m00 * m31;
        var tmp_19 = m30 * m01;
        var tmp_20 = m00 * m21;
        var tmp_21 = m20 * m01;
        var tmp_22 = m00 * m11;
        var tmp_23 = m10 * m01;

        var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) -
            (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
        var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) -
            (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
        var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) -
            (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
        var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) -
            (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);

        var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);

        return [
          d * t0,
          d * t1,
          d * t2,
          d * t3,
          d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) -
                (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)),
          d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) -
                (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)),
          d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) -
                (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)),
          d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) -
                (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)),
          d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) -
                (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)),
          d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) -
                (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)),
          d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) -
                (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)),
          d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) -
                (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)),
          d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) -
                (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)),
          d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) -
                (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)),
          d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) -
                (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)),
          d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) -
                (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02))
        ];
      },
    }

看完上面的代码,我相信好多同学的头都大了,实现这样一个简单的3D场景竟要这么繁琐的代码,不禁让很多同学失去了对WebGL实现炫酷3D效果的信心。

上面的代码虽然有一些是工具方法,并不是本程序必要的,但是即使去掉这部分代码,剩余的代码量仍然很大。

不要担心,我们还有Three.js这个大杀器,它可以使用简短的代码实现上述的功能。

Three.js实现

下面让我们看一下如何使用Three.js实现一个类似的3D场景。

import { defineComponent ,ref , reactive, onMounted } from 'vue';
import {Scene, PerspectiveCamera, WebGLRenderer, BoxGeometry, MeshBasicMaterial, Mesh} from 'three'
export default defineComponent({
  setup() {
    onMounted(()=>{
      //创建场景
      const scene = new Scene();
      //创建相机(透视相机)
      let camera=new PerspectiveCamera(30,window.innerWidth/window.innerHeight,0.1,1000);
      //创建渲染器
      const renderer= new WebGLRenderer();
      renderer.setSize(window.innerWidth,window.innerHeight);
      document.getElementById('canvas').appendChild(renderer.domElement);
      //创建立方体对象
      const geometry=new BoxGeometry(1,1,1);
      //创建材质对象,给立方体的六个面添加不同的颜色
      const meterials=[
        new MeshBasicMaterial({color:0x464678,}),
        new MeshBasicMaterial({color:0xC84678}),
        new MeshBasicMaterial({color:0xC87878}),
        new MeshBasicMaterial({color:0x7846C8}),
        new MeshBasicMaterial({color:0x3C46C8}),
        new MeshBasicMaterial({color:0x5096C8}),
      ];
      //创建网格对象,组合立方体和材质
      const cube=new Mesh(geometry,meterials);
      //将完整立方体添加到场景中去
      scene.add(cube);
      //设置相机位置
      camera.position.z=6;
      //动画渲染
      function animate(){
          requestAnimationFrame(animate);
          cube.rotation.y += 0.01;
          cube.rotation.z += 0.01;
          renderer.render(scene,camera);
      }
      animate()
    })
  },
});

怎么说呢,是不是瞬间神清气爽,感觉自己又可以了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KQekFOO2-1661502466166)(https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/72171315e44047abbc8801a379ad6d72~tplv-k3u1fbpfcp-watermark.image?)]

使用Three.js实现3D立方体旋转的场景的代码非常简短,逻辑清晰,上面在代码中给出了注释,这里就不再重复阐述了。

我们分析上面的代码,不难发现Three.js实际上帮我们封装了创建着色器、顶点数据、颜色数据、缓冲、投影矩阵、视图矩阵等操作,我们只需要调用Three.js封装好的api接口即可创建出炫酷的3D效果。

下面是使用Three.js的运行效果。
代码片段

最后

以上就是本文的全部内容了,本文分别通过原生WebGL和Three.js实现了一个3D立方体旋转的场景,通过直观的对比,我们可以发现使用Three.js可以极大的提高我们开发3D场景的效率,并可以直观的了解到Three.js是一个封装了创建着色器、顶点数据、颜色数据、缓冲、投影矩阵、视图矩阵等WebGL底层操作的3D渲染引擎。

**各位同学如何觉得通过阅读本文有所收获,还望不吝点赞、收藏+关注。后续会持续更新我的WebGL和Three.js学习过程和经验分享,如果感兴趣可以关注我。

你可能感兴趣的:(Three.js,前端,Three.js,WebGL)