对web3D感兴趣的同学一定对WebGL和Three.js不陌生了,前者是web端实现3D场景的不二之选,后者也是业界应用最广泛,认可度最高的web端3D渲染引擎之一。
Three.js说白了就是一个封装了WebGL大量繁琐底层操作的3D渲染库,他可以降低我们着手web3D开发的门槛,提高web3D开发的效率。
下面我们就通过一个旋转立方体的例子对比使用Three.js和直接使用WebGL两者的实现流程,通过这个简单的例子,直观的理解WebGL和Three.js的核心渲染流程。
本文大纲:
本文在PC端网页食用效果更佳,因代码量较大。
下面我们直接使用WebGL实现一个立方体旋转的效果。
首先代码主要分为两部分:着色器代码和js代码。
通常一个webgl程序会有了两个着色器组成一个完整的着色器程序:顶点着色器和片段着色器。
它们使用类似于C或者C++的GLSL强类型编程语言进行编写,分别用于处理顶点数据和颜色数据,下面是本程序的顶点着色器和片段着色器。
//顶点着色器,用于处理顶点数据
//片段着色器,用于处理颜色数据
因为本文旨在比较WebGL与Three.js的绘制流程,因此不详细介绍GLSL的语法,各位同学可以参考代码中的注释简单理解一下。
我们把WebGL在js端的实现分为初始化和渲染两部分。
初始化流程在代码的onMounted中执行,核心流程如下:
渲染我们在drawSence这个函数中执行,核心流程如下:
看一下运行效果:
代码片段
下面是全部的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实现一个类似的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学习过程和经验分享,如果感兴趣可以关注我。