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


    前言

    大家好,我是一拳~

    对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强类型编程语言进行编写,分别用于处理顶点数据和颜色数据,下面是本程序的顶点着色器和片段着色器。

    //顶点着色器,用于处理顶点数据
    
    //片段着色器,用于处理颜色数据
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    因为本文旨在比较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))
            ];
          },
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556

    看完上面的代码,我相信好多同学的头都大了,实现这样一个简单的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()
        })
      },
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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学习过程和经验分享,如果感兴趣可以关注我。

  • 相关阅读:
    蓝桥杯-粘木棍-DFS
    Kafka3.0.0版本——消费者(分区的分配以及再平衡)
    DHCPsnooping 配置实验(1)
    Python数据攻略-Pandas时间序列数据处理
    vue.js实例选项
    用于车载T-BOX汽车级的RA8900CE
    ABAP 报表中如何以二进制方式上传本地文件试读版
    【LeetCode练习】19. 删除链表的倒数第 N 个结点(中等|JS|快慢指针)
    单源点最短路径(输出路径)
    图解 Google V8 # 18 :异步编程(一):V8是如何实现微任务的?
  • 原文地址:https://blog.csdn.net/qq_33718648/article/details/126545997