1. R3F 简介:声明式 Three.js
React Three Fiber(R3F) 是 Three.js 的 React 渲染器。它把 Three.js 对象映射为 React 组件,让你用 JSX 声明式地描述 3D 场景,享受 React 的状态管理、组件复用和生态系统。
原生 Three.js
const geo = new THREE.BoxGeometry();
const mat = new THREE.MeshStandardMaterial();
const mesh = new THREE.Mesh(geo, mat);
mesh.position.x = 2;
scene.add(mesh);
React Three Fiber (JSX)
<mesh position-x={2}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
2. 安装
# 创建 Vite + React 项目
npm create vite@latest my-r3f-app -- --template react
cd my-r3f-app
# 安装 R3F 核心库
npm install three @react-three/fiber
# 安装 Drei(R3F 的工具组件库)
npm install @react-three/drei
# TypeScript 类型(推荐)
npm install --save-dev @types/three
3. Canvas 与基础场景
Canvas 是 R3F 的根组件,它创建 Three.js 的 WebGLRenderer 和渲染循环,内部的所有组件都是 Three.js 对象的 JSX 表示:
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
// Three.js 对象名转为 camelCase 即为 JSX 组件名
// new THREE.BoxGeometry() → <boxGeometry />
// new THREE.MeshStandardMaterial() → <meshStandardMaterial />
// new THREE.DirectionalLight() → <directionalLight />
function Scene() {
return (
<>
{/* 灯光 */}
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} intensity={2} />
{/* 立方体 */}
<mesh position={[0, 0, 0]}>
<boxGeometry args={[1, 1, 1]} /> {/* args = 构造函数参数 */}
<meshStandardMaterial color="#049EF4" metalness={0.8} roughness={0.2} />
</mesh>
{/* 控制器 */}
<OrbitControls enableDamping />
</>
);
}
export default function App() {
return (
<Canvas
camera={{ position: [0, 0, 5], fov: 75 }}
shadows
style={{ width: '100vw', height: '100vh' }}
>
<Scene />
</Canvas>
);
}
4. useFrame — 动画 Hook
useFrame 是 R3F 最核心的 Hook,在每一帧(requestAnimationFrame)调用,用于实现动画、物理更新等逻辑:
import { useRef } from 'react';
import { useFrame } from '@react-three/fiber';
function RotatingBox() {
const meshRef = useRef();
// state: { clock, camera, scene, gl, ... }
// delta: 上帧到这帧的时间差(秒)
useFrame((state, delta) => {
meshRef.current.rotation.x += delta * 0.5;
meshRef.current.rotation.y += delta * 0.8;
// 用时间驱动动画(如悬浮)
meshRef.current.position.y = Math.sin(state.clock.elapsedTime) * 0.3;
});
return (
<mesh ref={meshRef}>
<boxGeometry />
<meshStandardMaterial color="#049EF4" />
</mesh>
);
}
// 交互式组件:点击变色
function InteractiveSphere() {
const [hovered, setHovered] = useState(false);
const [clicked, setClicked] = useState(false);
return (
<mesh
onClick={() => setClicked(c => !c)}
onPointerOver={() => setHovered(true)}
onPointerOut={() => setHovered(false)}
>
<sphereGeometry args={[1, 32, 16]} />
<meshStandardMaterial
color={clicked ? '#ff4444' : hovered ? '#88ccff' : '#049EF4'}
roughness={0.2} metalness={0.8}
/>
</mesh>
);
}
5. useThree — 访问 Three.js 内部对象
import { useThree } from '@react-three/fiber';
function SceneSetup() {
const { gl, scene, camera, size, viewport } = useThree();
// gl — WebGLRenderer
// scene — THREE.Scene
// camera — 当前相机
// size — canvas 的像素尺寸 { width, height }
// viewport — 视口世界单位尺寸 { width, height }
useEffect(() => {
// 访问底层 renderer 设置
gl.shadowMap.enabled = true;
gl.toneMapping = THREE.ACESFilmicToneMapping;
// 加载 HDR 环境贴图
const rgbeLoader = new RGBELoader();
rgbeLoader.load('/env.hdr', (tex) => {
tex.mapping = THREE.EquirectangularReflectionMapping;
scene.environment = tex;
scene.background = tex;
});
}, [gl, scene]);
return null;
}
6. Drei 常用组件
@react-three/drei 是 R3F 的工具库,提供大量预制组件,大幅提升开发效率:
import {
OrbitControls, Stars, Text, Environment,
useGLTF, MeshReflectorMaterial, Float,
Center, useTexture, Html
} from '@react-three/drei';
// Stars — 直接使用内置星空(无需手写粒子系统)
<Stars
radius={100}
depth={50}
count={5000}
factor={4}
saturation={0}
fade
/>
// Text — 3D 文字(内置 SDF 字体渲染)
<Text
fontSize={0.5}
color="#049EF4"
anchorX="center"
anchorY="middle"
>
Hello 3D World!
</Text>
// Environment — 一行代码加载 HDR 环境贴图
<Environment preset="sunset" />
{/* 可选 preset: sunset/dawn/night/warehouse/forest/apartment/studio/city/park/lobby */}
// Float — 悬浮动画包装器
<Float speed={2} rotationIntensity={0.5} floatIntensity={1}>
<mesh>...</mesh>
</Float>
// useGLTF — 加载 GLTF 模型的 Hook
function Model() {
const { scene, animations } = useGLTF('/models/helmet.glb');
return <primitive object={scene} />;
}
// 预加载(防止首次加载卡顿)
useGLTF.preload('/models/helmet.glb');
// Html — 在 3D 场景中放置 HTML(如标签、UI)
<Html position={[0, 1.5, 0]} center>
<div className="label">Click me!</div>
</Html>
// MeshReflectorMaterial — 地面反射材质
<mesh rotation-x={-Math.PI / 2}>
<planeGeometry args={[20, 20]} />
<MeshReflectorMaterial
blur={[300, 100]}
resolution={1024}
mixBlur={1}
mixStrength={15}
roughness={1}
mirror={0}
/>
</mesh>
7. 性能优化:Suspense 懒加载
模型加载是异步操作,用 React Suspense 优雅处理加载状态:
import { Suspense } from 'react';
import { Html, useProgress } from '@react-three/drei';
// 加载进度组件
function Loader() {
const { progress } = useProgress();
return (
<Html center>
<div style={{ color: 'white' }}>
加载中... {progress.toFixed(0)}%
</div>
</Html>
);
}
export default function App() {
return (
<Canvas shadows camera={{ position: [0, 2, 8], fov: 50 }}>
<ambientLight intensity={0.5} />
<directionalLight position={[5, 5, 5]} castShadow />
<Environment preset="studio" />
<Suspense fallback={<Loader />}>
<Float speed={1.5}>
<Model />
</Float>
<Stars count={5000} fade />
</Suspense>
<OrbitControls enableDamping />
</Canvas>
);
}
8. R3F 与原生 Three.js 的对比
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| React 应用中的 3D 组件 | React Three Fiber | 与 React 状态/路由完美集成,组件可复用 |
| 独立 3D 网站/游戏 | 原生 Three.js | 无 React 开销,完全控制渲染管线 |
| 3D 产品展示页 | R3F + Drei | Drei 的 Environment/Float 等极大简化代码 |
| 游戏引擎级别应用 | 原生 Three.js | 精细控制每一帧,避免 React reconciliation 开销 |
| 数据可视化 3D 图表 | R3F | 与 React 数据流天然契合 |
R3F 与 Three.js 版本对应:R3F v8 对应 Three.js r140+,使用 `@react-three/fiber@8` 和 `three@latest` 即可。Drei 的版本需要与 R3F 主版本匹配,查看 Drei 的 README 确认兼容版本。
全教程总结:你已经完成了 Three.js 从基础到进阶的完整旅程!从 WebGL 原理到 Scene/Camera/Renderer 三要素,从 PBR 材质到 GLSL 着色器,从粒子系统到 React Three Fiber 声明式 3D——现在你具备了在浏览器中构建任何 3D 世界的能力。下一步:尝试结合本教程的知识,构建一个完整的 3D 作品集或产品展示页面。