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

如何为BufferGeometry的每个顶点分配不同颜色?Three.js技术问询

解决方案:用顶点颜色实现动态高度着色与水位调整

你已经搞定了地形高度的加载逻辑,但纹理贴图确实没法动态响应顶点高度变化——**顶点颜色(Vertex Colors)**才是解决这个问题的正确方向,它能让每个顶点的颜色直接绑定到高度值,还能轻松实现水位变化的视觉效果。我来帮你修改代码:

步骤1:给BufferGeometry添加顶点颜色属性

首先,我们需要给PlaneBufferGeometry新增一个color属性,用来存储每个顶点的RGB颜色值。在你遍历数据设置顶点高度的同时,同步计算每个顶点的颜色:

function init() {
  // ... 保留原有相机、灯光、渲染器等初始化代码 ...

  // initialize plane
  plane = new THREE.PlaneBufferGeometry(1500 * SIZE_AMPLIFIER, 1500 * SIZE_AMPLIFIER, 999, 999);
  plane.castShadow = true;
  plane.receiveShadow = true;

  const vertices = plane.attributes.position.array;
  // 创建颜色数组,每个顶点对应3个0-1范围的浮点数(RGB)
  const colorArray = new Float32Array(vertices.length);
  
  // 初始化水位(可以放到组件state里方便后续动态调整)
  this.waterLevel = 50; // 示例水位值,根据你的高度范围自行调整

  // 遍历数据,同步设置顶点高度与颜色
  for (let i = 0, j = 2; i < data.length; i += 1, j += 3) {
    let height;
    if (data[i] == 0) {
      height = 0;
    } else {
      height = data[i] / 65535 * 325 + 10;
    }
    vertices[j] = height;

    // 根据高度计算对应颜色
    let r, g, b;
    if (height < this.waterLevel) {
      // 低于水位:设置深蓝色
      r = 0.1;
      g = 0.3;
      b = 0.8;
    } else if (height < 100) {
      // 低海拔:草地绿色
      r = 0.2;
      g = 0.7;
      b = 0.3;
    } else if (height < 200) {
      // 中海拔:黄褐色山地
      r = 0.6;
      g = 0.5;
      b = 0.3;
    } else {
      // 高海拔:雪地白色
      r = 0.9;
      g = 0.9;
      b = 0.9;
    }
    // 给当前顶点赋值颜色(每个顶点占数组中3个连续位置)
    colorArray[j - 2] = r;
    colorArray[j - 1] = g;
    colorArray[j] = b;
  }

  // 将颜色数组添加为Geometry的官方属性
  plane.setAttribute('color', new THREE.BufferAttribute(colorArray, 3));
  // 标记位置属性需要更新,让Three.js感知到顶点高度变化
  plane.attributes.position.needsUpdate = true;

  // ... 保留后续法线计算、mesh创建等代码 ...
}

步骤2:修改材质以启用顶点颜色渲染

把原来带纹理的MeshLambertMaterial改成支持顶点颜色的配置,这样Three.js会直接使用我们刚才设置的顶点颜色进行渲染:

// 替换原来的材质代码
const material = new THREE.MeshLambertMaterial({
  vertexColors: THREE.VertexColors, // 核心:启用顶点颜色
  side: THREE.DoubleSide // 可选,如果你需要看到地形背面的颜色
});
const mesh = new THREE.Mesh(plane, material);

步骤3:添加动态调整水位的函数

为了能在React里通过滑块、按钮等组件随时调整水位,添加一个更新函数,修改顶点颜色并通知Three.js重新渲染:

// 把这个函数挂载到组件实例上,方便React事件调用
this.updateWaterLevel = (newLevel) => {
  this.waterLevel = newLevel;
  const vertices = plane.attributes.position.array;
  const colorArray = plane.attributes.color.array;

  // 重新计算每个顶点的颜色
  for (let i = 0, j = 2; i < data.length; i += 1, j += 3) {
    const height = vertices[j];
    let r, g, b;
    if (height < this.waterLevel) {
      r = 0.1;
      g = 0.3;
      b = 0.8;
    } else if (height < 100) {
      r = 0.2;
      g = 0.7;
      b = 0.3;
    } else if (height < 200) {
      r = 0.6;
      g = 0.5;
      b = 0.3;
    } else {
      r = 0.9;
      g = 0.9;
      b = 0.9;
    }
    colorArray[j - 2] = r;
    colorArray[j - 1] = g;
    colorArray[j] = b;
  }

  // 标记颜色属性需要更新,Three.js会在下一帧重新渲染
  plane.attributes.color.needsUpdate = true;
};

额外注意事项

  • 性能优化:你的地形是1000x1000的顶点(百万级规模),每次更新水位都会遍历所有顶点,如果是频繁触发的操作(比如滑块拖动),可以给updateWaterLevel加防抖处理;或者改用ShaderMaterial,把水位计算放到GPU着色器里,性能会提升很多。
  • 颜色自定义:你可以根据需求修改颜色阈值和RGB值,比如用THREE.Color的lerp方法实现不同高度区间的颜色平滑过渡。
  • React变量管理:把plane、mesh、waterLevel这些变量放到组件的实例属性(比如this.plane)或者useRef(函数组件)里,确保在事件处理函数里能正常访问。

这样修改后,地形颜色会完全绑定到顶点高度,调整水位时所有低于水位的顶点会自动变成蓝色,完美满足动态更新的需求。

内容的提问来源于stack exchange,提问作者Borislav Kiselkov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 14:02:29