React-three-fiber模型性能优化咨询:FPS骤降问题
React-Three-Fiber 模型集成导航栏后的性能优化方案
问题背景
将Circle模型(压缩后5MB,未压缩50MB)及CirclesLight灯光组件集成到导航栏NavItem后,页面FPS从120骤降至30,核心性能瓶颈集中在模型重复渲染、灯光数量过载、不必要的渲染开销三个方面,以下是针对性优化方案:
一、模型资源与实例化优化
1. 用InstancedMesh复用多实例模型
当前id=3、4的NavItem中重复渲染多个Circle组件,每个组件都是独立Mesh,会产生大量Draw Call。改用InstancedMesh可将多个相同模型合并为一次渲染,大幅降低GPU负载:
修改Circle组件为支持实例化的版本:
import { useGLTF } from '@react-three/drei' import React, { FC, useRef, useEffect } from 'react' import * as THREE from 'three' import { GLTF } from 'three-stdlib' type GLTFResult = GLTF & { nodes: { Sphere002: THREE.Mesh } materials: { ['Material.004']: THREE.MeshStandardMaterial } } // 全局缓存几何体和材质,避免重复加载实例 const { nodes, materials } = useGLTF('/models/circle/circle.glb') as GLTFResult const geometry = nodes.Sphere002.geometry const material = materials['Material.004'] // 支持多实例渲染的Circle组件 const CircleInstance: FC<{ instances: Array<{ position: THREE.Vector3, rotation: THREE.Euler, scale: THREE.Vector3 }> }> = ({ instances }) => { const meshRef = useRef<THREE.InstancedMesh>(null) // 初始化实例矩阵 useEffect(() => { if (!meshRef.current) return const mesh = meshRef.current mesh.count = instances.length instances.forEach((inst, index) => { const matrix = new THREE.Matrix4() matrix.compose(inst.position, inst.rotation, inst.scale) mesh.setMatrixAt(index, matrix) }) mesh.instanceMatrix.needsUpdate = true }, [instances]) return ( <instancedMesh ref={meshRef} geometry={geometry} material={material} count={0} frustumCulling // 开启视锥体剔除 /> ) } useGLTF.preload('/models/circle/circle.glb') export default CircleInstance
在NavItem中替换使用:
// id=3的模型部分替换为 <CircleInstance instances={[ { position: new THREE.Vector3(2.81, -3.92, 0.0), rotation: new THREE.Euler(0.0, 0, 0.0), scale: new THREE.Vector3(6.7, 6.7, 6.7) }, { position: new THREE.Vector3(4.94, -4.86, 0.0), rotation: new THREE.Euler(0.0, 6.13, 0.0), scale: new THREE.Vector3(8.88, 8.88, 8.88) } ]} />
2. 进一步压缩与简化模型
- 用Blender对模型做减面处理:保留视觉效果的前提下,用Decimate修改器降低多边形数量(比如减面50%)。
- 使用Draco压缩:将模型转为带Draco压缩的glb格式,可将体积压缩至1MB以内,同时减少GPU加载压力。
二、灯光系统优化
1. 合并冗余灯光,减少数量
当前CirclesLight包含13个灯光,且NavItem中多次重复渲染该组件,灯光数量翻倍。合并同类型、同颜色的灯光,降低光照计算开销:
修改CirclesLight组件:
import { FC } from 'react' const CirclesLight: FC = () => { return ( <> {/* 合并4个橙色平行光,调整总强度保持视觉一致 */} <directionalLight intensity={160} position={[0, 1.5, 0]} color={'#FF5406'} /> {/* 保留蓝色平行光 */} <directionalLight intensity={40} position={[1.568, 2.731, -4.679]} color='#2300ff' /> {/* 合并4个蓝色点光 */} <pointLight intensity={160} decay={2} color='#2300ff' position={[0, 1, -1]} /> {/* 合并5个浅白色点光 */} <pointLight intensity={200} decay={2} color='#fbffeb' position={[-0.5, 1.5, -1]} /> </> ) } export default CirclesLight
2. 仅在激活状态下渲染灯光
修改NavItem逻辑,仅当前激活的NavItem才渲染灯光:
// id=3的灯光部分修改为 {activeId === id && <CirclesLight />}
三、渲染可见性与生命周期优化
1. 非激活状态下停止渲染模型
当前非激活的NavItem中,模型仅位置偏移但仍在后台渲染,添加条件渲染逻辑:
// NavItem中模型区块的条件判断修改为 {(activeId === id) ? ( <Suspense fallback={null}> {/* 模型和灯光 */} </Suspense> ) : null}
2. 缓存Three.js对象,避免重复创建
每次render时创建新的Euler和Vector3会导致react-three-fiber频繁更新组件,用useMemo缓存:
import { useMemo } from 'react' // 在NavItem组件内添加 const id3Instances = useMemo(() => [ { position: new THREE.Vector3(2.81, -3.92, 0.0), rotation: new THREE.Euler(0.0, 0, 0.0), scale: new THREE.Vector3(6.7, 6.7, 6.7) }, { position: new THREE.Vector3(4.94, -4.86, 0.0), rotation: new THREE.Euler(0.0, 6.13, 0.0), scale: new THREE.Vector3(8.88, 8.88, 8.88) } ], [])
四、其他优化点
- 替换材质类型:如果模型不需要PBR光照效果,改用
MeshLambertMaterial或MeshBasicMaterial替代MeshStandardMaterial,减少光照计算开销。 - 使用
Simplify组件自动简化模型:import { Simplify } from '@react-three/drei' <Simplify amount={0.5}> <CircleInstance instances={id3Instances} /> </Simplify>
内容的提问来源于stack exchange,提问作者Nik
相关产品推荐
相关产品推荐

