3D 渲染引擎
3D 渲染引擎是一种软件工具,可以将 3D 模型转化为 2D 图像或视频,并提供各种渲染效果和交互功能。在前端技术中常用到的 3D 渲染引擎技术有:
| 名称 | 介绍 | 文档地址 |
|---|---|---|
| WebGL | 一种基于 OpenGL ES 的 JavaScript API,可以在 Web 浏览器中实现高性能的 3D 渲染,可以直接操作 GPU,实现高效的渲染和交互。它的主要优势在于可以在不需要额外插件的情况下在 Web 端实现高质量的 3D 渲染 | https://www.khronos.org/webgl/ |
| Three.js | 一个基于 WebGL 的 3D 渲染引擎,提供了丰富的渲染和交互功能,可以快速地创建复杂的 3D 场景 | https://threejs.org/docs/ |
| Babylon.js | 一个基于 WebGL 的 3D 渲染引擎,提供了丰富的渲染和交互功能,可以快速地创建复杂的 3D 场景,支持多种 3D 模型格式,包括 OBJ、FBX、Collada 等 | https://doc.babylonjs.com/ |
| A-Frame | 一个基于 WebVR 的 3D 渲染引擎,提供了简单易用的 API 和组件,可以快速地创建 VR 和 AR 场景,支持多种 VR 头显设备,包括 Oculus Rift、HTC Vive 等 | https://pixijs.com/docs/ |
Three.js
介绍
Three.js 是一款基于 WebGL 的 JavaScript 3D 渲染引擎,它提供了一系列的 API,可以用于创建各种复杂的 3D 场景和交互效果。Three.js 的主要特点包括:
:tada:高性能:Three.js 可以直接操作 GPU,实现高效的渲染和交互效果。同时,它还提供了一系列的优化技术,可以让开发者更好地掌控性能。
:tada:简单易用:Three.js 提供了简单易用的 API 和组件,可以帮助开发者快速创建各种 3D 场景和效果。
:tada:多平台支持:Three.js 可以在多种平台上运行,包括 Web、移动端、桌面端等。
:tada:社区支持:Three.js 拥有庞大的开发者社区,提供了大量的示例和文档,方便开发者学习和使用。
渲染 3D 场景
主要需要了解三个基本概念
- 场景 Scene
- 相机 Camera
- 渲染器 Renderer
创建场景
你可以把三维场景 Scene 对象理解为虚拟的3D场景,用来表示模拟生活中的真实三维场景,或者说三维世界。
// 创建3D场景对象Scene
const scene = new THREE.Scene();
导入模型
// 模型加载,设置材质
const hdrLoader = new RGBELoader();
hdrLoader.loadAsync("./textures/023.hdr").then((texture) => {
scene.background = texture;
scene.environment = texture;
scene.environment.mapping = THREE.EquirectangularReflectionMapping;
});
设置光源
// 添加平行光
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 100, 10); // 光源位置
scene.add(light); // 场景添加光源
摄像机
Threejs 提供了正投影相机 OrthographicCamera 和透视投影相机 PerspectiveCamera。透视投影相机 PerspectiveCamera 本质上就是在模拟人眼观察这个世界的规律。
// 实例化一个透视投影相机对象
const camera = new THREE.PerspectiveCamera();
相机位置 position
生活中用相机拍照,你相机位置不同,拍照结果也不同,threejs 中虚拟相机同样如此。
比如有一间房子,你拿着相机站在房间里面,看到的是房间内部,站在房子外面看到的是房子外面效果。
相机对象 Camera 具有位置属性 position ,通过位置属性 position 可以设置相机的位置。
//相机在Three.js三维坐标系中的位置
// 根据需要设置相机位置具体值
camera.position.set(200, 200, 200);
相机观察目标 lookAt()
你用相机拍照你需要控制相机的拍照目标,具体说相机镜头对准哪个物体或说哪个坐标。对于 threejs 相机而言,就是设置 lookAt() 方法的参数,指定一个 3D 坐标。
//相机观察目标指向Threejs 3D空间中某个位置
camera.lookAt(0, 0, 0); //坐标原点
camera.lookAt(0, 10, 0); //y轴上位置10
camera.lookAt(mesh.position); //指向mesh对应的位置
判断相机相对三维场景中长方体位置
你可以把三维场景中长方体 mesh 想象为一个房间,然后根据相机位置和长方体位置尺寸对比,判断两者相对位置。你可以发现设置相机坐标(200, 200, 200),位于长方体外面一处位置。
// 长方体尺寸100, 100, 100
const geometry = new THREE.BoxGeometry(100, 100, 100);
const mesh = new THREE.Mesh(geometry, material);
// 相机位置xyz坐标:0,10,0
mesh.position.set(0, 10, 0);
// 相机位置xyz坐标:200, 200, 200
camera.position.set(200, 200, 200);
透视投影相机 PerspectiveCamera :视锥体
透视投影相机的四个参数 fov, aspect, near, far 构成一个四棱台3D 空间,被称为视锥体,只有视锥体之内的物体,才会渲染出来,视锥体范围之外的物体不会显示在 Canvas 画布上。
// width和height用来设置Three.js输出的Canvas画布尺寸(像素px)
const width = 800; //宽度
const height = 500; //高度
// 30:视场角度, width / height:Canvas画布宽高比, 1:近裁截面, 3000:远裁截面
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
PerspectiveCamera 参数介绍:
| 参数 | 含义 | 默认值 |
|---|---|---|
| fov | 相机视锥体竖直方向视野角度 | 50 |
| aspect | 相机视锥体水平方向和竖直方向长度比,一般设置为 Canvas 画布宽高比 width / height | 1 |
| near | 相机视锥体近裁截面相对相机距离 | 0.1 |
| far | 相机视锥体远裁截面相对相机距离,far-near 构成了视锥体高度方向 | 2000 |
渲染器
生活中如果有了景物和相机,那么如果想获得一张照片,就需要你拿着相机,按一下,咔,完成拍照。对于threejs而言,如果完成“咔”这个拍照动作,就需要一个新的对象,也就是WebGL渲染器 WebGLRenderer。
WebGL渲染器 WebGLRenderer
通过WebGL渲染器 WebGLRenderer 可以实例化一个WebGL渲染器对象。
// 创建渲染器对象
const renderer = new THREE.WebGLRenderer();
设置Canvas画布尺寸 setSize()
// 定义threejs输出画布的尺寸(单位:像素px)
const width = 800; //宽度
const height = 500; //高度
renderer.setSize(width, height); //设置three.js渲染区域的尺寸(像素px)
渲染器渲染方法 render()
渲染器 WebGLRenderer 执行渲染方法 render() 就可以生成一个Canvas画布(照片),并把三维场景Scene呈现在canvas画布上面,你可以把 render() 理解为相机的拍照动作“咔”。
renderer.render(scene, camera); //执行渲染操作
渲染器Canvas画布属性 domElement
渲染器 WebGLRenderer 通过属性 domElement 可以获得渲染方法 render() 生成的Canvas画布, domElement 本质上就是一个HTML元素:Canvas画布。
document.body.appendChild(renderer.domElement);
Canvas画布插入到任意HTML元素中
<div id="webgl" style="margin-top: 200px;margin-left: 100px;"></div>
document.getElementById('webgl').appendChild(renderer.domElement);
给模型添加点击事件
在 Three.js 中给模型添加点击事件,可以使用 Raycaster 和 addEventListener 方法来实现。
射线拾取网格模型步骤
-
1.坐标转化(鼠标单击的屏幕坐标转标准设备坐标)
-
2.射线计算(通过鼠标单击位置+相机参数计算射线值)
-
3.射线交叉计算
// 创建 Raycaster 对象
const raycaster = new THREE.Raycaster();
// 监听鼠标点击事件,并获取鼠标点击位置
window.addEventListener('click', onClick, false);
// 获取鼠标点击位置
const mouse = new THREE.Vector2();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 将鼠标点击位置转换为 Three.js 二维坐标
raycaster.setFromCamera(mouse, camera);
// 检测是否有物体与射线相交
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
// 获取与射线相交的第一个物体
const object = intersects[0].object;
// 给该物体添加点击事件处理函数
object.onClick();
}
需要注意的是,使用 Raycaster 对象需要根据相机的位置和方向进行更新,因此需要在每一帧更新一次 Raycaster 对象。另外,在实际应用中,我们可能需要对不同的模型绑定不同的点击事件处理函数,可以通过给模型添加自定义属性的方式来实现。