第一章 基础知识介绍
1.1 Three.js概述
1.1.1 什么是Three.js
Three.js 是一个基于 JavaScript 的 3D 库,它就像是一个神奇的魔法盒子,能让开发者在网页上轻松创建和展示 3D 场景😎。它对 WebGL(一种用于在网页上绘制 3D 图形的 JavaScript API)进行了封装,使得开发者无需深入了解复杂的 WebGL 底层知识,就可以利用 Three.js 提供的简洁易用的 API 来创建各种 3D 效果,比如 3D 模型展示、3D 游戏、虚拟场景漫游等。
1.1.2 Three.js的应用场景
- 电商领域:在电商网站中,使用 Three.js 可以实现商品的 3D 展示,让用户可以从不同角度观察商品,增强购物体验🛍️。例如,展示一款手表,用户可以旋转、缩放手表,查看手表的细节。
- 教育领域:用于创建 3D 教学模型,帮助学生更好地理解抽象的概念。比如在物理课上,展示分子的结构;在地理课上,展示地球的地貌等🌍。
- 游戏开发:开发一些简单的 3D 网页游戏,无需用户下载安装,直接在网页上就可以玩🎮。
- 建筑设计:建筑师可以使用 Three.js 创建建筑的 3D 模型,进行虚拟的建筑漫游,提前展示建筑的效果和空间布局🏠。
- 艺术与创意:艺术家可以利用 Three.js 创造出独特的 3D 艺术作品,通过网页展示给更多人欣赏🎨。
1.1.3 Three.js的基本原理
Three.js 的基本原理可以简单概括为以下几个步骤:
- 场景(Scene):就像是一个舞台,所有的 3D 对象都将在这个舞台上进行展示。开发者需要创建一个场景对象,作为所有 3D 元素的容器🎭。
- 相机(Camera):相机决定了我们从哪个角度观察场景,就像摄影师选择拍摄角度一样📷。Three.js 提供了多种类型的相机,如透视相机(PerspectiveCamera)和正交相机(OrthographicCamera)。
- 几何体(Geometry):几何体定义了 3D 对象的形状,比如立方体、球体、圆柱体等。开发者可以根据需要创建不同的几何体🧊。
- 材质(Material):材质决定了 3D 对象的外观,如颜色、光泽、透明度等。不同的材质可以让同一个几何体呈现出不同的效果,比如金属材质、木质材质等🎨。
- 网格(Mesh):将几何体和材质组合在一起,就形成了一个网格对象,它代表了一个具体的 3D 对象。然后将网格对象添加到场景中🟩。
- 渲染器(Renderer):渲染器的作用是将场景和相机的信息进行处理,最终在网页上绘制出 3D 场景。Three.js 支持多种渲染方式,如 WebGL 渲染器、Canvas 渲染器等🖌️。
1.2 @react-three/fiber概述
1.2.1 @react-three/fiber简介
@react-three/fiber 是一个用于 React 的 3D 渲染库,它将 React 的声明式编程风格引入到 Three.js 中。简单来说,它允许开发者使用 React 的组件化思想来创建和管理 Three.js 场景,让开发 3D 应用变得更加高效和直观👏。
1.2.2 @react-three/fiber与Three.js的关系
@react-three/fiber 是建立在 Three.js 之上的,它并没有替代 Three.js,而是对 Three.js 进行了封装和扩展。@react-three/fiber 利用 React 的特性,将 Three.js 的各种对象(如场景、相机、几何体等)封装成 React 组件,使得开发者可以像使用普通 React 组件一样使用 Three.js 的功能。例如,开发者可以使用 JSX 语法来创建和组合 3D 组件,而不是像传统的 Three.js 那样使用命令式的代码来操作对象。
1.2.3 @react-three/fiber的优势
- 声明式编程:使用 React 的声明式语法,让代码更加简洁和易于理解。开发者只需要描述 3D 场景的最终状态,而不需要手动管理对象的创建和更新过程😃。
- 组件化开发:可以将复杂的 3D 场景拆分成多个小的组件,提高代码的可维护性和复用性。例如,将一个 3D 模型封装成一个组件,在不同的场景中重复使用🧩。
- 与 React 生态集成:可以方便地与其他 React 库和工具集成,如状态管理库(Redux、MobX)、路由库(React Router)等,增强应用的功能和扩展性🚀。
- 响应式设计:利用 React 的状态管理机制,实现 3D 场景的响应式设计。当状态发生变化时,3D 场景可以自动更新,提供更加流畅的用户体验🔄。
1.3 @react-three/drei概述
1.3.1 @react-three/drei简介
@react-three/drei 是一个基于 @react-three/fiber 的辅助库,它提供了一系列预构建的组件和工具,帮助开发者更快速地创建 3D 场景。它就像是一个 3D 开发的工具箱,里面有很多实用的工具和组件,可以节省开发者的开发时间和精力🧰。
1.3.2 @react-three/drei的功能特点
- 丰富的组件:提供了各种常用的 3D 组件,如灯光组件、控制器组件、模型加载组件等。例如,使用
OrbitControls
组件可以轻松实现 3D 场景的鼠标交互,让用户可以旋转、缩放和平移场景🖱️。 - 简化开发流程:通过封装一些复杂的操作,简化了 3D 开发的流程。比如,使用
GLTFLoader
组件可以直接加载 GLTF 格式的 3D 模型,而不需要手动处理模型加载的细节📦。 - 动画支持:提供了一些动画相关的组件和工具,方便开发者创建 3D 动画。例如,使用
useFrame
钩子可以实现逐帧动画,让 3D 对象动起来🎞️。
1.3.3 @react-three/drei与@react-three/fiber的协同作用
@react-three/drei 与 @react-three/fiber 是紧密配合的关系。@react-three/fiber 提供了使用 React 开发 3D 场景的基础架构,而 @react-three/drei 则在这个基础上提供了更多的实用组件和工具。开发者可以在 @react-three/fiber 创建的 3D 场景中,直接使用 @react-three/drei 提供的组件,快速搭建出功能丰富的 3D 应用。例如,在 @react-three/fiber 创建的场景中,使用 @react-three/drei 的 Environment
组件来添加环境光和反射效果,让场景更加逼真🌇。
第二章 环境搭建与配置
2.1 创建 React 项目
2.1.1 使用 Create React App 创建项目
Create React App 是一个官方提供的脚手架工具,它可以帮助我们快速搭建一个基础的 React 项目,无需手动配置复杂的构建工具。以下是详细步骤:
- 安装 Node.js 和 npm:Create React App 依赖 Node.js 和 npm,所以首先要确保你的电脑上已经安装了它们。你可以在Node.js 官网下载并安装适合你操作系统的版本。安装完成后,在终端输入以下命令检查是否安装成功:
node -v
npm -v
如果能正常显示版本号,说明安装成功😎。
- 使用 Create React App 创建项目:打开终端,输入以下命令创建一个名为
my-react-app
的项目:
npx create-react-app my-react-app
这里使用 npx
是因为它可以直接执行 create-react-app
而无需全局安装。命令执行完成后,会在当前目录下创建一个名为 my-react-app
的文件夹,里面包含了一个基础的 React 项目结构。
- 进入项目目录并启动项目:
cd my-react-app
npm start
执行 npm start
后,项目会自动在浏览器中打开,你会看到一个欢迎页面🎉。
2.1.2 使用 Vite 创建项目
Vite 是一个轻量级的构建工具,它的启动速度非常快,适合快速开发。以下是使用 Vite 创建 React 项目的步骤:
安装 Node.js 和 npm:同样,要先确保已经安装了 Node.js 和 npm,检查方法同上。
使用 Vite 创建项目:在终端输入以下命令创建一个名为
my-vite-react-app
的项目:
npm init vite@latest my-vite-react-app -- --template react
这个命令会使用 Vite 的 React 模板创建项目。
- 进入项目目录并安装依赖:
cd my-vite-react-app
npm install
- 启动项目:
npm run dev
启动后,在浏览器中访问相应的地址,就可以看到项目运行起来啦😃。
2.2 安装依赖
2.2.1 安装 Three.js
Three.js 是一个用于在网页上创建和展示 3D 图形的 JavaScript 库。在项目中安装 Three.js 可以使用以下命令:
npm install three
安装完成后,你就可以在项目中引入并使用 Three.js 来创建 3D 场景啦🤩。
2.2.2 安装 @react-three/fiber
@react-three/fiber 是一个用于在 React 中使用 Three.js 的库,它提供了一种更符合 React 风格的方式来创建 3D 场景。安装命令如下:
npm install @react-three/fiber
有了它,你可以像使用 React 组件一样创建和管理 3D 对象😎。
2.2.3 安装 @react-three/drei
@react-three/drei 是一个基于 @react-three/fiber 的辅助库,它提供了许多常用的 3D 组件和工具,能帮助我们更方便地创建 3D 场景。安装命令如下:
npm install @react-three/drei
安装后,你可以使用它提供的组件快速搭建复杂的 3D 场景👏。
2.3 项目配置
2.3.1 配置 Webpack(如果需要)
Webpack 是一个强大的模块打包工具,虽然 Create React App 和 Vite 已经有默认的打包配置,但在某些情况下,你可能需要自定义 Webpack 配置。
- 使用 Create React App 时:Create React App 默认隐藏了 Webpack 配置文件,如果你需要自定义配置,可以使用
npm run eject
命令将配置文件暴露出来。但要注意,这个操作是不可逆的,执行前请做好备份。
npm run eject
暴露后,你可以在 config
文件夹中找到 Webpack 相关的配置文件,然后根据自己的需求进行修改。
- 使用 Vite 时:Vite 默认使用 Rollup 作为打包工具,但也可以集成 Webpack。你可以通过安装
@vitejs/plugin-webpack
插件来实现。安装命令如下:
npm install @vitejs/plugin-webpack
然后在 vite.config.js
中配置插件:
import { defineConfig } from 'vite';
import webpackPlugin from '@vitejs/plugin-webpack';
export default defineConfig({
plugins: [webpackPlugin()],
});
2.3.2 配置 TypeScript(可选)
TypeScript 是 JavaScript 的超集,它可以为 JavaScript 代码添加静态类型检查,提高代码的可维护性和健壮性。
- 使用 Create React App 创建的项目:如果你使用 Create React App 创建项目时想使用 TypeScript,可以使用以下命令创建一个 TypeScript 版本的项目:
npx create-react-app my-react-app --template typescript
如果项目已经创建好,也可以通过以下命令将其转换为 TypeScript 项目:
npm install --save typescript @types/react @types/react-dom @types/node
然后将 .js
文件改为 .ts
或 .tsx
文件即可。
- 使用 Vite 创建的项目:使用 Vite 创建项目时可以直接选择 TypeScript 模板:
npm init vite@latest my-vite-react-app -- --template react-ts
如果项目已经创建好,同样可以通过安装相关依赖来支持 TypeScript:
npm install typescript @types/react @types/react-dom
然后创建 tsconfig.json
文件并进行相应配置:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"lib": ["dom", "dom.iterable", "esnext"],
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
通过以上步骤,你就可以完成项目的环境搭建和配置啦🥳!
第三章 @react-three/fiber的使用
3.1 基本场景搭建
3.1.1 创建场景(Scene)
在使用 @react-three/fiber
时,场景(Scene)就像是一个大舞台,所有的 3D 对象都会在这个舞台上展示。在 @react-three/fiber
里创建场景非常简单,只需要使用 <Canvas>
组件,它会自动为我们创建一个场景。
import { Canvas } from '@react-three/fiber';
function App() {
return (
<Canvas>
{/* 这里可以添加其他 3D 对象 */}
</Canvas>
);
}
export default App;
<Canvas>
组件是 @react-three/fiber
的核心组件,它会处理很多底层的工作,比如创建 WebGL 渲染器、场景、相机等。😎
3.1.2 创建相机(Camera)
相机就像是我们观看 3D 场景的眼睛,它决定了我们从哪个角度、以什么样的视野来观察场景。在 @react-three/fiber
中,默认会有一个相机,但我们也可以自定义相机。
import { Canvas, PerspectiveCamera } from '@react-three/fiber';
function App() {
return (
<Canvas>
<PerspectiveCamera position={[0, 0, 5]} />
{/* 这里可以添加其他 3D 对象 */}
</Canvas>
);
}
export default App;
上面的代码中,我们使用了 <PerspectiveCamera>
组件来创建一个透视相机,并通过 position
属性设置了相机的位置。透视相机可以模拟人眼的视觉效果,有近大远小的效果。🤩
3.1.3 创建渲染器(Renderer)
渲染器负责将 3D 场景渲染成 2D 图像显示在屏幕上。在 @react-three/fiber
中,<Canvas>
组件已经帮我们创建好了渲染器,我们一般不需要手动创建。但我们可以通过一些属性来配置渲染器。
import { Canvas } from '@react-three/fiber';
function App() {
return (
<Canvas
// 设置渲染器的抗锯齿效果
dpr={[1, 2]}
// 设置渲染器的背景颜色
gl={{ antialias: true, alpha: false }}
camera={{ position: [0, 0, 5] }}
>
{/* 这里可以添加其他 3D 对象 */}
</Canvas>
);
}
export default App;
在上面的代码中,我们通过 dpr
属性设置了设备像素比,通过 gl
属性配置了渲染器的一些参数,比如抗锯齿和是否支持透明度。😜
3.1.4 在 React 组件中渲染场景
现在我们已经创建好了场景、相机和渲染器,接下来就可以在 React 组件中渲染 3D 场景了。我们可以在 <Canvas>
组件内部添加各种 3D 对象。
import { Canvas, Box } from '@react-three/fiber';
function App() {
return (
<Canvas>
<Box position={[0, 0, 0]} args={[1, 1, 1]} />
</Canvas>
);
}
export default App;
上面的代码中,我们在 <Canvas>
组件内部添加了一个 <Box>
组件,它代表一个立方体。通过 position
属性设置了立方体的位置,通过 args
属性设置了立方体的尺寸。🎉
3.2 几何体与材质
3.2.1 常见几何体的使用(如立方体、球体等)
在 3D 场景中,几何体定义了物体的形状。@react-three/fiber
提供了很多常见的几何体组件。
立方体(Box)
import { Canvas, Box } from '@react-three/fiber';
function App() {
return (
<Canvas>
<Box position={[0, 0, 0]} args={[1, 1, 1]} />
</Canvas>
);
}
export default App;
球体(Sphere)
import { Canvas, Sphere } from '@react-three/fiber';
function App() {
return (
<Canvas>
<Sphere position={[2, 0, 0]} args={[1, 32, 32]} />
</Canvas>
);
}
export default App;
上面的代码中,我们分别使用了 <Box>
和 <Sphere>
组件来创建立方体和球体。args
属性用于设置几何体的参数,不同的几何体参数不同。😃
3.2.2 基本材质的应用(如 MeshBasicMaterial、MeshStandardMaterial 等)
材质决定了几何体的外观,比如颜色、光泽等。@react-three/fiber
提供了很多基本的材质组件。
MeshBasicMaterial
MeshBasicMaterial
是一种简单的材质,它不受光照影响,始终显示为固定的颜色。
import { Canvas, Box, MeshBasicMaterial } from '@react-three/fiber';
function App() {
return (
<Canvas>
<Box position={[0, 0, 0]} args={[1, 1, 1]}>
<MeshBasicMaterial color="red" />
</Box>
</Canvas>
);
}
export default App;
MeshStandardMaterial
MeshStandardMaterial
是一种更高级的材质,它受光照影响,有更真实的光照效果。
import { Canvas, Box, MeshStandardMaterial } from '@react-three/fiber';
function App() {
return (
<Canvas>
<Box position={[0, 0, 0]} args={[1, 1, 1]}>
<MeshStandardMaterial color="blue" />
</Box>
</Canvas>
);
}
export default App;
上面的代码中,我们分别使用了 <MeshBasicMaterial>
和 <MeshStandardMaterial>
组件来为立方体添加材质。🌈
3.2.3 材质的属性配置
材质有很多属性可以配置,比如颜色、透明度、金属度等。
import { Canvas, Box, MeshStandardMaterial } from '@react-three/fiber';
function App() {
return (
<Canvas>
<Box position={[0, 0, 0]} args={[1, 1, 1]}>
<MeshStandardMaterial
color="green"
transparent={true}
opacity={0.5}
metalness={0.8}
roughness={0.2}
/>
</Box>
</Canvas>
);
}
export default App;
在上面的代码中,我们通过 color
属性设置了材质的颜色,通过 transparent
和 opacity
属性设置了材质的透明度,通过 metalness
和 roughness
属性设置了材质的金属度和粗糙度。🤓
3.3 光照与阴影
3.3.1 不同类型光照的使用(如环境光、平行光、点光源等)
光照在 3D 场景中非常重要,它可以营造出不同的氛围和效果。@react-three/fiber
提供了多种类型的光照组件。
环境光(AmbientLight)
环境光会均匀地照亮整个场景,没有特定的方向。
import { Canvas, Box, MeshStandardMaterial, AmbientLight } from '@react-three/fiber';
function App() {
return (
<Canvas>
<AmbientLight intensity={0.5} />
<Box position={[0, 0, 0]} args={[1, 1, 1]}>
<MeshStandardMaterial color="yellow" />
</Box>
</Canvas>
);
}
export default App;
平行光(DirectionalLight)
平行光就像太阳光,所有的光线都是平行的。
import { Canvas, Box, MeshStandardMaterial, DirectionalLight } from '@react-three/fiber';
function App() {
return (
<Canvas>
<DirectionalLight position={[1, 1, 1]} intensity={0.8} />
<Box position={[0, 0, 0]} args={[1, 1, 1]}>
<MeshStandardMaterial color="purple" />
</Box>
</Canvas>
);
}
export default App;
点光源(PointLight)
点光源就像灯泡,光线从一个点向四面八方发射。
import { Canvas, Box, MeshStandardMaterial, PointLight } from '@react-three/fiber';
function App() {
return (
<Canvas>
<PointLight position={[0, 2, 0]} intensity={1} />
<Box position={[0, 0, 0]} args={[1, 1, 1]}>
<MeshStandardMaterial color="orange" />
</Box>
</Canvas>
);
}
export default App;
上面的代码中,我们分别使用了 <AmbientLight>
、<DirectionalLight>
和 <PointLight>
组件来添加不同类型的光照。🌟
3.3.2 光照的参数设置
光照有很多参数可以设置,比如强度、颜色等。
import { Canvas, Box, MeshStandardMaterial, DirectionalLight } from '@react-three/fiber';
function App() {
return (
<Canvas>
<DirectionalLight
position={[1, 1, 1]}
intensity={0.8}
color="white"
castShadow={true}
/>
<Box position={[0, 0, 0]} args={[1, 1, 1]}>
<MeshStandardMaterial color="pink" />
</Box>
</Canvas>
);
}
export default App;
在上面的代码中,我们通过 intensity
属性设置了光照的强度,通过 color
属性设置了光照的颜色,通过 castShadow
属性设置了光照是否可以投射阴影。😎
3.3.3 阴影的开启与配置
要在 3D 场景中显示阴影,需要进行一些配置。
import { Canvas, Box, MeshStandardMaterial, DirectionalLight, Plane } from '@react-three/fiber';
function App() {
return (
<Canvas
shadows
camera={{ position: [0, 2, 5], fov: 50 }}
>
<DirectionalLight
position={[1, 2, 4]}
intensity={0.5}
castShadow
shadow-mapSize-width={1024}
shadow-mapSize-height={1024}
/>
<Box position={[0, 1, 0]} args={[1, 1, 1]} castShadow receiveShadow>
<MeshStandardMaterial color="cyan" />
</Box>
<Plane rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.5, 0]} args={[10, 10]} receiveShadow>
<MeshStandardMaterial color="gray" />
</Plane>
</Canvas>
);
}
export default App;
在上面的代码中,我们首先在 <Canvas>
组件中设置了 shadows
属性为 true
来开启阴影功能。然后在光照组件中设置 castShadow
属性为 true
表示光照可以投射阴影,在物体组件中设置 castShadow
和 receiveShadow
属性为 true
分别表示物体可以投射阴影和接收阴影。同时,我们还可以通过 shadow-mapSize-width
和 shadow-mapSize-height
属性来设置阴影贴图的大小。🌙
3.4 动画与交互
3.4.1 使用 useFrame 钩子实现动画
useFrame
是 @react-three/fiber
提供的一个钩子,它可以在每一帧渲染之前执行一些代码,从而实现动画效果。
import { Canvas, Box, MeshStandardMaterial, useFrame } from '@react-three/fiber';
import { useRef } from 'react';
function RotatingBox() {
const mesh = useRef();
useFrame(() => {
mesh.current.rotation.x += 0.01;
mesh.current.rotation.y += 0.01;
});
return (
<Box ref={mesh} position={[0, 0, 0]} args={[1, 1, 1]}>
<MeshStandardMaterial color="magenta" />
</Box>
);
}
function App() {
return (
<Canvas>
<RotatingBox />
</Canvas>
);
}
export default App;
在上面的代码中,我们使用 useRef
来获取立方体的引用,然后在 useFrame
钩子中更新立方体的旋转角度,从而实现立方体的旋转动画。🎡
3.4.2 处理鼠标交互事件(如点击、悬停等)
@react-three/fiber
支持处理鼠标交互事件,比如点击、悬停等。
import { Canvas, Box, MeshStandardMaterial } from '@react-three/fiber';
function InteractiveBox() {
const handleClick = () => {
console.log('Box clicked!');
};
const handleHover = (e) => {
e.stopPropagation();
console.log('Box hovered!');
};
return (
<Box
position={[0, 0, 0]}
args={[1, 1, 1]}
onClick={handleClick}
onPointerOver={handleHover}
onPointerOut={handleHover}
>
<MeshStandardMaterial color="brown" />
</Box>
);
}
function App() {
return (
<Canvas>
<InteractiveBox />
</Canvas>
);
}
export default App;
在上面的代码中,我们通过 onClick
、onPointerOver
和 onPointerOut
属性分别处理了点击、鼠标悬停和鼠标移出事件。👏
3.4.3 实现物体的动画效果(如旋转、移动等)
除了使用 useFrame
钩子实现动画,我们还可以使用其他方式来实现物体的动画效果。
import { Canvas, Box, MeshStandardMaterial, useSpring } from '@react-three/fiber';
import { a } from '@react-spring/three';
function AnimatedBox() {
const props = useSpring({
from: { position: [-2, 0, 0] },
to: { position: [2, 0, 0] },
loop: true,
});
return (
<a.box position={props.position}>
<MeshStandardMaterial color="lime" />
</a.box>
);
}
function App() {
return (
<Canvas>
<AnimatedBox />
</Canvas>
);
}
export default App;
在上面的代码中,我们使用了 @react-spring/three
库中的 useSpring
钩子来实现物体的移动动画。通过 from
和 to
属性设置了动画的起始和结束状态,通过 loop
属性设置了动画是否循环。🚀
第四章 @react-three/drei的使用
@react - three/drei 是一个为 React Three Fiber 提供了大量预制组件和帮助函数的库,能让开发者更轻松地创建 3D 场景。下面我们来详细介绍它的使用。
4.1 辅助工具组件
4.1.1 使用 OrbitControls 实现相机控制
在 3D 场景中,相机控制是非常重要的,它能让用户自由地观察场景。OrbitControls
组件可以让我们轻松实现相机的旋转、缩放和平移控制。
- 安装依赖:确保你已经安装了
@react - three/drei
和@react - three/fiber
。
npm install @react - three/drei @react - three/fiber
- 代码示例:
import React from 'react';
import { Canvas } from '@react - three/fiber';
import { OrbitControls } from '@react - three/drei';
const Scene = () => {
return (
<Canvas>
{/* 其他 3D 对象 */}
<OrbitControls />
</Canvas>
);
};
export default Scene;
- 效果:在浏览器中打开这个场景,你就可以使用鼠标进行相机的旋转、缩放和平移操作啦😎。
4.1.2 使用 Stats 组件显示性能统计信息
Stats
组件可以帮助我们实时监控 3D 场景的性能,比如帧率(FPS)、渲染时间等。
- 代码示例:
import React from 'react';
import { Canvas } from '@react - three/fiber';
import { Stats } from '@react - three/drei';
const Scene = () => {
return (
<Canvas>
{/* 其他 3D 对象 */}
<Stats />
</Canvas>
);
};
export default Scene;
- 效果:在场景的左上角会出现一个性能统计面板,显示当前场景的性能指标🧐。
4.1.3 使用 AxesHelper 显示坐标轴
AxesHelper
组件可以在场景中显示坐标轴,帮助我们更好地理解 3D 空间的方向和位置。
- 代码示例:
import React from 'react';
import { Canvas } from '@react - three/fiber';
import { AxesHelper } from '@react - three/drei';
const Scene = () => {
return (
<Canvas>
<AxesHelper size={5} />
{/* 其他 3D 对象 */}
</Canvas>
);
};
export default Scene;
- 参数说明:
size
参数表示坐标轴的长度,这里设置为 5。 - 效果:场景中会出现一个彩色的坐标轴,红色代表 X 轴,绿色代表 Y 轴,蓝色代表 Z 轴🗺️。
4.2 3D 模型加载
4.2.1 加载常见 3D 模型格式(如 GLTF、OBJ 等)
@react - three/drei
提供了方便的组件来加载常见的 3D 模型格式。以 GLTF 格式为例:
- 代码示例:
import React from 'react';
import { Canvas } from '@react - three/fiber';
import { GLTFLoader } from '@react - three/drei';
const Scene = () => {
return (
<Canvas>
<GLTFLoader url="/path/to/your/model.gltf" />
</Canvas>
);
};
export default Scene;
- 说明:
url
属性指定了模型文件的路径。
4.2.2 处理模型加载过程中的状态(如加载中、加载完成、加载失败等)
我们可以通过监听 GLTFLoader
的事件来处理模型加载过程中的不同状态。
- 代码示例:
import React, { useState } from 'react';
import { Canvas } from '@react - three/fiber';
import { GLTFLoader } from '@react - three/drei';
const Scene = () => {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const onLoad = () => {
setLoading(false);
};
const onError = (err) => {
setLoading(false);
setError(err);
};
return (
<Canvas>
{loading && <div>Loading...</div>}
{error && <div>Error: {error.message}</div>}
<GLTFLoader url="/path/to/your/model.gltf" onLoad={onLoad} onError={onError} />
</Canvas>
);
};
export default Scene;
- 效果:在模型加载过程中,页面会显示“Loading…”,加载完成后该提示消失,如果加载失败会显示错误信息。
4.2.3 对加载的模型进行调整和优化
加载模型后,我们可能需要对模型进行一些调整,比如缩放、旋转、移动等。
- 代码示例:
import React from 'react';
import { Canvas } from '@react - three/fiber';
import { GLTFLoader } from '@react - three/drei';
const Scene = () => {
return (
<Canvas>
<GLTFLoader
url="/path/to/your/model.gltf"
onLoad={(gltf) => {
const model = gltf.scene;
model.scale.set(0.5, 0.5, 0.5); // 缩放模型
model.rotation.y = Math.PI / 4; // 旋转模型
model.position.x = 2; // 移动模型
}}
/>
</Canvas>
);
};
export default Scene;
- 说明:在
onLoad
回调函数中,我们可以获取到加载的模型对象,然后对其进行各种调整。
4.3 高级组件应用
4.3.1 使用 Text 组件创建 3D 文本
Text
组件可以让我们在 3D 场景中创建文本。
- 代码示例:
import React from 'react';
import { Canvas } from '@react - three/fiber';
import { Text } from '@react - three/drei';
const Scene = () => {
return (
<Canvas>
<Text
position={[0, 2, 0]}
fontSize={1}
color="red"
>
Hello, 3D World!
</Text>
</Canvas>
);
};
export default Scene;
- 参数说明:
position
指定文本的位置,fontSize
指定文本的大小,color
指定文本的颜色。
4.3.2 使用 Sky 组件创建天空盒
Sky
组件可以帮助我们创建一个逼真的天空盒,增强场景的沉浸感。
- 代码示例:
import React from 'react';
import { Canvas } from '@react - three/fiber';
import { Sky } from '@react - three/drei';
const Scene = () => {
return (
<Canvas>
<Sky sunPosition={[10, 10, 10]} />
{/* 其他 3D 对象 */}
</Canvas>
);
};
export default Scene;
- 参数说明:
sunPosition
指定太阳的位置,会影响天空的光照效果。
4.3.3 使用 Stars 组件创建星空效果
Stars
组件可以在场景中创建星空效果。
- 代码示例:
import React from 'react';
import { Canvas } from '@react - three/fiber';
import { Stars } from '@react - three/drei';
const Scene = () => {
return (
<Canvas>
<Stars count={1000} radius={100} depth={200} factor={4} saturation={0} fade />
{/* 其他 3D 对象 */}
</Canvas>
);
};
export default Scene;
- 参数说明:
count
表示星星的数量,radius
表示星星分布的半径,depth
表示星星的深度,factor
控制星星的大小变化,saturation
控制星星的饱和度,fade
表示星星是否有渐变效果。
通过以上介绍,你可以看到 @react - three/drei
为我们创建 3D 场景提供了很多便利的组件和功能,让我们可以更轻松地开发出炫酷的 3D 应用🎉。
第五章 参数详细解析
5.1 @react - three/fiber 参数解析
5.1.1 场景组件的参数
场景组件在 @react - three/fiber
中就像是一个舞台,所有的 3D 对象都将在这个舞台上表演。以下是一些常见的场景组件参数:
- 背景颜色(background):这个参数用于设置场景的背景颜色,就像给舞台铺上一块背景布。可以使用十六进制颜色代码或者 RGB 值来指定颜色。例如:
<Scene background="#000000" />
这里将场景的背景颜色设置为黑色。
- 雾(fog):雾可以为场景增添一种朦胧的氛围,就像舞台上弥漫着一层雾气。可以通过
Fog
或FogExp2
来创建雾效果。例如:
<Scene fog={new THREE.Fog(0xffffff, 1, 10)} />
这里创建了一个白色的线性雾,从距离相机 1 个单位开始,到 10 个单位结束。
5.1.2 相机组件的参数
相机组件就像是观众的眼睛,决定了我们从哪个角度和位置观察场景。
- 位置(position):指定相机在 3D 空间中的位置。它是一个包含
x
、y
、z
坐标的对象。例如:
<PerspectiveCamera position={{ x: 0, y: 0, z: 5 }} />
这里将相机放置在 z
轴上距离原点 5 个单位的位置。
- 视野(fov):视野决定了相机能够看到的角度范围,类似于人眼的视角。值越大,看到的范围越广,但对象会显得更小。例如:
<PerspectiveCamera fov={75} />
这里设置相机的视野为 75 度。
- 近裁剪面(near)和远裁剪面(far):这两个参数定义了相机能够看到的最近和最远的距离。在这个范围之外的对象将不会被渲染。例如:
<PerspectiveCamera near={0.1} far={1000} />
这里设置相机能够看到距离它 0.1 到 1000 个单位之间的对象。
5.1.3 几何体组件的参数
几何体组件用于创建各种 3D 形状,就像用积木搭建不同的物体。
- 尺寸(size):对于一些几何体,如立方体(BoxGeometry),可以通过
width
、height
和depth
来指定其尺寸。例如:
<BoxGeometry args={[1, 1, 1]} />
这里创建了一个边长为 1 的立方体。
- 细分(segments):细分参数决定了几何体表面的平滑程度。细分值越高,表面越平滑,但渲染成本也会增加。例如,在球体(SphereGeometry)中:
<SphereGeometry args={[1, 32, 32]} />
这里的 32
表示球体在水平和垂直方向上的细分数量。
5.1.4 材质组件的参数
材质组件决定了几何体的外观,就像给积木涂上不同的颜色和纹理。
- 颜色(color):设置材质的基本颜色。例如:
<MeshBasicMaterial color="#ff0000" />
这里将材质的颜色设置为红色。
- 透明度(transparent 和 opacity):
transparent
用于启用透明度,opacity
用于设置透明度的值(范围从 0 到 1)。例如:
<MeshBasicMaterial transparent opacity={0.5} />
这里创建了一个半透明的材质。
5.1.5 光照组件的参数
光照组件就像是舞台上的灯光,照亮场景中的对象。
- 颜色(color):指定光照的颜色。例如:
<DirectionalLight color="#ffffff" />
这里将平行光的颜色设置为白色。
- 强度(intensity):控制光照的强度,值越大,光照越亮。例如:
<DirectionalLight intensity={1.5} />
这里将平行光的强度设置为 1.5。
5.2 @react - three/drei 参数解析
5.2.1 辅助工具组件的参数
辅助工具组件可以帮助我们更好地调试和理解 3D 场景。
- 尺寸(size):对于一些辅助工具,如坐标轴辅助器(AxesHelper),可以通过
size
参数来指定其大小。例如:
<AxesHelper size={2} />
这里创建了一个大小为 2 的坐标轴辅助器。
5.2.2 模型加载组件的参数
模型加载组件用于加载外部的 3D 模型。
- 路径(path):指定模型文件的路径。例如:
<GLTFLoader url="/models/myModel.gltf" />
这里加载位于 /models
目录下的 myModel.gltf
模型。
- 加载完成回调(onLoad):当模型加载完成时触发的回调函数。例如:
<GLTFLoader
url="/models/myModel.gltf"
onLoad={(gltf) => {
console.log('模型加载完成', gltf);
}}
/>
5.2.3 高级组件的参数
高级组件通常提供了更复杂的功能和效果。
- 动画参数:一些高级组件可能支持动画效果,可以通过参数来控制动画的速度、循环次数等。例如,一个具有动画的组件可能有
animationSpeed
和loop
参数:
<AnimatedComponent animationSpeed={0.5} loop={true} />
这里将动画速度设置为 0.5,并开启循环播放。
🎈通过对这些参数的理解和使用,我们可以更加灵活地创建出丰富多彩的 3D 场景。
第六章 组件封装
在开发过程中,组件封装是一项非常重要的技术,它可以提高代码的复用性、可维护性和开发效率。本章将详细介绍如何进行组件封装,包括基础组件的封装、复杂组件的封装以及组件的复用与扩展。
6.1 封装基础组件
基础组件是构成复杂组件的基石,下面我们将分别介绍自定义几何体组件、自定义材质组件和自定义光照组件的封装。
6.1.1 封装自定义几何体组件
1. 几何体的概念
几何体是 3D 场景中物体的形状描述,例如立方体、球体、圆柱体等。在封装自定义几何体组件时,我们可以根据需求创建独特的形状。
2. 封装步骤
- 定义几何体数据:确定几何体的顶点坐标、面的连接关系等数据。例如,创建一个简单的三角形几何体,需要定义三个顶点的坐标。
// 定义三角形的顶点坐标
const vertices = new Float32Array([
0, 0, 0,
1, 0, 0,
0, 1, 0
]);
- 创建几何体对象:使用定义好的数据创建几何体对象。
import * as THREE from 'three';
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
- 封装为组件:将几何体的创建过程封装成一个可复用的函数或类。
function createTriangleGeometry() {
const vertices = new Float32Array([
0, 0, 0,
1, 0, 0,
0, 1, 0
]);
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
return geometry;
}
6.1.2 封装自定义材质组件
1. 材质的概念
材质决定了几何体在 3D 场景中的外观,如颜色、光泽、透明度等。常见的材质有基础材质、标准材质、物理材质等。
2. 封装步骤
- 选择材质类型:根据需求选择合适的材质类型,例如基础材质(MeshBasicMaterial)适用于简单的颜色显示,标准材质(MeshStandardMaterial)支持光照效果。
// 创建基础材质
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
- 定制材质属性:可以对材质的属性进行定制,如颜色、透明度、金属度等。
// 创建标准材质并定制属性
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
roughness: 0.5,
metalness: 0.2
});
- 封装为组件:将材质的创建过程封装成一个可复用的函数或类。
function createCustomMaterial() {
return new THREE.MeshStandardMaterial({
color: 0x00ff00,
roughness: 0.5,
metalness: 0.2
});
}
6.1.3 封装自定义光照组件
1. 光照的作用
光照在 3D 场景中起着至关重要的作用,它可以模拟现实世界中的光线效果,使物体看起来更加真实。常见的光照类型有环境光、平行光、点光源等。
2. 封装步骤
- 选择光照类型:根据场景需求选择合适的光照类型,例如环境光可以均匀照亮整个场景,平行光可以模拟太阳光。
// 创建环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
- 定制光照属性:可以对光照的属性进行定制,如颜色、强度、位置等。
// 创建平行光并定制属性
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
- 封装为组件:将光照的创建过程封装成一个可复用的函数或类。
function createDirectionalLight() {
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(1, 1, 1);
return light;
}
6.2 封装复杂组件
复杂组件通常由多个基础组件组合而成,下面我们将介绍带有动画效果的 3D 模型组件、交互式场景组件和可复用的 3D 界面组件的封装。
6.2.1 封装带有动画效果的 3D 模型组件
1. 动画的实现方式
在 3D 场景中,动画可以通过关键帧动画、骨骼动画、变形动画等方式实现。
2. 封装步骤
- 加载 3D 模型:使用合适的加载器加载 3D 模型,例如 GLTF 模型。
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load('model.gltf', (gltf) => {
const model = gltf.scene;
// 处理模型
});
- 添加动画控制:如果模型包含动画,可以通过动画混合器(AnimationMixer)来控制动画的播放、暂停、循环等。
const mixer = new THREE.AnimationMixer(model);
const clips = gltf.animations;
if (clips.length > 0) {
const action = mixer.clipAction(clips[0]);
action.play();
}
- 封装为组件:将模型加载和动画控制的过程封装成一个可复用的函数或类。
function createAnimatedModel() {
return new Promise((resolve, reject) => {
const loader = new GLTFLoader();
loader.load('model.gltf', (gltf) => {
const model = gltf.scene;
const mixer = new THREE.AnimationMixer(model);
const clips = gltf.animations;
if (clips.length > 0) {
const action = mixer.clipAction(clips[0]);
action.play();
}
resolve({ model, mixer });
}, undefined, reject);
});
}
6.2.2 封装交互式场景组件
1. 交互的实现方式
交互式场景允许用户与 3D 场景进行互动,常见的交互方式有鼠标点击、拖动、触摸等。
2. 封装步骤
- 添加交互事件监听:使用事件监听器来捕获用户的交互事件,例如鼠标点击事件。
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
// 计算鼠标在标准化设备坐标中的位置
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 通过鼠标位置更新射线
raycaster.setFromCamera(mouse, camera);
// 计算射线与场景中物体的交点
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
// 处理交点
const object = intersects[0].object;
console.log('Clicked on object:', object);
}
}
window.addEventListener('click', onMouseClick);
- 封装为组件:将交互事件的监听和处理过程封装成一个可复用的函数或类。
function createInteractiveScene(scene, camera) {
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
const object = intersects[0].object;
console.log('Clicked on object:', object);
}
}
window.addEventListener('click', onMouseClick);
return {
destroy: () => {
window.removeEventListener('click', onMouseClick);
}
};
}
6.2.3 封装可复用的 3D 界面组件
1. 3D 界面的概念
3D 界面是指在 3D 场景中创建的用户界面元素,如按钮、菜单、文本框等。
2. 封装步骤
- 创建界面元素:使用几何体和材质创建界面元素的外观,例如创建一个按钮。
// 创建按钮几何体
const buttonGeometry = new THREE.PlaneGeometry(1, 0.5);
// 创建按钮材质
const buttonMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff });
// 创建按钮网格
const button = new THREE.Mesh(buttonGeometry, buttonMaterial);
- 添加交互功能:为界面元素添加交互功能,如点击事件。
button.addEventListener('click', () => {
console.log('Button clicked!');
});
- 封装为组件:将界面元素的创建和交互功能封装成一个可复用的函数或类。
function createButton() {
const buttonGeometry = new THREE.PlaneGeometry(1, 0.5);
const buttonMaterial = new THREE.MeshBasicMaterial({ color: 0x0000ff });
const button = new THREE.Mesh(buttonGeometry, buttonMaterial);
button.addEventListener('click', () => {
console.log('Button clicked!');
});
return button;
}
6.3 组件的复用与扩展
组件封装的一个重要目的是实现组件的复用和扩展,下面我们将介绍如何在不同项目中复用封装的组件以及对封装组件进行扩展和定制。
6.3.1 在不同项目中复用封装的组件
1. 组件的导出与导入
将封装好的组件导出为模块,在其他项目中可以通过导入模块的方式复用组件。
// 导出组件
export function createTriangleGeometry() {
const vertices = new Float32Array([
0, 0, 0,
1, 0, 0,
0, 1, 0
]);
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
return geometry;
}
// 在其他项目中导入并使用组件
import { createTriangleGeometry } from './components';
const geometry = createTriangleGeometry();
2. 版本管理
在复用组件时,需要注意组件的版本管理,确保使用的组件版本与项目兼容。可以使用包管理工具(如 npm)来管理组件的版本。
6.3.2 对封装组件进行扩展和定制
1. 继承与扩展
可以通过继承封装好的组件类,对组件进行扩展和定制。例如,扩展自定义材质组件。
// 自定义材质组件
function createCustomMaterial() {
return new THREE.MeshStandardMaterial({
color: 0x00ff00,
roughness: 0.5,
metalness: 0.2
});
}
// 扩展自定义材质组件
class ExtendedMaterial extends THREE.MeshStandardMaterial {
constructor() {
super({
color: 0x00ff00,
roughness: 0.5,
metalness: 0.2
});
// 扩展属性
this.customProperty = 'custom value';
}
// 扩展方法
customMethod() {
console.log('Custom method called');
}
}
const extendedMaterial = new ExtendedMaterial();
2. 配置参数
在封装组件时,可以提供一些配置参数,允许用户根据需要对组件进行定制。例如,在创建自定义几何体组件时,可以提供顶点坐标作为配置参数。
function createCustomGeometry(vertices) {
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3));
return geometry;
}
// 使用配置参数创建几何体
const vertices = [
0, 0, 0,
2, 0, 0,
0, 2, 0
];
const geometry = createCustomGeometry(vertices);
通过组件的复用和扩展,可以大大提高开发效率,减少重复代码的编写。🎉
第七章 性能优化与调试
7.1 性能优化策略
7.1.1 减少不必要的渲染
在 Three.js 场景中,不必要的渲染会消耗大量的性能资源,就像一辆车一直空转,白白浪费汽油🚗。以下是一些减少不必要渲染的方法:
- 控制渲染循环:在 Three.js 中,通常使用
requestAnimationFrame
来实现动画循环。但如果场景中没有动态变化,就不需要一直进行渲染。可以通过一个标志位来控制是否需要渲染,例如:
let isAnimating = true;
function animate() {
if (isAnimating) {
renderer.render(scene, camera);
}
requestAnimationFrame(animate);
}
animate();
// 当不需要渲染时,将标志位设为 false
isAnimating = false;
- 减少场景更新频率:如果场景中的某些对象更新频率很高,但对视觉效果影响不大,可以适当降低其更新频率。比如,一些次要的粒子效果,不需要每一帧都更新。
7.1.2 优化几何体和材质的使用
几何体和材质是 Three.js 场景的重要组成部分,合理使用它们可以显著提升性能。
使用简单几何体:复杂的几何体需要更多的计算资源来渲染。在满足需求的前提下,尽量使用简单的几何体。例如,用立方体代替复杂的自定义模型。
合并几何体:如果场景中有多个相同的几何体,可以将它们合并成一个,减少渲染调用次数。Three.js 提供了
BufferGeometryUtils.mergeBufferGeometries
方法来实现几何体的合并,示例代码如下:
import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
const geometries = [];
// 假设这里有多个几何体
for (let i = 0; i < 10; i++) {
const geometry = new THREE.BoxGeometry(1, 1, 1);
geometries.push(geometry);
}
const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(geometries);
- 材质复用:避免为每个对象都创建新的材质,尽量复用相同的材质。例如:
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube1 = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), material);
const cube2 = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), material);
7.1.3 处理大规模场景的性能问题
当处理大规模场景时,性能问题会更加突出。以下是一些应对策略:
分区加载:将大规模场景划分为多个区域,只加载当前可见区域的对象。当相机移动到其他区域时,再加载相应区域的对象。可以使用八叉树(Octree)等数据结构来实现分区管理。
使用 LOD(Level of Detail):根据对象与相机的距离,动态调整对象的细节程度。距离相机近的对象使用高细节模型,距离远的对象使用低细节模型。Three.js 提供了
LOD
类来实现这个功能,示例代码如下:
const lod = new THREE.LOD();
const highDetailGeometry = new THREE.BoxGeometry(1, 1, 1);
const highDetailMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const highDetailMesh = new THREE.Mesh(highDetailGeometry, highDetailMaterial);
lod.addLevel(highDetailMesh, 0);
const lowDetailGeometry = new THREE.SphereGeometry(0.5, 8, 8);
const lowDetailMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const lowDetailMesh = new THREE.Mesh(lowDetailGeometry, lowDetailMaterial);
lod.addLevel(lowDetailMesh, 10);
scene.add(lod);
7.2 调试技巧
7.2.1 使用浏览器开发者工具调试 Three.js 场景
浏览器开发者工具是调试 Three.js 场景的强大工具,就像医生的听诊器,可以帮助我们发现问题所在👨⚕️。
查看场景结构:在 Chrome 开发者工具的“Elements”面板中,可以查看 Three.js 场景的 DOM 结构,了解对象的层次关系。
监测性能:使用“Performance”面板可以记录场景的性能数据,包括帧率、CPU 使用率等。通过分析这些数据,找出性能瓶颈。
调试材质和着色器:在“Sources”面板中,可以查看和调试材质和着色器的代码。可以在代码中设置断点,逐步调试。
7.2.2 利用日志和断点调试代码
日志和断点是调试代码的基本方法,可以帮助我们追踪代码的执行过程。
- 使用
console.log
:在关键位置添加console.log
语句,输出变量的值和执行信息。例如:
const cube = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial({ color: 0x00ff00 }));
console.log('Cube created:', cube);
- 设置断点:在浏览器开发者工具的“Sources”面板中,找到要调试的代码文件,设置断点。当代码执行到断点处时,会暂停执行,此时可以查看变量的值和调用栈。
7.2.3 解决常见的渲染问题和错误
在 Three.js 开发中,会遇到一些常见的渲染问题和错误,以下是一些解决方法:
黑屏问题:如果场景显示为黑屏,可能是相机位置设置不正确、材质加载失败等原因。可以检查相机的位置和方向,确保材质路径正确。
闪烁问题:闪烁问题可能是由于深度缓冲区冲突引起的。可以尝试调整对象的深度值,或者使用
renderer.shadowMap.enabled = true
来启用阴影映射。材质显示异常:如果材质显示不正常,可能是材质属性设置错误或者着色器代码有问题。可以检查材质的属性,如颜色、透明度等,同时调试着色器代码。
第八章 实际案例应用
8.1 简单3D展示页面
8.1.1 需求分析与设计
1. 需求分析
在创建简单3D展示页面之前,我们需要明确用户的需求。想象一下,你要向客户展示一款新产品,这个3D展示页面就像是一个虚拟的展厅。用户可能希望能够从不同的角度观察产品,放大或缩小产品的细节,甚至可能想要了解产品的一些基本信息。
- 交互需求:用户应该能够通过鼠标或触摸屏幕来旋转、缩放和平移3D模型。例如,在展示一个汽车模型时,用户可以用鼠标拖动来查看汽车的各个侧面,用鼠标滚轮来放大或缩小汽车的细节。
- 展示需求:页面需要清晰地展示3D模型,并且可以设置合适的光照和背景,以增强视觉效果。比如,对于一个珠宝模型,我们可以设置明亮的聚光灯来突出珠宝的光泽。
2. 设计方案
根据需求分析,我们可以制定以下设计方案:
- 选择合适的3D引擎:市面上有很多3D引擎可供选择,如Three.js、Babylon.js等。Three.js是一个轻量级的JavaScript 3D库,易于学习和使用,非常适合初学者。我们可以用它来创建3D场景、加载3D模型和处理用户交互。
- 设计页面布局:页面布局应该简洁明了,将3D展示区域放在主要位置,同时可以添加一些辅助信息,如模型的名称、描述等。例如,在页面的一侧可以设置一个面板,显示产品的规格和特点。
8.1.2 代码实现与讲解
1. 引入Three.js库
首先,我们需要在HTML文件中引入Three.js库。可以通过CDN链接的方式引入:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple 3D Display</title>
<!-- 引入Three.js库 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</head>
<body>
<!-- 这里是3D展示的容器 -->
<div id="container"></div>
<script src="script.js"></script>
</body>
</html>
2. 创建3D场景
在JavaScript文件(script.js)中,我们开始创建3D场景:
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);
// 创建一个立方体
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 渲染循环
function animate() {
requestAnimationFrame(animate);
// 让立方体旋转
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
这段代码的主要步骤如下:
- 创建一个场景(
THREE.Scene
),这就像是一个舞台,所有的3D对象都将在这个舞台上展示。 - 创建一个相机(
THREE.PerspectiveCamera
),相机决定了我们从哪个角度观察场景。 - 创建一个渲染器(
THREE.WebGLRenderer
),渲染器负责将场景和相机的信息渲染到屏幕上。 - 创建一个立方体(
THREE.Mesh
),并将其添加到场景中。 - 使用
requestAnimationFrame
函数创建一个渲染循环,让立方体不断旋转。
8.1.3 效果展示与优化
1. 效果展示
在浏览器中打开HTML文件,你将看到一个绿色的立方体在不断旋转。你可以通过调整相机的位置和立方体的旋转速度来改变展示效果。
2. 优化措施
- 添加光照:在场景中添加光照可以增强3D模型的真实感。例如,我们可以添加一个点光源:
// 添加点光源
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(5, 5, 5);
scene.add(pointLight);
- 处理用户交互:使用
OrbitControls
插件可以让用户通过鼠标来旋转、缩放和平移场景:
// 引入OrbitControls插件
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.128.0/examples/jsm/controls/OrbitControls.js';
// 创建控制器
const controls = new OrbitControls(camera, renderer.domElement);
8.2 交互式3D游戏
8.2.1 游戏功能规划
1. 游戏目标
在规划交互式3D游戏时,首先要明确游戏的目标。例如,这可能是一个冒险游戏,玩家需要控制一个角色在3D世界中探索,找到隐藏的宝藏;也可能是一个射击游戏,玩家需要击败敌人来获得胜利。
2. 功能设计
- 角色控制:玩家应该能够通过键盘或手柄来控制游戏角色的移动、跳跃和攻击。比如,在一个平台游戏中,玩家可以使用方向键控制角色左右移动,使用空格键控制角色跳跃。
- 游戏场景:设计一个丰富的3D游戏场景,包括地形、建筑和道具。例如,在一个幻想游戏中,场景可以是一个神秘的森林,里面有树木、石头和宝箱。
- 游戏交互:添加一些交互元素,如与NPC(非玩家角色)对话、触发机关等。比如,玩家可以与村庄里的NPC交谈,获取任务信息。
8.2.2 核心逻辑实现
1. 角色移动逻辑
使用Three.js和JavaScript来实现角色的移动逻辑。以下是一个简单的示例:
// 创建角色
const characterGeometry = new THREE.BoxGeometry();
const characterMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const character = new THREE.Mesh(characterGeometry, characterMaterial);
scene.add(character);
// 监听键盘事件
document.addEventListener('keydown', function (event) {
if (event.key === 'ArrowLeft') {
character.position.x -= 0.1;
} else if (event.key === 'ArrowRight') {
character.position.x += 0.1;
} else if (event.key === 'ArrowUp') {
character.position.z -= 0.1;
} else if (event.key === 'ArrowDown') {
character.position.z += 0.1;
}
});
这段代码实现了角色在键盘控制下的前后左右移动。
2. 碰撞检测逻辑
为了让游戏更加真实,需要实现碰撞检测逻辑。例如,当角色碰到墙壁时,不能穿过墙壁。可以使用射线检测的方法来实现碰撞检测:
// 创建射线
const raycaster = new THREE.Raycaster();
const direction = new THREE.Vector3(0, -1, 0);
// 在渲染循环中进行碰撞检测
function animate() {
requestAnimationFrame(animate);
// 设置射线的起点和方向
raycaster.set(character.position, direction);
// 检测碰撞
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0 && intersects[0].distance < 0.5) {
// 处理碰撞事件
}
renderer.render(scene, camera);
}
animate();
8.2.3 游戏的测试与发布
1. 测试
在发布游戏之前,需要进行充分的测试。测试内容包括:
- 功能测试:检查游戏的各项功能是否正常工作,如角色控制、碰撞检测等。
- 兼容性测试:确保游戏在不同的浏览器和设备上都能正常运行。
2. 发布
将游戏发布到合适的平台,如网页游戏平台、应用商店等。在发布时,需要注意游戏的性能优化,确保玩家能够流畅地玩游戏。
8.3 3D数据可视化项目
8.3.1 数据处理与准备
1. 数据收集
首先,需要收集相关的数据。这些数据可以来自各种数据源,如数据库、API接口或文件。例如,要展示全球气温变化的3D可视化项目,我们可以从气象部门的数据库中获取气温数据。
2. 数据清洗
收集到的数据可能存在一些噪声和缺失值,需要进行清洗。例如,删除重复的数据、填充缺失值等。可以使用Python的Pandas库来进行数据清洗:
import pandas as pd
# 读取数据
data = pd.read_csv('temperature_data.csv')
# 删除重复数据
data = data.drop_duplicates()
# 填充缺失值
data = data.fillna(method='ffill')
3. 数据转换
将清洗后的数据转换为适合3D可视化的格式。例如,将气温数据转换为三维坐标,其中x和y坐标表示地理位置,z坐标表示气温值。
8.3.2 3D可视化效果的实现
1. 选择可视化库
可以使用Three.js或其他专门的数据可视化库来实现3D可视化效果。以下是一个使用Three.js创建简单3D柱状图的示例:
// 创建场景、相机和渲染器
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 假设我们有一组数据
const data = [1, 2, 3, 4, 5];
// 创建柱状图
for (let i = 0; i < data.length; i++) {
const geometry = new THREE.BoxGeometry(0.5, data[i], 0.5);
const material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
const bar = new THREE.Mesh(geometry, material);
bar.position.x = i - (data.length - 1) / 2;
bar.position.y = data[i] / 2;
scene.add(bar);
}
// 渲染循环
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
8.3.3 与后端数据的交互
1. 使用API接口
通过API接口与后端服务器进行数据交互。例如,使用fetch
函数从后端获取最新的数据:
fetch('https://api.example.com/temperature_data')
.then(response => response.json())
.then(data => {
// 处理获取到的数据
// 更新3D可视化效果
});
2. 实时更新
为了实现实时数据更新,可以使用WebSocket或轮询的方式。例如,使用WebSocket:
const socket = new WebSocket('ws://api.example.com/temperature_stream');
socket.addEventListener('message', function (event) {
const newData = JSON.parse(event.data);
// 更新3D可视化效果
});
这样,我们就可以实现3D数据可视化项目与后端数据的交互,实时展示最新的数据。
第九章 总结与展望
9.1 总结React使用Three.js、@react-three/fiber、@react-three/drei的要点
1. React 与 Three.js
- 基本集成思路
- 要在 React 中使用 Three.js,首先需要创建一个 Three.js 的场景(Scene)、相机(Camera)和渲染器(Renderer)。在 React 里,通常会在组件的生命周期方法(如
useEffect
钩子)中进行初始化操作。 - 例如,在
useEffect
中创建场景、相机和渲染器,然后将渲染器的 DOM 元素挂载到 React 组件的 DOM 节点上。
- 要在 React 中使用 Three.js,首先需要创建一个 Three.js 的场景(Scene)、相机(Camera)和渲染器(Renderer)。在 React 里,通常会在组件的生命周期方法(如
import React, { useEffect, useRef } from 'react';
import * as THREE from 'three';
const ThreeScene = () => {
const ref = useRef(null);
useEffect(() => {
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
ref.current.appendChild(renderer.domElement);
// 后续可以添加几何体、材质等
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
camera.position.z = 5;
const animate = () => {
requestAnimationFrame(animate);
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render(scene, camera);
};
animate();
return () => {
renderer.dispose();
};
}, []);
return <div ref={ref} />;
};
export default ThreeScene;
- 交互处理
- 处理 Three.js 场景中的交互(如点击、鼠标移动等)时,需要将 React 的事件处理机制与 Three.js 的射线投射(Raycaster)结合。射线投射可以检测鼠标点击位置与场景中物体的相交情况。
2. @react - three/fiber
- 核心优势
@react - three/fiber
是 React 与 Three.js 的桥梁,它将 Three.js 的对象以 JSX 的方式呈现,使得代码更具声明性和可维护性。例如,使用<Canvas>
组件来创建 Three.js 场景,就像在 React 中创建普通组件一样。
import React from 'react';
import { Canvas } from '@react - three/fiber';
const Scene = () => {
return (
<Canvas>
<mesh>
<boxGeometry />
<meshBasicMaterial color="hotpink" />
</mesh>
</Canvas>
);
};
export default Scene;
- 状态管理
- 可以使用 React 的状态管理机制(如
useState
、useReducer
)来管理 Three.js 场景中的状态。例如,控制物体的位置、旋转等。
- 可以使用 React 的状态管理机制(如
3. @react - three/drei
- 常用辅助组件
@react - three/drei
提供了许多有用的辅助组件,如<OrbitControls>
用于添加相机的轨道控制,用户可以通过鼠标拖动、缩放和旋转场景。
import React from 'react';
import { Canvas } from '@react - three/fiber';
import { OrbitControls } from '@react - three/drei';
const SceneWithControls = () => {
return (
<Canvas>
<mesh>
<boxGeometry />
<meshBasicMaterial color="lightblue" />
</mesh>
<OrbitControls />
</Canvas>
);
};
export default SceneWithControls;
- 预构建的材质和几何体
- 它还提供了一些预构建的材质和几何体,方便快速搭建场景,减少了手动创建的工作量。
9.2 未来发展趋势与应用前景
1. 发展趋势
- 性能优化
- 随着 WebGL 技术的不断发展,未来 React 与 Three.js 相关库会在性能优化方面有更大的突破。例如,更高效的渲染算法、更好的内存管理等,以支持更复杂的 3D 场景。
- 与其他技术融合
- 可能会与 WebXR(虚拟现实、增强现实)技术更紧密地结合,为用户带来沉浸式的 3D 体验。同时,也可能会与 AI 技术结合,实现智能的 3D 场景交互。
2. 应用前景
- 游戏开发
- 在网页游戏开发领域,使用 React、Three.js 及其相关库可以快速开发出跨平台的 3D 游戏,降低开发成本和难度。
- 电商展示
- 电商平台可以利用 3D 模型展示商品,让用户从不同角度查看商品细节,提高购物体验。
- 教育领域
- 用于创建 3D 教学模型,如物理实验模拟、生物细胞展示等,使教学内容更加生动形象。
9.3 进一步学习的建议和资源推荐
1. 学习建议
- 实践项目
- 动手做一些小的 3D 项目,从简单的几何体展示到复杂的交互场景,逐步提升自己的能力。可以参考一些开源项目,学习别人的代码结构和实现思路。
- 深入理解原理
- 学习 Three.js 的底层原理,了解 3D 图形学的基础知识,如坐标系、光照模型、材质等,这有助于更好地使用相关库。
2. 资源推荐
- 官方文档
- Three.js 官方文档(https://threejs.org/docs/):是学习 Three.js 的权威资料,包含了详细的 API 文档和示例。
@react - three/fiber
官方文档(https://docs.pmnd.rs/react - three - fiber/getting - started/introduction)和@react - three/drei
官方文档(https://github.com/pmndrs/drei):提供了组件的使用说明和示例。
- 在线教程
- YouTube 上有很多关于 Three.js 和 React 3D 开发的教程,如 Bruno Simon 的 Three.js Journey 系列教程,内容丰富且详细。
- 开源项目
- 在 GitHub 上搜索相关的开源项目,如
react - three - fiber - demos
等,可以学习到实际项目中的代码结构和最佳实践。
- 在 GitHub 上搜索相关的开源项目,如