快速入门 React Three Fiber – wiki基地


征服 3D 世界:React Three Fiber 快速入门指南

引言:拥抱 Web 上的 3D 可能性

在当今的 Web 开发领域,仅仅构建静态或简单的交互式界面已经无法满足日益增长的用户期待。随着技术的飞速发展,将令人惊叹的 3D 图形、沉浸式体验和高性能可视化集成到 Web 应用程序中变得越来越重要。当我们谈论 Web 上的 3D 图形时,Three.js 无疑是最强大、最灵活、最受欢迎的库之一。它提供了一个高级 API,让开发者能够轻松创建和渲染复杂的 3D 场景。

然而,对于习惯了现代 Web 框架(如 React)声明式、组件化开发模式的开发者来说,直接使用 Three.js 的命令式 API 可能会感觉有些别扭。在 React 应用中管理 Three.js 场景、对象、状态以及如何将它们与 React 的组件生命周期和状态管理相结合,可能会变得复杂而繁琐。

这正是 React Three Fiber (R3F) 应运而生的地方。

React Three Fiber 是 Three.js 的一个 React 渲染器。它不是 Three.js 的替代品,而是 Three.js 的一个桥梁,它允许你使用 React 的声明式语法、组件模型和生态系统来构建 Three.js 场景。想象一下,用 JSX 语法像构建 DOM 元素一样构建 3D 对象、设置材质、添加灯光、控制相机,这正是 R3F 带来的魔力。

本文将带你踏上 React Three Fiber 的快速入门之旅。我们将从零开始,一步步搭建环境,理解 R3F 的核心概念,并通过一个简单的实例构建你的第一个 3D 场景。我们将详细解释每一个步骤和代码片段,确保你能够快速掌握 R3F 的基础,并为探索更复杂的 3D 世界打下坚实的基础。

如果你已经熟悉 React 的开发流程,并渴望将 3D 图形的力量融入你的 Web 应用,那么请跟随本文,一起进入 React Three Fiber 的精彩世界!

为什么选择 React Three Fiber?解决痛点,拥抱高效

在深入学习如何使用 R3F 之前,我们先来理解一下为什么 R3F 如此受欢迎,它解决了哪些在使用原生 Three.js 与 React 结合时可能遇到的问题:

  1. 声明式 vs. 命令式: Three.js 是一个命令式库。这意味着你需要手动创建对象、设置属性、将它们添加到场景中、手动更新它们的状态,并在需要时手动移除它们。而在 React 中,我们习惯了声明式编程:你描述 UI (在这里是 3D 场景) 应该是什么样子,React 负责根据状态的变化来更新它。R3F 将 Three.js 的命令式 API 封装起来,让你能够以 React 组件的方式来声明 3D 场景图。
  2. 组件化: React 的核心是组件化。你可以将复杂的 UI 拆分成独立的、可重用的组件。R3F 将这个概念引入到 3D 场景构建中。你可以创建代表特定 3D 对象、效果或交互逻辑的 React 组件,然后在场景中像使用普通 React 组件一样使用它们。这极大地提高了代码的可读性、可维护性和可重用性。
  3. 状态管理和数据流: 在原生 Three.js 中管理复杂的应用状态并将其同步到 3D 场景对象的属性上可能非常棘手。而 R3F 允许你利用 React 强大的状态管理能力(如 useState, useReducer, Context API, Redux, Zustand 等)。状态的变化自然会触发 R3F 组件的重新渲染,R3F 渲染器会自动计算出需要更新的 Three.js 对象属性,并高效地应用这些更新。
  4. React 生态系统集成: 使用 R3F,你可以无缝地使用 React 的 Hooks、Effect、Context 以及各种第三方 React 库。例如,你可以轻松地将数据获取、动画库(如 react-spring / @react-three/fiber/animated)、甚至物理引擎(如 @react-three/rapier)集成到你的 3D 场景中。
  5. 性能: R3F 并非简单地包裹 Three.js API。它内部有一套高效的机制来处理 Three.js 对象的属性更新。它会对比 React 虚拟 DOM 的变化,只更新 Three.js 场景中真正发生变化的属性,而不是每次都重新创建或更新整个场景。这通常能带来更好的性能。此外,它还提供了 useFrame 等 Hooks 来优化动画和渲染循环的管理。
  6. 强大的生态库 @react-three/drei 与 R3F 紧密相关的 @react-three/drei 库提供了大量实用的、现成的组件和 Hooks,比如相机控制器(OrbitControls)、加载器、几何体、材质、后处理效果等等。这个库极大地加速了 R3F 的开发效率。在实际开发中,drei 几乎是必不可少的。

