关于 r3f-performance
面向 React Three Fiber 场景开发者的性能优化技能,涵盖绘制调用、几何体、资源加载、内存管理和性能分析。
主要功能
- 01通过实例化和合并减少绘制调用
- 02按距离使用细节层次
- 03延迟加载大型资源
- 04释放未使用的图形资源
- 05使用性能面板分析瓶颈
适用场景
- 01优化包含重复物体的场景
- 02改善大型模型加载时的卡顿
- 03排查帧率低或内存增长
- 04调整移动场景的渲染开销
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ bbeierle12/ skill-mcp-claude --skill r3f-performance安装后在 Claude Code 中输入/r3f-performance确认已加载。
附带文件_meta.json
详细介绍
R3F 性能优化
通过减少绘制调用、优化几何体、智能加载和性能分析来优化渲染性能。
快速开始
// Performance-optimized Canvas
<Canvas
dpr={[1, 2]} // Limit pixel ratio
performance={{ min: 0.5 }} // Adaptive performance
frameloop="demand" // Only render on change
gl={{
powerPreference: 'high-performance',
antialias: false // Disable for mobile
}}
>
<Suspense fallback={null}>
<Scene />
</Suspense>
</Canvas>
帧预算
目标为 60fps = 每帧 16.67ms。预算分配如下:
| 阶段 | 目标 | 说明 |
|---|---|---|
| JavaScript | < 4ms | useFrame 逻辑、状态更新 |
| GPU 渲染 | < 10ms | 绘制调用、着色器 |
| 合成 | < 2ms | 后期处理、叠加层 |
| 缓冲 | ~1ms | 安全余量 |
绘制调用优化
黄金法则
减少绘制调用 > 减少三角形数量
包含 100 个网格(每个网格 1000 个三角形)的场景,比包含 1 个网格(共 100,000 个三角形)的场景更慢。
减少绘制调用的技术
| 技术 | 绘制调用数 | 适用场景 |
|---|---|---|
| 实例化 | 每种独特网格 1 次 | 100 个以上相同对象 |
| 合并几何体 | 每个合并批次 1 次 | 静态场景部分 |
| 纹理图集 | 更少的材质 | 大量相似纹理 |
| LOD | 降低复杂度 | 大型/远距离对象 |
实例化(适用于相同网格)
// 10,000 cubes = 1 draw call
<instancedMesh args={[undefined, undefined, 10000]}>
<boxGeometry />
<meshStandardMaterial />
</instancedMesh>
几何体合并(静态场景)
import { useMemo } from 'react';
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils';
import * as THREE from 'three';
function MergedScene() {
const mergedGeometry = useMemo(() => {
const geometries: THREE.BufferGeometry[] = [];
// Create many positioned geometries
for (let i = 0; i < 100; i++) {
const geo = new THREE.BoxGeometry(1, 1, 1);
geo.translate(
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20,
(Math.random() - 0.5) * 20
);
geometries.push(geo);
}
return mergeGeometries(geometries);
}, []);
return (
<mesh geometry={mergedGeometry}>
<meshStandardMaterial />
</mesh>
);
}
细节层次(LOD)
根据摄像机距离切换几何体:
import { useMemo } from 'react';
import * as THREE from 'three';
function LODMesh() {
const lod = useMemo(() => {
const lodObject = new THREE.LOD();
// High detail (close)
const highGeo = new THREE.SphereGeometry(1, 64, 64);
const highMesh = new THREE.Mesh(highGeo, new THREE.MeshStandardMaterial({ color: 'red' }));
lodObject.addLevel(highMesh, 0);
// Medium detail
const medGeo = new THREE.SphereGeometry(1, 32, 32);
const medMesh = new THREE.Mesh(medGeo, new THREE.MeshStandardMaterial({ color: 'orange' }));
lodObject.addLevel(medMesh, 10);
// Low detail (far)
const lowGeo = new THREE.SphereGeometry(1, 8, 8);
const lowMesh = new THREE.Mesh(lowGeo, new THREE.MeshStandardMaterial({ color: 'green' }));
lodObject.addLevel(lowMesh, 30);
return lodObject;
}, []);
return <primitive object={lod} />;
}
Drei LOD 助手
import { Detailed } from '@react-three/drei';
function AdaptiveSphere() {
return (
<Detailed distances={[0, 10, 30]}>
{/* Close: high detail */}
<mesh>
<sphereGeometry args={[1, 64, 64]} />
<meshStandardMaterial />
</mesh>
{/* Medium distance */}
<mesh>
<sphereGeometry args={[1, 32, 32]} />
<meshStandardMaterial />
</mesh>
{/* Far: low detail */}
<mesh>
<sphereGeometry args={[1, 8, 8]} />
<meshStandardMaterial />
</mesh>
</Detailed>
);
}
视锥体剔除
摄像机视野之外的对象不会被渲染。此功能默认启用,但:
// Disable for objects that animate into view unpredictably
<mesh frustumCulled={false}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
// Force bounding sphere update for dynamic geometry
useEffect(() => {
geometry.computeBoundingSphere();
}, [geometry]);
自适应性能
R3F 的自适应性能系统会自动调整 DPR:
<Canvas
performance={{
min: 0.5, // Minimum DPR under stress
max: 1, // Maximum DPR
debounce: 200 // Debounce time for changes (ms)
}}
/>
手动控制性能
import { useThree } from '@react-three/fiber';
function PerformanceMonitor() {
const { performance } = useThree();
useFrame(() => {
// Check current performance
if (performance.current < 1) {
// System is under stress, reduce complexity
}
});
// Trigger performance drop
const triggerRegress = () => {
performance.regress(); // Temporarily lower DPR
};
}
延迟加载
使用 Suspense 拆分代码
import { Suspense, lazy } from 'react';
const HeavyModel = lazy(() => import('./HeavyModel'));
function Scene() {
return (
<Suspense fallback={<SimpleLoader />}>
<HeavyModel />
</Suspense>
);
}
渐进式加载
import { useGLTF } from '@react-three/drei';
function Model() {
// Preload in background
useGLTF.preload('/model.glb');
const { scene } = useGLTF('/model.glb');
return <primitive object={scene} />;
}
// Preload before component mounts
useEffect(() => {
useGLTF.preload('/next-model.glb');
}, []);
基于视图加载
import { useInView } from 'react-intersection-observer';
function LazySection() {
const { ref, inView } = useInView({
triggerOnce: true,
rootMargin: '200px' // Start loading 200px before visible
});
return (
<group ref={ref}>
{inView && <HeavyContent />}
</group>
);
}
内存管理
释放未使用的资源
// Manual disposal
useEffect(() => {
return () => {
geometry.dispose();
material.dispose();
texture.dispose();
};
}, []);
// Drei helper for GLTF
import { useGLTF } from '@react-three/drei';
useEffect(() => {
return () => {
useGLTF.clear('/model.glb');
};
}, []);
纹理优化
import { useTexture } from '@react-three/drei';
import * as THREE from 'three';
// Compress and optimize
const texture = useTexture('/texture.jpg', (tex) => {
tex.minFilter = THREE.LinearMipmapLinearFilter;
tex.generateMipmaps = true;
tex.anisotropy = 4; // Lower = faster, higher = sharper
});
// Use compressed formats (KTX2)
import { useKTX2 } from '@react-three/drei';
const texture = useKTX2('/texture.ktx2');
性能分析
Stats 面板
import { Stats } from '@react-three/drei';
<Canvas>
<Stats /> {/* FPS, MS, MB counters */}
<Scene />
</Canvas>
性能面板
import { Perf } from 'r3f-perf';
<Canvas>
<Perf
position="top-left"
showGraph // Show FPS graph
minimal={false} // Full or minimal view
/>
<Scene />
</Canvas>
手动性能分析
import { useThree } from '@react-three/fiber';
function ProfileInfo() {
const { gl } = useThree();
useEffect(() => {
const info = gl.info;
console.log({
drawCalls: info.render.calls,
triangles: info.render.triangles,
points: info.render.points,
lines: info.render.lines,
textures: info.memory.textures,
geometries: info.memory.geometries
});
});
return null;
}
帧时间测量
function FrameProfiler() {
const frameTimeRef = useRef<number[]>([]);
useFrame(() => {
const start = performance.now();
// ... your logic ...
const elapsed = performance.now() - start;
frameTimeRef.current.push(elapsed);
if (frameTimeRef.current.length > 60) {
const avg = frameTimeRef.current.reduce((a, b) => a + b) / 60;
console.log(`Avg frame time: ${avg.toFixed(2)}ms`);
frameTimeRef.current = [];
}
});
return null;
}
常见瓶颈
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| FPS 低,绘制调用多 | 网格过多 | 使用实例化、合并或 LOD |
| FPS 低,绘制调用少 | 着色器/材质开销大 | 简化着色器,使用开销更低的材质 |
| 加载时卡顿 | 资源过大 | 延迟加载、压缩、使用 LOD |
| 内存持续增长 | 未释放资源 | 卸载时释放资源 |
| 移动设备问题 | DPR 高、启用了抗锯齿 | 限制 DPR,禁用抗锯齿 |
优化清单
[ ] Draw calls < 100 for complex scenes
[ ] Instancing for repeated objects
[ ] LOD for large/distant objects
[ ] Geometry merged where possible
[ ] Textures compressed (KTX2/Basis)
[ ] DPR capped at 2
[ ] Lazy loading for heavy assets
[ ] Proper disposal on unmount
[ ] Frustum culling enabled
[ ] Shadows optimized or disabled
文件结构
r3f-performance/
├── SKILL.md
├── references/
│ ├── profiling-guide.md # Deep profiling techniques
│ ├── mobile-optimization.md # Mobile-specific tips
│ └── large-scenes.md # Handling massive scenes
└── scripts/
├── utils/
│ ├── lod-helper.ts # LOD setup utilities
│ ├── merge-helper.ts # Geometry merging
│ └── perf-monitor.ts # Performance monitoring
└── presets/
├── mobile.ts # Mobile-optimized Canvas config
└── desktop.ts # Desktop-optimized Canvas config
参考资料
references/profiling-guide.md— 使用浏览器 DevTools 进行深入性能分析references/mobile-optimization.md— 移动设备专项优化references/large-scenes.md— 处理包含 100k 个以上对象的场景
评论
支持 Markdown