Expo React Native结合Three.js加载GLB模型时纹理无法显示的问题求助
Expo React Native结合Three.js加载GLB模型时纹理无法显示的问题求助
你遇到的错误Creating blobs from 'ArrayBuffer' and 'ArrayBufferView' are not supported是核心问题,我来帮你分析原因并给出具体的解决方法:
错误原因分析
这个错误是因为在Expo/React Native环境中,Three.js官方GLTFLoader的默认纹理加载逻辑依赖Web环境的Blob API,但这个API在React Native中是不被支持的。另外你直接使用Asset.fromModule返回的uri而没有等待Asset下载完成,也可能导致模型资源加载不完整,进一步加剧纹理加载失败的问题。
具体解决方法(推荐用方法一,简单可靠)
方法一:使用expo-three适配Expo环境
expo-three是Expo官方提供的Three.js适配库,它已经处理了React Native环境下的纹理加载、资源适配等问题,能直接解决你的Blob不支持问题。
- 首先安装依赖
expo install expo-three
- 修改你的
Character类代码
将原有的GLTFLoader替换为expo-three的loadAsync方法,同时确保先完成Asset的下载:
import { Group, AnimationMixer, Clock, AnimationAction } from 'three'; import { Asset } from 'expo-asset'; import { loadAsync } from 'expo-three'; // 导入expo-three的加载方法 class Character extends Group { private mixer: AnimationMixer | null = null; private clock: Clock = new Clock(); private actions: { [key: string]: AnimationAction } = {}; private currentAnimationName: string | null = null; private activeAction: AnimationAction | null = null; constructor() { super(); this.init(); // 调用异步初始化方法(constructor不能是async的) } private async init() { try { // 1. 先加载并下载模型Asset,确保资源完全可用 const modelAsset = Asset.fromModule(require(`./models/Soldier6.glb`)); await modelAsset.downloadAsync(); // 2. 使用expo-three的loadAsync加载GLTF模型,自动适配Expo纹理加载逻辑 const gltf = await loadAsync(modelAsset.uri); const modelWrapper = new Group(); const character = gltf.scene; modelWrapper.add(character); this.mixer = new AnimationMixer(character); // 存储所有动画到字典,方便后续调用 gltf.animations.forEach((clip) => { this.actions[clip.name] = this.mixer!.clipAction(clip); }); // 默认播放第一个动画 if (gltf.animations.length > 0) { this.setActiveAnimation(gltf.animations[0].name); } this.add(modelWrapper); console.log("Animations " + gltf.animations.map(anim => anim.name).join(', ')); } catch (error) { console.error('模型加载失败:', error); } } public setActiveAnimation(animationName: string) { if (!this.mixer || !this.actions[animationName]) { console.warn(`动画 "${animationName}" 不存在`); return; } if (this.currentAnimationName === animationName) return; const newAction = this.actions[animationName]; if (this.activeAction) { this.activeAction.fadeOut(0.2); } newAction.reset().fadeIn(0.2).play(); this.activeAction = newAction; this.currentAnimationName = animationName; } public update(delta: number) { if (this.mixer) { this.mixer.update(delta); } } } export default Character;
方法二:自定义GLTFLoader纹理加载器(不推荐,实现复杂)
如果你不想引入额外依赖,可以自定义纹理加载逻辑避免使用Blob,但这部分实现较繁琐:
你需要给GLTFLoader设置一个自定义的TextureLoader,手动处理ArrayBuffer到纹理的转换(比如用Expo FileSystem将ArrayBuffer保存为本地文件后再加载),但这种方法需要写很多适配代码,容易出问题,所以更推荐方法一。
额外注意事项
- 确保你的GLB模型纹理是正确嵌入的(meshyAi生成的模型通常默认嵌入纹理),如果是外部独立纹理,需要单独处理纹理的Asset路径。
- 在App主组件的渲染循环中,记得调用
character.update(clock.getDelta())来驱动动画混合器,比如:
// 主组件示例 import { Clock } from 'three'; import { GLView } from 'expo-gl'; import Character from './Character'; import { useRef, useEffect } from 'react'; function App() { const characterRef = useRef<Character>(null); const clock = new Clock(); useEffect(() => { const animationLoop = () => { const delta = clock.getDelta(); characterRef.current?.update(delta); requestAnimationFrame(animationLoop); }; animationLoop(); }, []); return ( <GLView style={{ flex: 1 }} onContextCreate={(gl) => { // 这里初始化Three.js场景、相机、渲染器,并将character添加到场景中 }} /> ); }
按照以上方法修改后,你的GLB模型纹理应该就能正常显示,动画也能正常播放了。




