Three.js中如何为模型单个面添加VideoTexture,其余面用不同材质?
问题描述
我用Blender 4.2制作了包含一台电脑和两台显示器的模型,导入Three.js场景后,遍历找到名为screen-monitor-display的网格,给它设置VideoTexture时会覆盖整个网格。之后尝试给每个面加不同材质,但错误地重复赋值node.material,导致所有面都变成了MeshNormalMaterial,视频纹理失效。
需要实现:给屏幕对象的单个面添加VideoTexture,其余面设置不同材质。
解决方案
要给网格的单个面设置不同材质,需要通过材质数组+面索引分配的方式实现,具体步骤如下:
1. 准备材质数组
先创建两个材质:一个带VideoTexture的屏幕专用材质,一个用于其余面的基础材质:
const monitorScreenOne = document.getElementById( 'monitor-1-video' ); const videoTexture = new THREE.VideoTexture( monitorScreenOne ); monitorScreenOne.load(); monitorScreenOne.setAttribute('playsinline', true); // 材质数组:索引0对应屏幕面,索引1对应其余面 const materials = [ new THREE.MeshBasicMaterial({ map: videoTexture }), new THREE.MeshNormalMaterial() ];
2. 为目标面分配材质索引
根据模型使用的几何体类型(Geometry/BufferGeometry),给目标面设置对应的材质索引,再将材质数组赋值给网格:
const loader = new GLTFLoader(loadingManager); loader.load('resources/script/lol/test.gltf', (gltf) => { gltf.scene.traverse(function(node) { if(node.name === 'screen-monitor-display' && node.isMesh) { node.position.x = 10; // 处理旧版Geometry类型 if (node.geometry.isGeometry) { // 假设第1个面是屏幕显示面,根据你的模型实际索引调整 node.geometry.faces[0].materialIndex = 0; // 其余面统一用索引1的材质 for(let i = 1; i < node.geometry.faces.length; i++){ node.geometry.faces[i].materialIndex = 1; } // 通知Three.js更新几何体分组 node.geometry.groupsNeedUpdate = true; } // 处理新版BufferGeometry类型(GLTF导入默认用这个) else if (node.geometry.isBufferGeometry) { // 创建材质索引数组,默认全部用索引1的材质 const materialIndices = new Uint16Array(node.geometry.attributes.position.count / 3); materialIndices.fill(1); // 将目标面(这里取第1个面)的索引设为0 materialIndices[0] = 0; // 将索引数组添加为几何体属性 node.geometry.setAttribute('materialIndex', new THREE.BufferAttribute(materialIndices, 1)); } // 给网格应用材质数组 node.material = materials; } }); scene.add(gltf.scene); });
3. 关键注意点
- 确认目标面索引:需要在Blender中查看屏幕面的索引,或者在Three.js中打印几何体的面数据,确保设置的是正确的面。
- BufferGeometry兼容性:新版Three.js导入GLTF默认使用BufferGeometry,优先处理这种情况。
- 属性更新:修改材质索引后,必须触发几何体的更新通知,否则Three.js不会识别材质变化。
内容的提问来源于stack exchange,提问作者bccode
相关产品推荐
相关产品推荐