总而言之,React Three Fiber 让 Web 上的 3D 开发变得更加符合现代前端开发的习惯,它结合了 Three.js 的强大功能与 React 的优雅和高效。

前置知识和环境准备

在开始 R3F 冒险之前,你需要确保具备以下条件:

  1. 基础的 JavaScript、HTML、CSS 知识: 这是 Web 开发的基础。
  2. 基础的 React 知识: 你需要理解 React 的组件、JSX、Props、State、Hooks(特别是 useState, useRef, useEffect)等核心概念。
  3. Node.js 和 npm/yarn/pnpm: 用于项目创建和依赖管理。请确保你的 Node.js 版本较新(推荐 LTS 版本)。
  4. 现代浏览器: 支持 WebGL 的现代浏览器(几乎所有主流浏览器都支持)。

接下来,我们将创建一个新的 React 项目,并安装必要的依赖。我们将使用 Vite 作为构建工具,因为它快速且易于配置,是创建现代 React 项目的推荐方式。

1. 创建一个新的 React 项目

打开你的终端,执行以下命令:

“`bash
npm create vite my-r3f-app –template react-ts # 或者 –template react 如果不使用 TypeScript

或者使用 yarn

yarn create vite my-r3f-app –template react-ts

或者使用 pnpm

pnpm create vite my-r3f-app –template react-ts

“`

根据提示完成项目创建。进入项目目录:

bash
cd my-r3f-app

安装项目依赖:

“`bash
npm install

或者 yarn install

或者 pnpm install

“`

运行项目,确保一切正常:

“`bash
npm run dev

或者 yarn dev

或者 pnpm dev

“`

