THREE.js中InstancedMesh的setColorAt方法失效问题求助
Three.js InstancedMesh 单个实例变色失效问题修复
问题根源
- 材质未开启实例颜色支持:你使用的
MeshBasicMaterial默认只会读取自身的color属性,完全忽略InstancedMesh的instanceColor数据,必须手动开启实例颜色渲染开关。 - 未初始化实例颜色缓冲区:创建
InstancedMesh后,没有为每个实例分配初始颜色的缓冲区,导致setColorAt没有可修改的颜色数据容器。 - 事件监听重复绑定:原代码把鼠标事件监听放在
tick函数内,每帧都会重复绑定一次事件,触发逻辑时会执行多次,引发异常。
修复后的完整代码
let plane let intersects let color = new THREE.Color(); // 保存所有实例的初始黄色,用于重置颜色 const initialColor = new THREE.Color(0xffff00); const geometry = new THREE.BoxGeometry(1, 1, .1); function createInstancePlane(){ const material = new THREE.MeshBasicMaterial({ color: 0xffff00, transparent: true, opacity: 1, // 关键:开启实例颜色渲染支持 vertexColors: THREE.InstanceColor }); plane = new THREE.InstancedMesh(geometry, material, 11856); scene.add(plane); // 初始化实例颜色缓冲区:给每个实例设置初始黄色 const colorArray = new Float32Array(11856 * 3); for(let i = 0; i < 11856; i++){ initialColor.toArray(colorArray, i * 3); } plane.instanceColor = new THREE.InstancedBufferAttribute(colorArray, 3); const dummy = new THREE.Object3D(); let mm = 0; for (let i = 0; i < 152; i++) { for (let j = 0; j < 78; j++) { dummy.position.x = i - 76; dummy.position.y = j - 39; dummy.position.z = 0; dummy.updateMatrix(); plane.setMatrixAt(mm, dummy.matrix); mm++; } } // 更新实例矩阵生效 plane.instanceMatrix.needsUpdate = true; } createInstancePlane() const rayCaster = new THREE.Raycaster(); let mouse = new THREE.Vector2(); window.addEventListener("mousemove", (e) => { mouse.x = (e.clientX / window.innerWidth) * 2 - 1; mouse.y = -(e.clientY / window.innerHeight) * 2 + 1; }); // 记录上一个悬停的实例ID,用于重置颜色 let lastHoveredInstanceId = null; let funcWhileMouseMove = () => { rayCaster.setFromCamera(mouse, camera); intersects = rayCaster.intersectObject(plane); // 先把上一个悬停的方块恢复初始颜色 if(lastHoveredInstanceId !== null){ plane.setColorAt(lastHoveredInstanceId, initialColor); lastHoveredInstanceId = null; } for (let obj of intersects) { plane.setColorAt(obj.instanceId, color.set(0xff0000)); lastHoveredInstanceId = obj.instanceId; plane.instanceColor.needsUpdate = true; } }; // 把事件监听移到tick外面,避免重复绑定 window.addEventListener("mouseup", () => { window.removeEventListener("mousemove", funcWhileMouseMove); setTimeout(() => { plane.material.color.set("pink"); }, 1000); }); window.addEventListener("mousedown", () => { window.addEventListener("mousemove", funcWhileMouseMove); }); function tick() { stats.begin(); stats.end(); window.requestAnimationFrame(tick); } tick();
关键修复点说明
- 材质配置:给
MeshBasicMaterial添加vertexColors: THREE.InstanceColor,让材质能够读取并渲染实例颜色数据。 - 颜色缓冲区初始化:创建
InstancedBufferAttribute作为所有实例的初始颜色容器,没有这个容器,setColorAt就是空操作。 - 事件监听优化:将鼠标事件移出
tick函数,避免每帧重复绑定导致的多次触发问题。 - 颜色重置逻辑:记录上一个悬停的方块ID,鼠标移开时恢复其初始颜色,防止所有方块都变成红色。
内容的提问来源于stack exchange,提问作者Kamlesh
相关产品推荐
相关产品推荐

