r3f-loaders
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Three Fiber Loaders
React Three Fiber 加载器
Quick Start
快速开始
tsx
import { Canvas } from '@react-three/fiber'
import { useGLTF, OrbitControls } from '@react-three/drei'
import { Suspense } from 'react'
function Model() {
const { scene } = useGLTF('/models/robot.glb')
return <primitive object={scene} />
}
export default function App() {
return (
<Canvas>
<ambientLight />
<Suspense fallback={null}>
<Model />
</Suspense>
<OrbitControls />
</Canvas>
)
}tsx
import { Canvas } from '@react-three/fiber'
import { useGLTF, OrbitControls } from '@react-three/drei'
import { Suspense } from 'react'
function Model() {
const { scene } = useGLTF('/models/robot.glb')
return <primitive object={scene} />
}
export default function App() {
return (
<Canvas>
<ambientLight />
<Suspense fallback={null}>
<Model />
</Suspense>
<OrbitControls />
</Canvas>
)
}useGLTF (Drei)
useGLTF(Drei)
The recommended way to load GLTF/GLB models.
这是加载GLTF/GLB模型的推荐方式。
Basic Usage
基础用法
tsx
import { useGLTF } from '@react-three/drei'
function Model() {
const gltf = useGLTF('/models/robot.glb')
// gltf contains:
// - scene: THREE.Group (the main scene)
// - nodes: Object of named meshes
// - materials: Object of named materials
// - animations: Array of AnimationClip
return <primitive object={gltf.scene} />
}tsx
import { useGLTF } from '@react-three/drei'
function Model() {
const gltf = useGLTF('/models/robot.glb')
// gltf包含:
// - scene: THREE.Group(主场景)
// - nodes: 命名网格对象
// - materials: 命名材质对象
// - animations: AnimationClip数组
return <primitive object={gltf.scene} />
}Using Nodes and Materials
使用节点与材质
tsx
function Model() {
const { nodes, materials } = useGLTF('/models/robot.glb')
return (
<group>
{/* Use specific meshes */}
<mesh
geometry={nodes.Body.geometry}
material={materials.Metal}
position={[0, 0, 0]}
/>
<mesh
geometry={nodes.Head.geometry}
material={materials.Plastic}
position={[0, 1, 0]}
/>
</group>
)
}tsx
function Model() {
const { nodes, materials } = useGLTF('/models/robot.glb')
return (
<group>
{/* 使用特定网格 */}
<mesh
geometry={nodes.Body.geometry}
material={materials.Metal}
position={[0, 0, 0]}
/>
<mesh
geometry={nodes.Head.geometry}
material={materials.Plastic}
position={[0, 1, 0]}
/>
</group>
)
}With TypeScript (gltfjsx)
结合TypeScript(gltfjsx)
Generate typed components using gltfjsx:
bash
npx gltfjsx model.glb --typestsx
// Generated component
import { useGLTF } from '@react-three/drei'
import { GLTF } from 'three-stdlib'
type GLTFResult = GLTF & {
nodes: {
Body: THREE.Mesh
Head: THREE.Mesh
}
materials: {
Metal: THREE.MeshStandardMaterial
Plastic: THREE.MeshStandardMaterial
}
}
export function Model(props: JSX.IntrinsicElements['group']) {
const { nodes, materials } = useGLTF('/model.glb') as GLTFResult
return (
<group {...props} dispose={null}>
<mesh geometry={nodes.Body.geometry} material={materials.Metal} />
<mesh geometry={nodes.Head.geometry} material={materials.Plastic} />
</group>
)
}
useGLTF.preload('/model.glb')使用gltfjsx生成带类型的组件:
bash
npx gltfjsx model.glb --typestsx
// 生成的组件
import { useGLTF } from '@react-three/drei'
import { GLTF } from 'three-stdlib'
type GLTFResult = GLTF & {
nodes: {
Body: THREE.Mesh
Head: THREE.Mesh
}
materials: {
Metal: THREE.MeshStandardMaterial
Plastic: THREE.MeshStandardMaterial
}
}
export function Model(props: JSX.IntrinsicElements['group']) {
const { nodes, materials } = useGLTF('/model.glb') as GLTFResult
return (
<group {...props} dispose={null}>
<mesh geometry={nodes.Body.geometry} material={materials.Metal} />
<mesh geometry={nodes.Head.geometry} material={materials.Plastic} />
</group>
)
}
useGLTF.preload('/model.glb')Draco Compression
Draco压缩
tsx
import { useGLTF } from '@react-three/drei'
function Model() {
// Drei automatically handles Draco if the file is Draco-compressed
const { scene } = useGLTF('/models/compressed.glb')
return <primitive object={scene} />
}
// Or specify Draco decoder path
useGLTF.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/')tsx
import { useGLTF } from '@react-three/drei'
function Model() {
// 如果文件是Draco压缩的,Drei会自动处理
const { scene } = useGLTF('/models/compressed.glb')
return <primitive object={scene} />
}
// 或者指定Draco解码器路径
useGLTF.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/')Preloading
预加载
tsx
import { useGLTF } from '@react-three/drei'
// Preload at module level
useGLTF.preload('/models/robot.glb')
useGLTF.preload(['/model1.glb', '/model2.glb'])
function Model() {
// Will be instant if preloaded
const { scene } = useGLTF('/models/robot.glb')
return <primitive object={scene} />
}tsx
import { useGLTF } from '@react-three/drei'
// 在模块级别预加载
useGLTF.preload('/models/robot.glb')
useGLTF.preload(['/model1.glb', '/model2.glb'])
function Model() {
// 如果已预加载,会立即加载完成
const { scene } = useGLTF('/models/robot.glb')
return <primitive object={scene} />
}Processing Loaded Model
处理已加载的模型
tsx
function Model() {
const { scene } = useGLTF('/models/robot.glb')
useEffect(() => {
// Enable shadows on all meshes
scene.traverse((child) => {
if (child.isMesh) {
child.castShadow = true
child.receiveShadow = true
}
})
}, [scene])
return <primitive object={scene} />
}tsx
function Model() {
const { scene } = useGLTF('/models/robot.glb')
useEffect(() => {
// 为所有网格启用阴影
scene.traverse((child) => {
if (child.isMesh) {
child.castShadow = true
child.receiveShadow = true
}
})
}, [scene])
return <primitive object={scene} />
}useLoader (Core R3F)
useLoader(核心R3F)
For loading any Three.js asset.
用于加载任意Three.js资源。
Basic Texture Loading
基础纹理加载
tsx
import { useLoader } from '@react-three/fiber'
import { TextureLoader } from 'three'
function TexturedMesh() {
const texture = useLoader(TextureLoader, '/textures/color.jpg')
return (
<mesh>
<boxGeometry />
<meshStandardMaterial map={texture} />
</mesh>
)
}tsx
import { useLoader } from '@react-three/fiber'
import { TextureLoader } from 'three'
function TexturedMesh() {
const texture = useLoader(TextureLoader, '/textures/color.jpg')
return (
<mesh>
<boxGeometry />
<meshStandardMaterial map={texture} />
</mesh>
)
}Multiple Assets
多资源加载
tsx
function MultiTexture() {
const [colorMap, normalMap, roughnessMap] = useLoader(TextureLoader, [
'/textures/color.jpg',
'/textures/normal.jpg',
'/textures/roughness.jpg',
])
return (
<mesh>
<sphereGeometry args={[1, 64, 64]} />
<meshStandardMaterial
map={colorMap}
normalMap={normalMap}
roughnessMap={roughnessMap}
/>
</mesh>
)
}tsx
function MultiTexture() {
const [colorMap, normalMap, roughnessMap] = useLoader(TextureLoader, [
'/textures/color.jpg',
'/textures/normal.jpg',
'/textures/roughness.jpg',
])
return (
<mesh>
<sphereGeometry args={[1, 64, 64]} />
<meshStandardMaterial
map={colorMap}
normalMap={normalMap}
roughnessMap={roughnessMap}
/>
</mesh>
)
}With Extensions (Draco)
结合扩展(Draco)
tsx
import { useLoader } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
function Model() {
const gltf = useLoader(GLTFLoader, '/model.glb', (loader) => {
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/')
loader.setDRACOLoader(dracoLoader)
})
return <primitive object={gltf.scene} />
}tsx
import { useLoader } from '@react-three/fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
function Model() {
const gltf = useLoader(GLTFLoader, '/model.glb', (loader) => {
const dracoLoader = new DRACOLoader()
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/')
loader.setDRACOLoader(dracoLoader)
})
return <primitive object={gltf.scene} />
}Progress Callback
进度回调
tsx
function Model() {
const gltf = useLoader(
GLTFLoader,
'/model.glb',
undefined, // extensions
(progress) => {
console.log(`Loading: ${(progress.loaded / progress.total) * 100}%`)
}
)
return <primitive object={gltf.scene} />
}tsx
function Model() {
const gltf = useLoader(
GLTFLoader,
'/model.glb',
undefined, // 扩展
(progress) => {
console.log(`加载进度:${(progress.loaded / progress.total) * 100}%`)
}
)
return <primitive object={gltf.scene} />
}Preloading
预加载
tsx
import { useLoader } from '@react-three/fiber'
import { TextureLoader } from 'three'
// Preload
useLoader.preload(TextureLoader, '/textures/color.jpg')
useLoader.preload(TextureLoader, ['/tex1.jpg', '/tex2.jpg'])
// Clear cache
useLoader.clear(TextureLoader, '/textures/color.jpg')tsx
import { useLoader } from '@react-three/fiber'
import { TextureLoader } from 'three'
// 预加载
useLoader.preload(TextureLoader, '/textures/color.jpg')
useLoader.preload(TextureLoader, ['/tex1.jpg', '/tex2.jpg'])
// 清除缓存
useLoader.clear(TextureLoader, '/textures/color.jpg')Drei Loader Hooks
Drei加载器钩子
useTexture
useTexture
tsx
import { useTexture } from '@react-three/drei'
// Single
const texture = useTexture('/texture.jpg')
// Array
const [color, normal] = useTexture(['/color.jpg', '/normal.jpg'])
// Named object (spreads directly to material)
const textures = useTexture({
map: '/color.jpg',
normalMap: '/normal.jpg',
roughnessMap: '/roughness.jpg',
})
<meshStandardMaterial {...textures} />
// With callback for configuration
const texture = useTexture('/texture.jpg', (tex) => {
tex.wrapS = tex.wrapT = THREE.RepeatWrapping
tex.repeat.set(4, 4)
})
// Preload
useTexture.preload('/texture.jpg')tsx
import { useTexture } from '@react-three/drei'
// 单纹理
const texture = useTexture('/texture.jpg')
// 多纹理数组
const [color, normal] = useTexture(['/color.jpg', '/normal.jpg'])
// 命名对象(可直接扩展到材质)
const textures = useTexture({
map: '/color.jpg',
normalMap: '/normal.jpg',
roughnessMap: '/roughness.jpg',
})
<meshStandardMaterial {...textures} />
// 带配置回调
const texture = useTexture('/texture.jpg', (tex) => {
tex.wrapS = tex.wrapT = THREE.RepeatWrapping
tex.repeat.set(4, 4)
})
// 预加载
useTexture.preload('/texture.jpg')useCubeTexture
useCubeTexture
tsx
import { useCubeTexture } from '@react-three/drei'
function EnvMap() {
const envMap = useCubeTexture(
['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
{ path: '/textures/cube/' }
)
return (
<mesh>
<sphereGeometry />
<meshStandardMaterial envMap={envMap} metalness={1} roughness={0} />
</mesh>
)
}tsx
import { useCubeTexture } from '@react-three/drei'
function EnvMap() {
const envMap = useCubeTexture(
['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
{ path: '/textures/cube/' }
)
return (
<mesh>
<sphereGeometry />
<meshStandardMaterial envMap={envMap} metalness={1} roughness={0} />
</mesh>
)
}useEnvironment
useEnvironment
tsx
import { useEnvironment } from '@react-three/drei'
// Preset
const envMap = useEnvironment({ preset: 'sunset' })
// Presets: apartment, city, dawn, forest, lobby, night, park, studio, sunset, warehouse
// Custom HDR file
const envMap = useEnvironment({ files: '/hdri/studio.hdr' })
// Cube map
const envMap = useEnvironment({
files: ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
path: '/textures/',
})tsx
import { useEnvironment } from '@react-three/drei'
// 使用预设
const envMap = useEnvironment({ preset: 'sunset' })
// 预设选项:apartment, city, dawn, forest, lobby, night, park, studio, sunset, warehouse
// 自定义HDR文件
const envMap = useEnvironment({ files: '/hdri/studio.hdr' })
// 立方体贴图
const envMap = useEnvironment({
files: ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
path: '/textures/',
})useVideoTexture
useVideoTexture
tsx
import { useVideoTexture } from '@react-three/drei'
function VideoPlane() {
const texture = useVideoTexture('/video.mp4', {
start: true,
loop: true,
muted: true,
crossOrigin: 'anonymous',
})
return (
<mesh>
<planeGeometry args={[16/9 * 2, 2]} />
<meshBasicMaterial map={texture} toneMapped={false} />
</mesh>
)
}tsx
import { useVideoTexture } from '@react-three/drei'
function VideoPlane() {
const texture = useVideoTexture('/video.mp4', {
start: true,
loop: true,
muted: true,
crossOrigin: 'anonymous',
})
return (
<mesh>
<planeGeometry args={[16/9 * 2, 2]} />
<meshBasicMaterial map={texture} toneMapped={false} />
</mesh>
)
}useFont
useFont
tsx
import { useFont, Text3D } from '@react-three/drei'
// Preload font
useFont.preload('/fonts/helvetiker.json')
function Text() {
return (
<Text3D font="/fonts/helvetiker.json" size={1} height={0.2}>
Hello
<meshStandardMaterial color="gold" />
</Text3D>
)
}tsx
import { useFont, Text3D } from '@react-three/drei'
// 预加载字体
useFont.preload('/fonts/helvetiker.json')
function Text() {
return (
<Text3D font="/fonts/helvetiker.json" size={1} height={0.2}>
Hello
<meshStandardMaterial color="gold" />
</Text3D>
)
}Suspense Patterns
Suspense模式
Basic Suspense
基础Suspense用法
tsx
import { Suspense } from 'react'
function Scene() {
return (
<Canvas>
<Suspense fallback={<Loader />}>
<Model />
</Suspense>
</Canvas>
)
}
function Loader() {
return (
<mesh>
<boxGeometry />
<meshBasicMaterial color="gray" wireframe />
</mesh>
)
}tsx
import { Suspense } from 'react'
function Scene() {
return (
<Canvas>
<Suspense fallback={<Loader />}>
<Model />
</Suspense>
</Canvas>
)
}
function Loader() {
return (
<mesh>
<boxGeometry />
<meshBasicMaterial color="gray" wireframe />
</mesh>
)
}Loading Progress UI
加载进度UI
tsx
import { useProgress, Html } from '@react-three/drei'
function Loader() {
const { active, progress, errors, item, loaded, total } = useProgress()
return (
<Html center>
<div className="loader">
<div className="progress-bar">
<div style={{ width: `${progress}%` }} />
</div>
<p>{Math.round(progress)}% loaded</p>
<p>Loading: {item}</p>
</div>
</Html>
)
}
function App() {
return (
<Canvas>
<Suspense fallback={<Loader />}>
<Scene />
</Suspense>
</Canvas>
)
}tsx
import { useProgress, Html } from '@react-three/drei'
function Loader() {
const { active, progress, errors, item, loaded, total } = useProgress()
return (
<Html center>
<div className="loader">
<div className="progress-bar">
<div style={{ width: `${progress}%` }} />
</div>
<p>{Math.round(progress)}% 已加载</p>
<p>正在加载:{item}</p>
</div>
</Html>
)
}
function App() {
return (
<Canvas>
<Suspense fallback={<Loader />}>
<Scene />
</Suspense>
</Canvas>
)
}Drei Loader Component
Drei Loader组件
tsx
import { Loader } from '@react-three/drei'
function App() {
return (
<>
<Canvas>
<Suspense fallback={null}>
<Scene />
</Suspense>
</Canvas>
{/* HTML loading overlay */}
<Loader />
</>
)
}tsx
import { Loader } from '@react-three/drei'
function App() {
return (
<>
<Canvas>
<Suspense fallback={null}>
<Scene />
</Suspense>
</Canvas>
{/* HTML加载遮罩 */}
<Loader />
</>
)
}Other Model Formats
其他模型格式
OBJ + MTL
OBJ + MTL
tsx
import { useLoader } from '@react-three/fiber'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'
function OBJModel() {
const materials = useLoader(MTLLoader, '/model.mtl')
const obj = useLoader(OBJLoader, '/model.obj', (loader) => {
materials.preload()
loader.setMaterials(materials)
})
return <primitive object={obj} />
}tsx
import { useLoader } from '@react-three/fiber'
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'
function OBJModel() {
const materials = useLoader(MTLLoader, '/model.mtl')
const obj = useLoader(OBJLoader, '/model.obj', (loader) => {
materials.preload()
loader.setMaterials(materials)
})
return <primitive object={obj} />
}FBX
FBX
tsx
import { useFBX } from '@react-three/drei'
function FBXModel() {
const fbx = useFBX('/model.fbx')
return <primitive object={fbx} scale={0.01} />
}
// Preload
useFBX.preload('/model.fbx')tsx
import { useFBX } from '@react-three/drei'
function FBXModel() {
const fbx = useFBX('/model.fbx')
return <primitive object={fbx} scale={0.01} />
}
// 预加载
useFBX.preload('/model.fbx')STL
STL
tsx
import { useLoader } from '@react-three/fiber'
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
function STLModel() {
const geometry = useLoader(STLLoader, '/model.stl')
return (
<mesh geometry={geometry}>
<meshStandardMaterial color="gray" />
</mesh>
)
}tsx
import { useLoader } from '@react-three/fiber'
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader'
function STLModel() {
const geometry = useLoader(STLLoader, '/model.stl')
return (
<mesh geometry={geometry}>
<meshStandardMaterial color="gray" />
</mesh>
)
}PLY
PLY
tsx
import { useLoader } from '@react-three/fiber'
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader'
function PLYModel() {
const geometry = useLoader(PLYLoader, '/model.ply')
useEffect(() => {
geometry.computeVertexNormals()
}, [geometry])
return (
<mesh geometry={geometry}>
<meshStandardMaterial vertexColors />
</mesh>
)
}tsx
import { useLoader } from '@react-three/fiber'
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader'
function PLYModel() {
const geometry = useLoader(PLYLoader, '/model.ply')
useEffect(() => {
geometry.computeVertexNormals()
}, [geometry])
return (
<mesh geometry={geometry}>
<meshStandardMaterial vertexColors />
</mesh>
)
}Clone for Multiple Instances
克隆多实例
tsx
import { useGLTF, Clone } from '@react-three/drei'
function Trees() {
const { scene } = useGLTF('/models/tree.glb')
return (
<>
<Clone object={scene} position={[0, 0, 0]} />
<Clone object={scene} position={[5, 0, 0]} />
<Clone object={scene} position={[-5, 0, 0]} />
<Clone object={scene} position={[0, 0, 5]} scale={1.5} />
</>
)
}tsx
import { useGLTF, Clone } from '@react-three/drei'
function Trees() {
const { scene } = useGLTF('/models/tree.glb')
return (
<>
<Clone object={scene} position={[0, 0, 0]} />
<Clone object={scene} position={[5, 0, 0]} />
<Clone object={scene} position={[-5, 0, 0]} />
<Clone object={scene} position={[0, 0, 5]} scale={1.5} />
</>
)
}Error Handling
错误处理
tsx
import { useGLTF } from '@react-three/drei'
import { ErrorBoundary } from 'react-error-boundary'
function ModelWithErrorHandling() {
return (
<ErrorBoundary fallback={<FallbackModel />}>
<Suspense fallback={<Loader />}>
<Model />
</Suspense>
</ErrorBoundary>
)
}
function FallbackModel() {
return (
<mesh>
<boxGeometry />
<meshBasicMaterial color="red" wireframe />
</mesh>
)
}tsx
import { useGLTF } from '@react-three/drei'
import { ErrorBoundary } from 'react-error-boundary'
function ModelWithErrorHandling() {
return (
<ErrorBoundary fallback={<FallbackModel />}>
<Suspense fallback={<Loader />}>
<Model />
</Suspense>
</ErrorBoundary>
)
}
function FallbackModel() {
return (
<mesh>
<boxGeometry />
<meshBasicMaterial color="red" wireframe />
</mesh>
)
}Asset Caching
资源缓存
tsx
import { useGLTF, useTexture } from '@react-three/drei'
// Assets are automatically cached by URL
// Same URL = same asset instance
function Scene() {
// These all reference the same cached asset
const model1 = useGLTF('/model.glb')
const model2 = useGLTF('/model.glb')
const model3 = useGLTF('/model.glb')
// Clear cache if needed
useGLTF.clear('/model.glb')
}tsx
import { useGLTF, useTexture } from '@react-three/drei'
// 资源会自动按URL缓存
// 相同URL = 相同资源实例
function Scene() {
// 这些都引用同一个缓存的资源
const model1 = useGLTF('/model.glb')
const model2 = useGLTF('/model.glb')
const model3 = useGLTF('/model.glb')
// 必要时清除缓存
useGLTF.clear('/model.glb')
}Performance Tips
性能优化技巧
- Preload critical assets: Avoid loading during interaction
- Use Draco compression: Smaller file sizes
- Use LOD models: Different detail levels for distance
- Clone instead of reload: For multiple instances
- Lazy load non-critical: Load on demand
tsx
// Preload strategy
useGLTF.preload('/models/hero.glb') // Critical
useTexture.preload('/textures/main.jpg') // Critical
function LazyModel({ visible }) {
// Only load when visible
const { scene } = useGLTF(visible ? '/models/detail.glb' : null)
return scene ? <primitive object={scene} /> : null
}- 预加载关键资源:避免在交互过程中加载
- 使用Draco压缩:减小文件体积
- 使用LOD模型:根据距离使用不同细节层级
- 克隆而非重新加载:创建多实例时使用克隆
- 懒加载非关键资源:按需加载
tsx
// 预加载策略
useGLTF.preload('/models/hero.glb') // 关键资源
useTexture.preload('/textures/main.jpg') // 关键资源
function LazyModel({ visible }) {
// 仅在可见时加载
const { scene } = useGLTF(visible ? '/models/detail.glb' : null)
return scene ? <primitive object={scene} /> : null
}See Also
另请参阅
- - Playing loaded animations
r3f-animation - - Texture configuration
r3f-textures - - Materials from loaded models
r3f-materials
- - 播放加载的动画
r3f-animation - - 纹理配置
r3f-textures - - 从加载的模型中使用材质
r3f-materials