2024-09-03
前端
00

目录

结构介绍
元素讲解
Scene
Camera
相机类型
参数解析
Geometry, Material, Mesh
几何 Geometry
材质 Material
网格 Mesh
Light
Renderer
总结

ThreeJS是一个基于WebGL的图形渲染框架,封装了诸如场景、灯光、阴影、材质、贴图、空间运算等一系列功能,让你不必要再从底层WebGL开始写起。

结构介绍

image.png

一个ThreeJS的工作流程相当是这样的:

  • 创建场景Scene,相机Camera,以及里面的各种元素
  • 渲染器Render负责进行渲染,并对接下来的帧的内容进行监控处理

元素讲解

在使用ThreeJS之前,我们需要进行导入。对于React项目来说,通常我们将3D内容写进组件中进行封装,并交给上层父组件调用。

组件的书写结构如下:

JS
// 导入threejs import * as THREE from "three"; // 在react中我们使用useEffect调用threejs import {useEffect} from "react"; function SampleComponent() { useEffect(() => { // 这里书写threejs主体内容 return () => { // 这里一般用于在结束时删除组件防止内存泄露 } }, []); return null } export default SampleComponent;

在使用 React 编写 Three.js 应用时,将代码写在 useEffect 里,并返回 null,是一种常见的模式。采用这种形式的原因如下:

  1. 副作用管理useEffect 是 React 用来管理副作用的钩子函数。Three.js 场景的创建和渲染属于副作用,因为它们直接操作 DOM 或者 Canvas,不属于 React 的渲染流程。因此,将这些逻辑放在 useEffect 里可以确保它们在组件挂载或更新时正确执行。

  2. 只在组件挂载时执行:通过指定依赖数组(如 []),可以让 useEffect 只在组件挂载时执行一次。这样可以防止 Three.js 场景的重复创建,提升性能。

  3. 返回 null 只是不渲染 DOM 元素:在 React 组件中返回 null 意味着这个组件不渲染任何 DOM 元素。但在 useEffect 里执行的 Three.js 逻辑仍然会创建并渲染到指定的 Canvas 上。这种方式实际上将 Three.js 的渲染与 React 的 DOM 渲染分离开来,避免 React 重新渲染时不必要的 DOM 操作干扰 Three.js 的渲染。

  4. 清理资源useEffect 可以返回一个清理函数,当组件卸载或依赖项改变时执行。这样可以确保 Three.js 场景的资源(比如几何体、材质等)能够在组件销毁时正确释放,避免内存泄漏。

将 Three.js 的代码放在 useEffect 里,并返回 null,可以让你更好地控制 Three.js 的生命周期管理,同时与 React 的组件渲染逻辑保持分离。这样不仅能确保性能优化,还能避免不必要的资源浪费。

Scene

与Unity类似, 场景Scene代表了一个视图,它是所有需要显示的元素的集合。它往往在最开始声明,声明之后只需要通过add向其中增加希望显示的东西即可。

JS
// 声明 const scene = new THREE.Scene(); // 添加元素 scene.add(sampleElement);

Camera

同样与Unity类似,相机Camera代表了显示元素的视角,它能够按需变换,以达到不同的目的。声明后,通常只需要通过改变它的位置、角度等,也可以通过轨道进行自由变换。

JS
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000, )

相机类型

相机的常用类型有以下几种:

  • 透视相机 (PerspectiveCamera):使用透视投影的相机,类似于人眼的视觉效果。远处的物体看起来较小,而近处的物体看起来较大。常用于需要现实感的三维场景中。
  • 正交相机 (OrthographicCamera):使用正交投影的相机,物体不会因距离而缩小。常用于二维场景或需要精确控制物体位置的三维场景中。
  • 立方相机 (CubeCamera):用于创建环境贴图或反射效果。它会从场景的六个方向拍摄图像,并将它们存储为立方体贴图。常用于反射和折射的效果,如镜面和水面。
  • 立体相机 (StereoCamera):用于创建立体效果(3D视觉),适合VR或AR应用。立体相机会渲染左右两个视图,提供深度感。

参数解析

我们在这里只创建最简单的透视相机。透视相机接受四个参数,分别是:

  1. fov: 相机视角,视角越大范围越广
  2. aspect: 图像宽高比,实例声明为窗口宽高比
  3. near: 近裁剪面,与相机的距离小于这个范围的元素将不会被渲染
  4. far: 远裁剪面,与相机的距离大于这个范围的元素将不会被渲染

Geometry, Material, Mesh

物体三巨头。这三个元素构成了渲染一个显示物体的关键。

几何 Geometry

以面向对象的思想来说,几何相当于一个抽象出来的类。它代表了一种形状,而不代表一个具体物体。它的声明如下所示:

JS
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);

在这里,我们取了一个 BoxGeometry,大小为 (1, 1, 1),并将这个大小的盒子形状命名为 cubeGeometry。在这里,我们并没有完成一个具体的物体的创建,而是 创建了一个特定大小、特定形状的模板。它需要经过处理后才能表示一个具体的物体。

材质 Material

材质可以理解为具体物体的表面的样式,它决定了这个物体的外观。它的声明如下所示:

JS
const cubeMaterial = new THREE.MeshBasicMaterial({color: 0xffff00});

在这里,我们使用了基本的材质,并把颜色定为 0xffff00

网格 Mesh

虽然它被称为网格,但它才指代一个具体的物体。具体来说,它会使用Mesh方法结合几何与材质,并将其统一为一个Mesh对象,这个对象表示一个在视图中具体的物体。最后,它需要被添加进scene,从而显示出来。

JS
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial); scene.add(cube);

至此,一个颜色为0xffff00,形状为BoxGeometry,大小为(1, 1, 1)的物体cube就被创建完毕了。

Light

光照能够让几何体显现明暗关系,进而出现能够让人脑接受的3D效果。Light有很多类型,这里先使用一束平行光。

JS
const color = 0xFFFFFF; const intensity = 1; const light = new THREE.DirectionalLight(color, intensity); light.position.set(-1, 2, 4); scene.add(light);

Renderer

那么,我们场景已经做好了,物体已经添加了,摄像机已经就位了,接下来就要把三维空间里的所有可视元素渲染到二维平面的网页中,这就是渲染器Renderer的作用。它的声明如下:

JS
const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement);

第一行声明了一个基于WebGL渲染器的渲染器实例,第二行则是规定了这个渲染器的尺寸,也就是画布的范围。

第三行将这个渲染器的domElement直接放在了HTML文件的body下充当子元素。这样,浏览器就会显示这个渲染好的结果。

同时,我们需要声明一个 render() 函数:

JS
function render() { renderer.render(scene, camera) requestAnimationFrame(render) } render()

这个函数用于执行渲染操作。

其中,renderer.render(scene, camera) 是核心,它将指定的场景通过指定的相机渲染出来。

requestAnimationFrame 是一个浏览器提供的API,用于高效地循环动画。

调用 requestAnimationFrame(render) 会在下一次重绘时调用 render 函数,实现每帧都进行渲染。这种方式比使用 setTimeoutsetInterval 更加节能且平滑,因为它能够使浏览器根据显示器的刷新率来优化渲染。

最后一行 render(),是第一次调用,它启动了渲染循环,使得 requestAnimationFrame 开始调用 render,进而实现持续的动画效果。

总结

那么,回过头来看这个结构图:

image.png

这个结构图代表了:使用WebGL渲染器渲染一个透视相机,内容是一个场景,内含一个平行光源,以及三个立方体网格对象。这三个对象使用了同一个BoxGeometry形状,但是使用了三种不同的材质。

本文作者:Jeff Wu

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!