Raycast Highlight with InstancedMesh

You need an additional buffer attribute for storing original colors of instances.

  var instanceColorsBase = new Float32Array(instanceColors.length);
  instanceColorsBase.set(instanceColors);
  geometry.setAttribute( 'instanceColor', new THREE.InstancedBufferAttribute( new Float32Array( instanceColors ), 3 ) );
  geometry.setAttribute( 'instanceColorBase', new THREE.BufferAttribute(new Float32Array( instanceColorsBase ), 3 ) );

instanceColorBase is the attribute you restore instanceColor from.

Also you need to remember instanceId of previous raycast, so it’s prevInstanceId.
And a color of highlighting: var highlightColor = new THREE.Color("yellow");

Then, when you check intersection:

    if (instanceId != prevInstanceId) {
      setInstanceColor(instanceId, true);
      setInstanceColor(prevInstanceId, false);
      prevInstanceId = instanceId;
    }

Where setInstanceColor() is:

function setInstanceColor(instanceId, isHighlighting){
  if (instanceId == -1) return;
  mesh.geometry.attributes.instanceColor.setXYZ(
    instanceId,
    isHighlighting ? highlightColor.r : mesh.geometry.attributes.instanceColorBase.getX(instanceId),
    isHighlighting ? highlightColor.g : mesh.geometry.attributes.instanceColorBase.getY(instanceId),
    isHighlighting ? highlightColor.b : mesh.geometry.attributes.instanceColorBase.getZ(instanceId)
  );
  mesh.geometry.attributes.instanceColor.needsUpdate = true;
}

Means, when isHighlighting is true, then we get the color data of highlightColor, otherwise, we restore it from the instanceColorBase attribute.

Made it from the scratch at midnight, so this solution can be optimized and improved :blush:

2 Likes