访问浏览器中显示的地址(通常是 http://localhost:5173),你应该能看到 Vite 默认的 React 欢迎页面。

2. 安装 React Three Fiber 和 Three.js

现在,安装 R3F 和 Three.js 本身:

“`bash
npm install @react-three/fiber three

或者 yarn add @react-three/fiber three

或者 pnpm add @react-three/fiber three

“`

3. 安装辅助库 @react-three/drei (强烈推荐)

如前所述,@react-three/drei 提供了大量便捷的组件。强烈建议安装它:

“`bash
npm install @react-three/drei

或者 yarn add @react-three/drei

或者 pnpm add @react-three/drei

“`

至此,你的项目环境已经准备就绪,可以开始构建你的第一个 R3F 场景了!

R3F 核心概念解析

在动手写代码之前,理解 R3F 的几个核心概念至关重要:

  1. <Canvas> 组件: 这是 R3F 应用的入口点。它负责设置 WebGL 渲染器 (WebGLRenderer)、场景 (Scene) 和相机 (Camera)。所有你在 <Canvas> 内部定义的 3D 对象和组件都将在这个 3D 环境中渲染。你可以通过它的 props 来配置渲染器、相机、画布大小等。
  2. 声明式场景图:<Canvas> 内部,你可以像写 HTML/JSX 一样嵌套地声明 Three.js 对象。例如,<mesh> 组件对应 Three.js 的 Mesh 类,<boxGeometry> 对应 BoxGeometry<meshStandardMaterial> 对应 MeshStandardMaterial。这些组件的属性(props)直接映射到对应的 Three.js 对象的属性。R3F 会负责创建这些 Three.js 对象并将它们组织成一个场景图。
  3. Hooks: R3F 提供了一些重要的 Hooks 来与 3D 环境进行交互:
    • useFrame: 这是一个渲染循环 Hook。你可以在任何组件中使用它来在每一帧渲染之前执行代码,这非常适合做动画和更新。它提供了一个 state 对象,其中包含渲染器的状态、场景、相机、当前时间等信息。
    • useLoader: 用于异步加载 3D 模型、纹理等资源。
    • useThree: 让你访问当前 <Canvas> 的 Three.js 状态(renderer, scene, camera, gl context 等)。
    • useRef: 在 R3F 中,如果你需要直接访问底层 Three.js 对象的实例(例如,为了在 useFrame 中直接修改它的位置或旋转),你需要使用 useRef Hook 来获取一个引用,并将其赋给 R3F 组件的 ref 属性。
  4. Props 映射: R3F 组件的 props 通常直接映射到 underlying Three.js 对象的属性或方法。例如,<mesh position={[1, 2, 3]}> 会设置 Three.js Mesh 对象的 position 属性为 [1, 2, 3]。Props 的值可以是原始类型、数组,甚至是 Three.js 对象实例本身。
  5. 事件系统: R3F 为 3D 对象提供了一套基于指针(鼠标、触摸)的事件系统。你可以直接在 <mesh> 或其他支持事件的组件上添加 onClick, onPointerOver, onPointerOut 等事件处理器,就像在处理 DOM 元素事件一样。

理解了这些概念,我们就可以开始构建实际的 3D 场景了。

构建你的第一个 R3F 场景:旋转的立方体

我们将构建一个非常简单的场景:一个旋转的彩色立方体,它带有光照,并且你可以通过鼠标来控制相机视角。

我们将修改 src/App.jsx (或 src/App.tsx) 文件来创建我们的 3D 场景。

1. 清理默认代码

首先,清空 src/App.jsxsrc/App.tsx 中的默认内容,只保留一个基本的函数组件结构。同时删除 src/App.csssrc/index.css 中的内容,或者直接删除文件并移除 main.jsx/main.tsx 中的引用,以便我们从一个干净的环境开始。为了让 Canvas 占据整个屏幕,我们可以给 body 或一个容器元素设置 width: 100vw; height: 100vh; margin: 0; overflow: hidden;。这里我们在 src/index.css 中设置。

src/index.css (清空并添加以下样式):

css
html, body, #root {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
overflow: hidden; /* 防止滚动条出现 */
background-color: #222; /* 设置一个深色背景 */
}

src/App.jsx (或 src/App.tsx, 清空并添加基本结构):

“`jsx
import React from ‘react’;
import { Canvas } from ‘@react-three/fiber’;
import { OrbitControls } from ‘@react-three/drei’;

function App() {
return (
// Canvas 是 R3F 场景的容器

{/ 在这里添加我们的 3D 对象和灯光 /}
{/ 默认相机位置在 Z 轴正方向,可以通过 camera props 配置 /}
{/ 默认会有一个环境光,但添加自己的灯光会更好 /}

  {/* 辅助组件:OrbitControls 让我们能够用鼠标旋转、缩放、平移相机 */}
  <OrbitControls />

  {/* 添加灯光 */}
  <ambientLight intensity={0.5} /> {/* 环境光,均匀照亮所有物体 */}
  <pointLight position={[10, 10, 10]} /> {/* 点光源,从一个点向四周发射光线 */}

  {/* 在这里添加我们的立方体 */}

</Canvas>

);
}

export default App;
“`

现在运行 npm run dev,你应该看到一个空的深色背景,并且可以通过鼠标拖拽来改变视角(这是 OrbitControls 的效果)。

2. 添加一个立方体

我们使用 <mesh> 组件来表示一个 3D 网格对象。一个网格对象由几何体 (geometry) 和材质 (material) 组成。

“`jsx
import React from ‘react’;
import { Canvas } from ‘@react-three/fiber’;
import { OrbitControls } from ‘@react-three/drei’;

// 添加一个组件来表示我们的立方体
function Box() {
// 返回一个 mesh。
// mesh 包含了几何体 (geometry) 和材质 (material)
return (

{/ boxGeometry 定义立方体的形状。 args 是构造函数参数 [width, height, depth] /}

{/ meshStandardMaterial 定义立方体的外观。 color 设置颜色 /}
{/ meshStandardMaterial 需要光照才能正确显示 /}


);
}

function App() {
return (

  <ambientLight intensity={0.5} />
  <pointLight position={[10, 10, 10]} />

  {/* 在 Canvas 中使用我们的 Box 组件 */}
  <Box />

</Canvas>

);
}

export default App;
“`

