当视频游戏遇到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();
运行后效果如图: