React Native+Expo+Three.js模型纹理未包裹且渲染黑屏问题排查
Expo + Three.js React Native 3D模型黑屏&纹理失效问题解决
问题描述
使用Expo和Three.js在React Native应用中渲染3D模型时,模型已加载但完全黑屏,纹理无法正确包裹。已尝试以下操作但未解决:
- 提升环境光与平行光强度
- 更换材质类型(MeshBasicMaterial、MeshStandardMaterial)
- 检查纹理加载状态
- 验证.obj和.mtl文件格式
相关代码:
import React, { useState, useRef } from "react"; import { View } from "react-native"; import { GLView } from "expo-gl"; import { Renderer } from "expo-three"; import * as THREE from "three"; import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader"; import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader"; import { Asset } from "expo-asset"; const ThreeJSModelViewer: React.FC = () => { const [modelLoaded, setModelLoaded] = useState(false); const [error, setError] = useState<string | null>(null); const objectRef = useRef<THREE.Object3D | null>(null); const handleContextCreate = async (gl: any) => { const { drawingBufferWidth: width, drawingBufferHeight: height } = gl; const renderer = new Renderer({ gl }); renderer.setSize(width, height); renderer.setClearColor("#ffffff", 0); const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000); camera.position.z = 7; const ambientLight = new THREE.AmbientLight(0xffffff, 0.8); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.position.set(1, 1, 1).normalize(); scene.add(directionalLight); try { const objAsset = Asset.fromModule(require("../../assets/temp-obj/ibraheem.obj")); const mtlAsset = Asset.fromModule(require("../../assets/temp-obj/ibraheem.mtl")); const textureAsset = Asset.fromModule(require("../../assets/temp-obj/ibraheem.png")); await Promise.all([objAsset.downloadAsync(), mtlAsset.downloadAsync(), textureAsset.downloadAsync()]); const mtlLoader = new MTLLoader(); const materials = await mtlLoader.loadAsync(mtlAsset.uri); materials.preload(); const objLoader = new OBJLoader(); objLoader.setMaterials(materials); const object = await objLoader.loadAsync(objAsset.uri); const textureLoader = new THREE.TextureLoader(); const texture: THREE.Texture = await new Promise((resolve, reject) => { textureLoader.load( textureAsset.uri, (loadedTexture) => resolve(loadedTexture), undefined, (error) => reject(error) ); }); object.traverse((child) => { if (child instanceof THREE.Mesh) { child.material = new THREE.MeshPhongMaterial({ map: texture, specular: new THREE.Color(0x111111), shininess: 200, }); } }); // Resize and position the object const resizeObject = (obj: THREE.Object3D) => { const box = new THREE.Box3().setFromObject(obj); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y, size.z); const scale = 5 / maxDim; obj.scale.multiplyScalar(scale); box.setFromObject(obj); box.getCenter(obj.position); obj.position.multiplyScalar(-1); }; resizeObject(object); scene.add(object); setModelLoaded(true); objectRef.current = object; const animate = () => { requestAnimationFrame(animate); if (objectRef.current) { objectRef.current.rotation.y += 0.01; } renderer.render(scene, camera); gl.endFrameEXP(); }; animate(); } catch (error) { console.error("Error loading 3D model:", error); setError("Failed to load 3D model."); } }; return ( <View style={{ flex: 1 }}> <GLView style={{ flex: 1 }} onContextCreate={handleContextCreate} /> {/* ... (other UI components) */} </View> ); }; export default ThreeJSModelViewer;
.mtl文件内容:
newmtl FaceTexture map_Kd ibraheem.png
解决方案
以下是几个关键修复点,逐一尝试:
1. 修复纹理加载设置
iOS/Android平台下,Three.js纹理默认的flipY属性可能导致纹理显示异常,同时需要确保纹理的UV映射模式正确:
const texture: THREE.Texture = await new Promise((resolve, reject) => { textureLoader.load( textureAsset.uri, (loadedTexture) => { // 修复纹理翻转问题 loadedTexture.flipY = false; // 设置纹理包裹模式(适配UV坐标) loadedTexture.wrapS = THREE.RepeatWrapping; loadedTexture.wrapT = THREE.RepeatWrapping; resolve(loadedTexture); }, undefined, (error) => reject(error) ); });
2. 禁用背面剔除或设置双面渲染
如果模型的面法线方向错误,默认的背面剔除会导致模型不可见,添加双面渲染:
object.traverse((child) => { if (child instanceof THREE.Mesh) { child.material = new THREE.MeshPhongMaterial({ map: texture, specular: new THREE.Color(0x111111), shininess: 200, // 开启双面渲染 side: THREE.DoubleSide }); } });
3. 确保相机正确看向模型中心
调整模型位置后,需要让相机聚焦到模型中心:
resizeObject(object); scene.add(object); // 让相机看向模型中心 camera.lookAt(object.position); setModelLoaded(true);
4. 修正渲染器清屏设置
当前设置renderer.setClearColor("#ffffff", 0)会让背景完全透明,若模型仍黑屏,可临时将alpha值改为1,确认是否是模型本身不可见:
renderer.setClearColor("#ffffff", 1);
5. 处理MTLLoader的纹理路径问题
MTLLoader默认会从.mtl文件所在路径加载纹理,但Expo Asset的URI是独立的,可手动为材质指定纹理路径:
const mtlLoader = new MTLLoader(); // 设置纹理加载器的资源路径 mtlLoader.setTexturePath(textureAsset.uri.replace('ibraheem.png', '')); const materials = await mtlLoader.loadAsync(mtlAsset.uri); materials.preload();
6. 添加像素比适配
为Expo Renderer添加像素比适配,避免渲染模糊或异常:
import { Platform } from 'react-native'; // ... const renderer = new Renderer({ gl }); renderer.setSize(width, height); renderer.setPixelRatio(Platform.OS === 'ios' ? 2 : 1); renderer.setClearColor("#ffffff", 1);
内容的提问来源于stack exchange,提问作者Tauseef Razaq
相关产品推荐
相关产品推荐