保存文件。浏览器应该会显示一个粉红色的立方体。你可以用鼠标拖拽来观察它。

代码解释:

  • 我们创建了一个名为 Box 的 React 组件。
  • Box 组件中,我们返回一个 <mesh>...</mesh> 结构。这告诉 R3F 创建一个 Three.js 的 Mesh 对象。
  • <boxGeometry args={[1, 1, 1]} />:这是一个内置的 R3F 组件,对应 Three.js 的 BoxGeometry 类。args={[1, 1, 1]} 传入 BoxGeometry 构造函数的参数,这里表示创建一个边长为 1 的立方体。
  • <meshStandardMaterial color="hotpink" />:对应 Three.js 的 MeshStandardMaterial 类。color="hotpink" 设置材质的颜色。MeshStandardMaterial 是一种基于物理的材质,需要光照才能正确渲染出颜色和阴影。
  • 我们将 Box 组件放在 <Canvas> 中。R3F 会自动处理创建 Three.js 对象并将其添加到场景中。

3. 让立方体动起来:使用 useFrame Hook

静态的立方体很无聊。我们让它旋转起来!这需要使用 useFrame Hook。

“`jsx
import React, { useRef } from ‘react’; // 引入 useRef
import { Canvas, useFrame } from ‘@react-three/fiber’; // 引入 useFrame
import { OrbitControls } from ‘@react-three/drei’;

function Box() {
// 1. 创建一个 ref 来引用 mesh 对象
const meshRef = useRef();

// 2. 使用 useFrame Hook
// 它在每一帧渲染之前调用
useFrame((state, delta) => {
// state 包含 Three.js 的状态 (renderer, scene, camera, 等)
// delta 是自上一帧以来的时间差 (秒)

// 3. 通过 ref 访问 Three.js mesh 对象,并更新它的旋转
if (meshRef.current) {
  // 旋转 x 轴
  meshRef.current.rotation.x += delta;
  // 旋转 y 轴
  meshRef.current.rotation.y += delta * 0.5;
}

});

return (
// 4. 将 ref 关联到 mesh 组件




);
}

function App() {
return (




);
}

export default App;
“`

保存文件。现在你应该看到立方体在持续旋转了!

代码解释:

  • 我们引入了 useRefuseFrame Hooks。
  • const meshRef = useRef(); 创建一个 ref 对象。
  • <mesh ref={meshRef}> 将这个 ref 对象关联到 <mesh> 组件。当 <mesh> 被渲染并创建了 underlying Three.js Mesh 对象后,这个对象实例就会被赋给 meshRef.current
  • useFrame((state, delta) => { ... }); 注册了一个函数,这个函数会在每一帧 Three.js 渲染 之前 被调用。这是执行动画和更新场景的理想位置。
  • state 参数提供了当前的 Three.js 渲染状态。delta 参数是自上一帧渲染以来的时间间隔(以秒为单位)。使用 delta 进行动画更新可以确保动画速度与帧率无关。
  • if (meshRef.current) { ... }:我们访问 meshRef.current 来获取 Three.js 的 Mesh 实例。
  • meshRef.current.rotation.x += delta;meshRef.current.rotation.y += delta * 0.5;:我们直接修改 Three.js Mesh 对象的旋转属性。因为 useFrame 在渲染前运行,这些更改会在当前帧被渲染出来。

4. 添加交互性:改变颜色

让我们的立方体在点击时改变颜色。这就像处理 DOM 事件一样简单。

