THREE.js编辑器导出场景加载后显示异常求助
THREE.js加载GLTF导出场景材质异常问题排查
我正在开发一个THREE.js项目,将打包后的GLTF文件导入在线编辑器时显示完全正常,但在自身项目中加载导出的场景时出现两处材质问题:一种材质完全异常(丢失颜色等),另一种材质出现奇怪的黄色色调。
编辑器显示效果:
项目中显示效果:
我已尝试设置sRGB为true,并关闭ToneMapping,但显示效果仍不正常。
以下是应用代码:
import * as THREE from './build/three.module.js'; import Stats from './node_modules/three/examples/jsm/libs/stats.module.js'; import { GLTFLoader } from './node_modules/three/examples/jsm/loaders/GLTFLoader.js'; import { Octree } from './node_modules/three/examples/jsm/math/Octree.js'; import { OctreeHelper } from './node_modules/three/examples/jsm/helpers/OctreeHelper.js'; import { Capsule } from './node_modules/three/examples/jsm/math/Capsule.js'; import { GUI } from './node_modules/three/examples/jsm/libs/lil-gui.module.min.js'; const clock = new THREE.Clock(); const scene = new THREE.Scene(); scene.background = new THREE.Color( 0x88ccee ); const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.rotation.order = 'YXZ'; const container = document.getElementById( 'container' ); const renderer = new THREE.WebGLRenderer( { antialias: true, } ); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFShadowMap; renderer.outputEncoding = THREE.sRGBEncoding; renderer.textureEncoding = THREE.sRGBEncoding; renderer.toneMapping = THREE.NoToneMapping; container.appendChild( renderer.domElement ); const stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.top = '0px'; container.appendChild( stats.domElement ); const GRAVITY = 30; const STEPS_PER_FRAME = 5; const ray = new THREE.Raycaster(); const worldOctree = new Octree(); const playerCollider = new Capsule( new THREE.Vector3( 0, 0.15, 0 ), new THREE.Vector3( 0, 1.5, 0 ), 0.35 ); const playerVelocity = new THREE.Vector3(); const playerDirection = new THREE.Vector3(); let playerOnFloor = false; let mouseTime = 0; const keyStates = {}; const vector1 = new THREE.Vector3(); const vector2 = new THREE.Vector3(); const vector3 = new THREE.Vector3(); document.addEventListener( 'keydown', ( event ) => { keyStates[ event.code ] = true; } ); document.addEventListener( 'keyup', ( event ) => { keyStates[ event.code ] = false; } ); container.addEventListener( 'mousedown', () => { document.body.requestPointerLock(); mouseTime = performance.now(); } ); document.addEventListener( 'mouseup', () => { // if ( document.pointerLockElement !== null ) throwBall(); } ); document.body.addEventListener( 'mousemove', ( event ) => { if ( document.pointerLockElement === document.body ) { camera.rotation.y -= event.movementX / 500; camera.rotation.x -= event.movementY / 500; } } ); window.addEventListener( 'resize', onWindowResize ); function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } function playerCollisions() { const result = worldOctree.capsuleIntersect( playerCollider ); playerOnFloor = false; if ( result ) { playerOnFloor = result.normal.y > 0; if ( ! playerOnFloor ) { playerVelocity.addScaledVector( result.normal, - result.normal.dot( playerVelocity ) ); } playerCollider.translate( result.normal.multiplyScalar( result.depth ) ); } } function updatePlayer( deltaTime ) { let damping = Math.exp( - 4 * deltaTime ) - 1; if ( ! playerOnFloor ) { playerVelocity.y -= GRAVITY * deltaTime; // small air resistance damping *= 0.1; } playerVelocity.addScaledVector( playerVelocity, damping ); const deltaPosition = playerVelocity.clone().multiplyScalar( deltaTime ); playerCollider.translate( deltaPosition ); playerCollisions(); camera.position.copy( playerCollider.end ); } function getForwardVector() { camera.getWorldDirection( playerDirection ); playerDirection.y = 0; playerDirection.normalize(); return playerDirection; } function getSideVector() { camera.getWorldDirection( playerDirection ); playerDirection.y = 0; playerDirection.normalize(); playerDirection.cross( camera.up ); return playerDirection; } function controls( deltaTime ) { // gives a bit of air control const speedDelta = deltaTime * ( playerOnFloor ? 15 : 8 ); if ( keyStates[ 'KeyW' ] ) { playerVelocity.add( getForwardVector().multiplyScalar( speedDelta ) ); } if ( keyStates[ 'KeyS' ] ) { playerVelocity.add( getForwardVector().multiplyScalar( - speedDelta ) ); } if ( keyStates[ 'KeyA' ] ) { playerVelocity.add( getSideVector().multiplyScalar( - speedDelta ) ); } if ( keyStates[ 'KeyD' ] ) { playerVelocity.add( getSideVector().multiplyScalar( speedDelta ) ); } } const loader = new THREE.ObjectLoader(); loader.load( './scene-4.json', ( gallery ) => { scene.add( gallery ); gallery.position.set(0,0,3); worldOctree.fromGraphNode( gallery ); gallery.traverse( child => { if ( child.isMesh ) { child.castShadow = true; child.receiveShadow = true; if ( child.material.map ) { child.material.map.anisotropy = 4; } } } ); const helper = new OctreeHelper( worldOctree ); helper.visible = false; scene.add( helper ); const gui = new GUI( { width: 200 } ); gui.add( { debug: false }, 'debug' ) .onChange( function ( value ) { helper.visible = value; } ); animate(); } ); function teleportPlayerIfOob() { if ( camera.position.y <= - 25 ) { playerCollider.start.set( 0, 0.35, 0 ); playerCollider.end.set( 0, 1, 0 ); playerCollider.radius = 0.35; camera.position.copy( playerCollider.end ); camera.rotation.set( 0, 0, 0 ); } } function animate() { const deltaTime = Math.min( 0.05, clock.getDelta() ) / STEPS_PER_FRAME; // we look for collisions in substeps to mitigate the risk of // an object traversing another too quickly for detection. for ( let i = 0; i < STEPS_PER_FRAME; i ++ ) { controls( deltaTime ); updatePlayer( deltaTime ); teleportPlayerIfOob(); } renderer.render( scene, camera ); stats.update(); requestAnimationFrame( animate ); }
可能的原因及解决方案
加载器类型不匹配:你导入了
GLTFLoader但实际用ObjectLoader加载.json文件。ObjectLoader仅支持THREE.js原生JSON场景格式,GLTF模型必须用GLTFLoader加载,这是材质解析异常的核心原因。
修复代码:// 替换ObjectLoader为GLTFLoader const loader = new GLTFLoader(); loader.load( './你的模型文件.glb', (gltf) => { const gallery = gltf.scene; scene.add(gallery); // 后续场景处理逻辑不变 });材质编码未同步:即使设置了全局渲染器编码,模型材质的纹理编码可能未正确继承。遍历模型时强制设置纹理编码:
gallery.traverse( child => { if ( child.isMesh ) { // ...原有代码 if (child.material.map) { child.material.map.encoding = THREE.sRGBEncoding; child.material.needsUpdate = true; } } });缺少匹配的光照:在线编辑器通常默认配置了环境光和方向光,而你的项目中仅设置了阴影但无有效光源,导致材质颜色显示偏差。添加基础光照:
// 添加环境光 scene.add(new THREE.AmbientLight(0xffffff, 0.6)); // 添加方向光 const dirLight = new THREE.DirectionalLight(0xffffff, 0.8); dirLight.position.set(5, 10, 7.5); dirLight.castShadow = true; scene.add(dirLight);THREE.js版本差异:如果在线编辑器使用的THREE.js版本与你的项目不一致,可能存在材质解析的兼容性问题。尝试升级或降级项目的THREE.js版本,与编辑器版本对齐。
内容的提问来源于stack exchange,提问作者KhaosEmerald
相关产品推荐
相关产品推荐

