You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

THREE.js中InstancedMesh的setColorAt方法失效问题求助

Three.js InstancedMesh 单个实例变色失效问题修复

问题根源

  1. 材质未开启实例颜色支持:你使用的MeshBasicMaterial默认只会读取自身的color属性,完全忽略InstancedMesh的instanceColor数据,必须手动开启实例颜色渲染开关。
  2. 未初始化实例颜色缓冲区:创建InstancedMesh后,没有为每个实例分配初始颜色的缓冲区,导致setColorAt没有可修改的颜色数据容器。
  3. 事件监听重复绑定:原代码把鼠标事件监听放在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 08:05:26