“`jsx
import React, { useRef, useState } from ‘react’; // 引入 useState
import { Canvas, useFrame } from ‘@react-three/fiber’;
import { OrbitControls } from ‘@react-three/drei’;

function Box(props) { // 组件可以接收 props
const meshRef = useRef();
const [hovered, setHover] = useState(false); // 使用 state 跟踪鼠标是否悬停
const [active, setActive] = useState(false); // 使用 state 跟踪是否被点击

useFrame((state, delta) => {
if (meshRef.current) {
// 让它缓慢旋转
meshRef.current.rotation.x += delta * 0.5;
// 根据是否激活来调整 Y 轴旋转速度
meshRef.current.rotation.y += delta * (active ? 2 : 0.5);
}
});

return (
// 将 props 传递给 mesh,比如 position
setActive(!active)} // 点击事件
onPointerOver={(event) => setHover(true)} // 鼠标悬停事件
onPointerOut={(event) => setHover(false)}> {/ 鼠标移出事件 /}

{/ 根据悬停状态改变材质颜色 /}


);
}

function App() {
return (


  {/* 在 Canvas 中使用 Box 组件,并设置它的位置 */}
  {/* 注意:Three.js 使用 Y 轴朝上作为默认的上方向 */}
  <Box position={[0, 0, 0]} /> {/* 立方体默认在原点 */}

</Canvas>

);
}

export default App;
“`

保存文件。现在:

  • 立方体仍然旋转。
  • 将鼠标悬停在立方体上时,它会变成浅蓝色。
  • 点击立方体时,它会放大,并且 Y 轴旋转速度会加快。再次点击会恢复正常大小和旋转速度。

代码解释:

  • 我们引入了 useState Hook。
  • Box 组件内部,我们使用 useState 创建了 hoveredactive 状态变量来跟踪鼠标交互。
  • <mesh ... onClick={(event) => setActive(!active)} ...>:直接在 <mesh> 组件上添加 onClick prop。当用户点击 3D 对象时,这个函数就会被调用。event 对象包含与点击相关的信息,比如点击位置等(在这里我们不需要)。我们简单地切换 active 状态。
  • onPointerOveronPointerOut 事件类似,用于处理鼠标悬停和移出事件。它们更新 hovered 状态。
  • <mesh scale={active ? 1.5 : 1} ...>:通过 React 的 JSX 语法,我们可以根据 active 状态动态地设置 <mesh>scale prop。R3F 会自动将这个 prop 映射到 underlying Three.js Mesh 对象的 scale 属性。
  • <meshStandardMaterial color={hovered ? 'lightblue' : 'hotpink'} />:同样,我们根据 hovered 状态动态地设置材质的颜色 prop。
  • useFrame 中,我们根据 active 状态调整 Y 轴的旋转速度。
  • App 组件中,我们通过 position={[0, 0, 0]} prop 将立方体放在场景原点。

这个简单的例子展示了 R3F 如何让你以声明式、组件化、集成 React 状态管理的方式来构建和控制 3D 场景。你可以通过 Props 将数据传递给 3D 组件,使用 State 管理交互,使用 Hooks 实现动画和副作用。

进一步探索:R3F 生态系统和进阶

恭喜你!你已经成功创建了你的第一个 React Three Fiber 场景。但这仅仅是开始。R3F 生态系统非常丰富,还有很多强大的功能等待你去探索:

  1. @react-three/drei 的更多组件: 我们仅仅使用了 OrbitControls。Drei 还提供了:
    • 各种相机控制器(FlyControls, MapControls 等)。
    • 各种几何体(Sphere, Cone, Torus 等)。
    • 各种材质(MeshWobbleMaterial, MeshReflectorMaterial 等)。
    • 光照 helpers (DirectionalLightHelper, SpotLightHelper 等)。
    • Helpers(AxesHelper, GridHelper 等)。
    • 文本组件(Text, Text3D)。
    • 图像和视频纹理加载 (Image, VideoTexture)。
    • 后处理效果(需要 @react-three/postprocessing 配合)。
    • 性能优化工具 (AdaptiveDpr, AdaptiveEvents)。
    • 许多其他实用的组件和 Hooks。 强烈建议浏览 Drei 的文档和示例。
  2. 加载 3D 模型: 在现实世界的应用中,你通常会加载 Blender、Cinema 4D 等工具创建的 3D 模型(如 .gltf, .glb, .obj 格式)。你可以使用 @react-three/drei 提供的 useGLTFuseLoader Hook 来轻松加载这些模型。
  3. 纹理和材质: 学习如何加载和应用纹理(.jpg, .png 等图片文件)来为你的 3D 对象添加细节和真实感。探索 Three.js 和 R3F 提供的不同材质类型(MeshPhongMaterial, MeshLambertMaterial 等)及其属性。
  4. 复杂动画: 除了简单的旋转,你还可以实现更复杂的动画,比如骨骼动画、关键帧动画。可以结合 useFrame、React State/Props 或专门的动画库(如 @react-three/fiber/animated 或 Three.js 内置的 AnimationMixer)。
  5. 物理引擎集成: 如果你的场景需要物理模拟(如碰撞、重力),可以集成物理引擎库,例如 @react-three/rapier
  6. 后处理效果: 添加景深、泛光、色彩校正等效果,提升场景的视觉质量。可以使用 @react-three/postprocessing 库。
  7. 性能优化: 对于复杂的场景,性能是一个重要考虑因素。学习如何优化几何体、减少 Draw Calls、使用实例化 (InstancedMesh)、控制渲染频率等。
  8. TypeScript 支持: 如果你使用 TypeScript,R3F 和 Drei 都提供了很好的类型定义,可以帮助你编写更健壮的代码。

