如何在Three.js中通过下拉选项修改gITF/glb/fbx等3D模型的墙面纹理
Three.js实现点击网格替换下拉菜单选择的纹理
核心实现分为三个关键环节:射线检测选中目标网格、预加载纹理并监听下拉选择、替换纹理并更新材质
1. 用Raycaster实现网格点击检测
Three.js没有原生点击检测能力,需要通过射线投射判断点击位置与场景中网格的交集:
const raycaster = new THREE.Raycaster(); const mouse = new THREE.Vector2(); let selectedMesh = null; // 存储选中的目标网格 // 实时更新鼠标的归一化设备坐标 window.addEventListener('mousemove', (e) => { mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; }); // 处理点击事件,选中目标网格 window.addEventListener('click', () => { raycaster.setFromCamera(mouse, camera); // 递归检测场景所有子物体,确保能选中嵌套模型里的网格 const intersects = raycaster.intersectObjects(scene.children, true); if (intersects.length > 0) { // 重置之前选中网格的状态(比如高亮) if (selectedMesh) { selectedMesh.material.emissive.set(0x000000); } // 选中最靠前的网格 selectedMesh = intersects[0].object; // 可选:给选中网格加高亮反馈 selectedMesh.material.emissive.set(0x333333); } else { // 未选中时清空状态 if (selectedMesh) { selectedMesh.material.emissive.set(0x000000); selectedMesh = null; } } });
2. 处理下拉菜单的纹理选择与预加载
先编写HTML下拉菜单,再预加载纹理并绑定选择事件:
HTML部分
<select id="textureSelector"> <option value="wood">木纹纹理</option> <option value="metal">金属纹理</option> <option value="fabric">织物纹理</option> </select>
JS部分
const textureLoader = new THREE.TextureLoader(); const textureCache = new Map(); // 缓存加载好的纹理,避免重复请求 // 预加载纹理,替换为你的实际纹理路径 textureCache.set('wood', textureLoader.load('/assets/textures/wood.jpg')); textureCache.set('metal', textureLoader.load('/assets/textures/metal.png')); textureCache.set('fabric', textureLoader.load('/assets/textures/fabric.webp')); // 监听下拉菜单选择事件 document.getElementById('textureSelector').addEventListener('change', (e) => { const selectedTexture = textureCache.get(e.target.value); if (!selectedMesh || !selectedTexture) return; // 注意:如果模型材质是共享的,先克隆材质再修改,避免影响其他网格 if (selectedMesh.material.isMeshStandardMaterial || selectedMesh.material.isMeshBasicMaterial) { // 克隆材质(可选,根据你的模型材质是否共享决定) // selectedMesh.material = selectedMesh.material.clone(); selectedMesh.material.map = selectedTexture; selectedMesh.material.needsUpdate = true; // 必须设置,触发Three.js材质更新 } });
3. 关键注意事项
- 材质共享问题:从glTF/FBX/glb加载的模型,多个网格可能共用同一材质,直接修改会导致所有用该材质的网格都变纹理。解决办法是在替换前克隆材质:
selectedMesh.material = selectedMesh.material.clone(); - 异步纹理加载:如果纹理加载慢,可以用回调确保加载完成后再替换:
textureLoader.load('/assets/textures/wood.jpg', (texture) => { textureCache.set('wood', texture); // 加载完成后可触发替换逻辑 }); - 目标网格筛选:如果只想让特定网格可被选中,给目标网格加自定义标记:
然后在点击检测时判断:// 加载模型后,给目标网格加标记 targetMesh.userData.isEditable = true;const targetIntersect = intersects.find(intersect => intersect.object.userData.isEditable); if (targetIntersect) { selectedMesh = targetIntersect.object; // ...后续逻辑 } - 触摸设备适配:监听
touchstart事件,获取触摸坐标替换鼠标坐标即可,逻辑和点击一致。
内容的提问来源于stack exchange,提问作者Gaurav Kaushal
相关产品推荐
相关产品推荐

