如何为ThreeJS实例化网格的每个着色器材质实例设置不同颜色
解决方案:为InstancedMesh的自定义ShaderMaterial实现实例独立颜色
要让每个InstancedMesh实例拥有独立的面和边缘颜色,你需要在自定义ShaderMaterial中手动处理实例颜色属性——mesh.setColorAt()本质是给几何体的instanceColor属性赋值,但你的自定义着色器没有读取这个属性,所以无法生效。以下是完整修改步骤:
1. 为几何体添加实例颜色属性
创建BoxGeometry后,添加instanceColor属性,用于存储每个实例的颜色数据:
const geometry = new THREE.BoxGeometry(1, 1, 1); // 添加实例颜色属性,每个实例对应一组RGB值 geometry.instanceColor = new THREE.InstancedBufferAttribute(new Float32Array(cell.length * 23 * 3), 3);
2. 修改顶点着色器
在顶点着色器中读取instanceColor属性,通过varying变量传递给片元着色器:
varying vec2 vUv; varying vec3 vInstanceColor; // 新增:传递实例颜色 void main() { vUv = uv; vInstanceColor = instanceColor; // 读取实例颜色属性 gl_Position = projectionMatrix * modelViewMatrix * instanceMatrix * vec4(position, 1.0); }
3. 修改片元着色器
使用传递过来的实例颜色替换原来的全局color uniform,实现每个实例独立的面+边缘颜色:
varying vec2 vUv; uniform float thickness; varying vec3 vInstanceColor; // 接收顶点着色器传递的实例颜色 float edgeFactor(vec2 p){ vec2 grid = abs(fract(p - 0.5) - 0.5) / fwidth(p) / thickness; return min(grid.x, grid.y); } void main() { float a = clamp(edgeFactor(vUv), 0., 1.); // 用实例颜色替换全局color,边缘保留黑色(可改为独立的实例边缘色属性) vec3 c = mix(vec3(0), vInstanceColor, a); gl_FragColor = vec4(c, 1.0); }
4. 更新ShaderMaterial设置
去掉不再需要的color uniform,确保材质配置正确:
var material = new THREE.ShaderMaterial({ uniforms: { thickness: { value: 1.5 } }, vertexShader: vertexShader, fragmentShader: fragmentShader, extensions: { derivatives: true } });
5. 在实例化时设置每个实例的颜色
在循环中为每个实例分配颜色,同时保留原有的矩阵设置逻辑:
const dummy = new THREE.Object3D(); let counter = 0; for (let i = 0; i < cell.length; i++){ for(let j = 0; j < cell[i].elevations.length - 1 ; j++){ let depth = cell[i].elevations[j] - cell[i].elevations[j + 1]; let zCenterElevation = (depth / 2) + cell[i].elevations[j + 1]; dummy.position.set(cell[i].x, zCenterElevation, -cell[i].y); dummy.scale.set(cell[i].width, depth, cell[i].height); dummy.updateMatrix(); mesh.setMatrixAt(counter, dummy.matrix); // 为当前实例设置颜色(示例用随机色,可替换为你的业务数据颜色) const color = new THREE.Color(); color.setHSL(Math.random(), 0.7, 0.5); geometry.instanceColor.setXYZ(counter, color.r, color.g, color.b); counter++; } } // 标记属性更新,让GPU生效 geometry.instanceColor.needsUpdate = true;
扩展:实现独立边缘颜色
如果需要每个实例的边缘颜色也独立,可重复上述逻辑,新增instanceEdgeColor的InstancedBufferAttribute,在片元着色器中改为:
vec3 c = mix(vInstanceEdgeColor, vInstanceColor, a);
内容的提问来源于stack exchange,提问作者martinjp
相关产品推荐
相关产品推荐