实践建议与技巧

  • 从小处着手: 刚开始时,不要试图构建过于复杂的场景。从简单的几何体、灯光开始,逐步增加功能。
  • 阅读文档和示例: React Three Fiber 和 Drei 都有很棒的文档和大量的示例。遇到问题时,查阅文档是最好的解决办法。Three.js 的文档也是必备参考。
  • 利用 @react-three/drei 再次强调,Drei 可以为你节省大量时间,充分利用它提供的现成组件。
  • 理解 Three.js 基础: 虽然 R3F 抽象了很多细节,但理解 Three.js 的核心概念(场景图、几何体、材质、相机、灯光、向量、矩阵、欧拉角、四元数等)对于深入开发仍然非常重要。当你在 R3F 中遇到问题时,往往需要回溯到 Three.js 的原理来寻找解决方案。
  • 使用 useRef 访问底层对象: 如果你需要对 Three.js 对象进行命令式操作(比如在 useFrame 中频繁更新其属性),请务必使用 useRef 获取引用,而不是依赖 React 的 State/Props 更新(后者会涉及更多的开销,不适合高频率的属性变化)。
  • 利用 React DevTools: 你可以使用 React DevTools 来检查你的 R3F 组件树,查看它们的 Props 和 State,这对于调试很有帮助。
  • Canvas Props: 花时间了解 <Canvas> 组件的各种 props,它们可以用来配置渲染器、相机、性能选项等。例如,dpr (设备像素比)、gl (WebGLRenderer options)、camera (相机初始位置和属性)、shadows 等。

总结

通过本文,我们详细介绍了 React Three Fiber 是什么,它如何解决了 Three.js 与 React 集成时的痛点,并带你一步步搭建了环境,理解了核心概念,并成功构建了一个包含灯光、交互和动画的旋转立方体场景。

你现在应该已经掌握了使用 React Three Fiber 构建 Web 3D 应用的基础:

  • 使用 <Canvas> 创建 3D 渲染环境。
  • 使用 JSX 声明 3D 场景图,利用 R3F 组件映射 Three.js 对象。
  • 使用 useFrame Hook 实现动画和逐帧更新。
  • 使用 useRef 获取底层 Three.js 对象引用进行直接操作。
  • 利用 React 的 State 和 Props 实现组件化和交互。
  • 利用 @react-three/drei 库简化开发。

这只是 Web 3D 世界的冰山一角。React Three Fiber 为你打开了一扇通往无限创意可能性的大门。无论是构建产品展示、数据可视化、游戏原型、还是沉浸式体验,R3F 都能成为你的强大工具。

现在,勇敢地去探索吧!尝试加载自己的 3D 模型,应用不同的材质,添加更复杂的光照,实现更精彩的动画和交互。随着你的实践和学习,你将能够利用 React 和 Three.js 的力量,在 Web 上创造出令人惊叹的 3D 体验。

祝你在 React Three Fiber 的世界中探索愉快!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部