【Three.js】随着元宇宙开启WEB3D之路
当视频游戏遇到Web 2.0时会发生什么? 当虚拟世界相遇地球的地理空间地图?当模拟变成现实,生活和商业变成虚拟?当你使用虚拟地球在物理地球上导航时,你的化身就变成了你的在线代理?这一切发生的就是元宇宙。
元宇宙设想了一个由虚拟世界和3D技术广泛应用重塑的未来。Three.js是一个非常令人印象深刻的 JavaScript 3D 库,它也使用 WebGL(或 2d Canvas)进行渲染。随着 WebGL API 标准的改进,以及对 WebXR 的支持,Three.js 成为了一个可以用来营造沉浸式体验的主流工具。与此同时,浏览器对 3D 渲染和 WebXR 设备 API 的支持也得到提升,使得 web 成为一个越来越有吸引力的 3D 内容平台。
Three.js
Three.js 是Ricardo Cabello(@mrdoob) 在2010年开发的一个JavaScript库(如今它在Github上有许多贡献者)。这个令人难以置信的工具让用户可以在浏览器上处理 3D 图形,使用 WebGL 技术非常简单和直观的方式来实现。而 WebGL 技术已经非常普及和被浏览器广泛支持。
WebGL 在许多设备和浏览器中创建丰富的交互体验,点击查看浏览器支持程度。
开始
本文将以 Vue 为基础框架来构建 Three.js 示例,关于代码可以查阅 GitHub。
首先安装依赖:
npm install three --save
构建
现在开始添加代码到页面,如下:
<template>
<div id="app"></div>
</template>
<script>
import * as THREE from "three";
export default {
name: "App",
data() {
return {};
},
mounted() {
this.init();
},
methods: {
init() {
const scene = new THREE.Scene();
// 创建一个基本透视相机 camera
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 4;
// 创建一个抗锯齿渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 配置渲染器清除颜色
renderer.setClearColor("#000000");
// 配置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 添加渲染器到DOM
document.body.appendChild(renderer.domElement);
// 创建一个立方体网格
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: "#433F81" });
const cube = new THREE.Mesh(geometry, material);
// 将立方体到场景中
scene.add(cube);
const render = function () {
requestAnimationFrame(render);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
render();
},
},
};
</script>
然后再添加简单的样式:
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
执行脚本 yarn serve
,打开浏览器将看到如下效果:
这是每个 Three.js 应用程序一种常见模式,包括 WebGlRenderer
、 Scene
、 Camera
,如下:
- 创建渲染器
WebGlRenderer
- 创建
Scene
- 创建
Camera
渲染器是放置场景结果的地方。在 Three.js 中,可以有多个场景,每个场景可以有不同的对象。
在示例中创建 WebGLRenderer
,再将窗口的大小作为参数传递给它,然后将它附加到 DOM 中。
// 创建一个抗锯齿渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
// 配置渲染器清除颜色
renderer.setClearColor("#000000");
// 配置渲染器尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 添加渲染器到DOM
document.body.appendChild(renderer.domElement);
首先需要创建一个空场景,将在其中添加创建的立方体对象:
const scene = new THREE.Scene();
最后创建一个相机 camera
,将 视野 、纵横比以及近平面和远平面作为参数:
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
到此 Three.js 应用程序的 3 个基本元素已经完成了。
几何、材料和网格
第二种常见模式是向场景添加对象:
- 创建几何
- 创建材质
- 创建网格
- 将网格添加到场景中。
在 Three.js 中,网格是几何体与材质的组合。
几何是对象的数学公式,在 Three.js 中有很多几何,将在以后的章节中探索它,几何体提供了要添加到场景中的对象的顶点。
材质可以定义为对象的属性及其与场景光源的行为。如下图所示,有不同类型的材料。
现在知道了网格、几何体和材质是什么,将把它们添加到场景中。在示例中,使用基本材质创建一个立方体:
// 创建一个立方体网格
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: "#1b55e3" });
const cube = new THREE.Mesh(geometry, material);
// 将立方体到场景中
scene.add(cube);
请求动画帧
最后一段代码是为场景设置动画,使用 requestAnimationFrame,它允许有一个以每秒 60 帧(最多)运行的函数。
const render = () => {
requestAnimationFrame(render);
renderer.render(scene, camera);
};
render();
立方体动画
为了在渲染循环中为立方体设置动画,需要更改它的属性。当创建一个网格时,可以访问一组在动画制作时非常有用的属性。
// Rotation (XYZ) 弧度
cube.rotation.x;
cube.rotation.y;
cube.rotation.z;
// Position (XYZ)
cube.position.x;
cube.position.y;
cube.position.z;
// Scale (XYZ)
cube.scale.x;
cube.scale.y;
cube.scale.z;
在示例中,为立方体设置 X 和 Y 旋转动画:
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
控制台
作为前端,控制台是最好的调试工具。当使用 Three.js 时,控制台是必不可少的工具。
效果增持
现在理解了示例的逻辑,将向场景中添加更多片段,目的是生成更复杂的片段。
/**
* 创建材质的方法
*/
const createMesh = (boxOptions, meshOptions) => {
const geometry = new THREE.BoxGeometry(...boxOptions);
const material = new THREE.MeshBasicMaterial(meshOptions);
return new THREE.Mesh(geometry, material);
};
const cube01 = createMesh([1, 1, 1], {
color: "#A49FEF",
wireframe: true,
transparent: true,
});
scene.add(cube01);
const cube01_wireframe = createMesh([3, 3, 3], {
color: "#433F81",
wireframe: true,
transparent: true,
});
scene.add(cube01_wireframe);
const cube02 = createMesh([1, 1, 1], {
color: "#A49FEF",
});
scene.add(cube02);
const cube02_wireframe = createMesh([3, 3, 3], {
color: "#433F81",
wireframe: true,
transparent: true,
});
scene.add(cube02_wireframe);
const bar01 = createMesh([10, 0.05, 0.5], {
color: "#00FFBC",
});
bar01.position.z = 0.5;
scene.add(bar01);
const bar02 = createMesh([10, 0.05, 0.5], {
color: "#ffffff",
});
bar02.position.z = 0.5;
scene.add(bar02);
const render = () => {
requestAnimationFrame(render);
cube01.rotation.x += 0.01;
cube01.rotation.y += 0.01;
cube01_wireframe.rotation.x += 0.01;
cube01_wireframe.rotation.y += 0.01;
cube02.rotation.x -= 0.01;
cube02.rotation.y -= 0.01;
cube02_wireframe.rotation.x -= 0.01;
cube02_wireframe.rotation.y -= 0.01;
bar01.rotation.z -= 0.01;
bar02.rotation.z += 0.01;
renderer.render(scene, camera);
};
render();
运行后效果如图: