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

如何在A-Frame中实现带城市边界的分色编码3D地图

问题背景
Uncaught TypeError: extrudeBufferGeometry.fromGeometry is not a function
at i.render ((index):104:29)
at i.onDataLoaded ((index):66:12)
at Queue._call (d3-queue.v3.js:43:54)
at maybeNotify (d3-queue.v3.js:120:7)
at d3-queue.v3.js:91:12
at Object.<anonymous> (d3.v4.js:11470:86)
at Dispatch.call (d3.v4.js:792:72)
at XMLHttpRequest.respond (d3.v4.js:11395:13)
问题根因
  1. 分色渲染不生效:原有代码遍历每个地理要素生成独立拉伸几何体后,将所有几何体合并为单个网格,且仅绑定了单一材质,无法给单个要素分配独立颜色;代码中定义的材质变量被重复覆盖,未实际生效。
  2. A-Frame 1.3.0版本报错:A-Frame 1.3.0内置的Three.js r135版本已移除旧版BufferGeometry.fromGeometry()API,原有基于Geometry对象的合并、转换逻辑不再兼容新版运行环境。
修复及实现方案

兼容问题修复

替换原有Geometry相关逻辑,直接使用BufferGeometry生成拉伸图形,通过BufferGeometryUtils完成几何体合并,适配新版Three.js API。

分色渲染实现

  • 引入D3色阶比例尺,根据要素绑定的属性值(如人口、GDP、行政等级等)映射对应颜色
  • 遍历每个地理要素时记录几何体顶点偏移量,给合并后的BufferGeometry添加材质组,每个组对应一个要素的专属材质,实现单网格多颜色渲染
  • 可按需区分顶面、侧面颜色,也可配置不同层级要素的色阶规则
核心修改代码

替换原有组件中的render方法为如下实现:

render: function (populationByFipsCode, maxPopulation, maxExtrudeHeight) {
  if (!populationByFipsCode) return;
  const geometries = [];
  const stateOutlineVertices = [];
  // 配置5档蓝色系分色色阶,可按需替换色值、调整分档数量
  const colorScale = d3.scaleQuantize()
    .domain([0, maxPopulation])
    .range(['#f7fbff', '#deebf7', '#9ecae1', '#4292c6', '#08519c']);
  const topMaterials = [];
  const sideMaterials = [];
  const geometryGroups = [];
  let materialIndex = 0;
  let vertexOffset = 0;

  this.geoProjectionComponent.geoJson.features.forEach(function (feature) {
    const population = populationByFipsCode[feature.id];
    const extrudeAmount = (population / maxPopulation) * maxExtrudeHeight;
    // 根据当前要素属性值匹配对应颜色
    const featureColor = new THREE.Color(colorScale(population));
    // 分别生成顶面、侧面对应材质,侧面颜色做加深处理
    topMaterials.push(new THREE.MeshStandardMaterial({ color: featureColor }));
    sideMaterials.push(new THREE.MeshStandardMaterial({ color: featureColor.clone().multiplyScalar(0.7) }));
    
    const extrudeSettings = {
      amount: extrudeAmount,
      bevelEnabled: false
    };

    const mapRenderContext = this.system.renderToContext(feature, this.geoProjectionComponent.projection);
    const stateShapes = mapRenderContext.toShapes();
    // 收集轮廓线顶点
    stateOutlineVertices = stateOutlineVertices.concat(mapRenderContext.toVertices(extrudeAmount));
    // 直接生成BufferGeometry类型的拉伸几何体
    const extrudedFeatureGeometry = new THREE.ExtrudeBufferGeometry(stateShapes, extrudeSettings);
    geometries.push(extrudedFeatureGeometry);
    
    // 计算当前几何体的索引偏移,配置材质组
    const indexCount = extrudedFeatureGeometry.index ? extrudedFeatureGeometry.index.count : extrudedFeatureGeometry.attributes.position.count;
    // 顶面材质组
    geometryGroups.push({
      start: vertexOffset,
      count: indexCount / 2,
      materialIndex: materialIndex * 2
    });
    // 侧面材质组
    geometryGroups.push({
      start: vertexOffset + indexCount / 2,
      count: indexCount / 2,
      materialIndex: materialIndex * 2 + 1
    });
    vertexOffset += indexCount;
    materialIndex++;
  }.bind(this));

  // 合并所有要素几何体
  const extrudeBufferGeometry = THREE.BufferGeometryUtils.mergeBufferGeometries(geometries, true);
  // 绑定材质组
  extrudeBufferGeometry.clearGroups();
  geometryGroups.forEach(group => {
    extrudeBufferGeometry.addGroup(group.start, group.count, group.materialIndex);
  });
  // 合并材质数组
  const allMaterials = [];
  topMaterials.forEach((mat, idx) => {
    allMaterials.push(mat, sideMaterials[idx]);
  });

  const extrudedMap = new THREE.Mesh(extrudeBufferGeometry, allMaterials);
  this.el.setObject3D('map', extrudedMap);

  // 生成轮廓线,适配新版Three.js API
  const stateOutlineGeometry = new THREE.BufferGeometry();
  stateOutlineGeometry.setAttribute('position', new THREE.Float32BufferAttribute(stateOutlineVertices, 3));
  const stateOutlineMaterial = new THREE.LineBasicMaterial({ color: 0xcccaaa });
  const stateOutlines = new THREE.LineSegments(stateOutlineGeometry, stateOutlineMaterial);
  this.el.setObject3D('lines', stateOutlines);
}

补充说明:

  • 需要额外引入和当前Three.js版本匹配的BufferGeometryUtils工具库,用于BufferGeometry对象合并
  • 若不需要区分顶面和侧面颜色,可简化材质逻辑,仅保留一套材质数组即可
  • 若要实现国家、省、市不同层级要素的颜色区分,只需在遍历feature时增加要素类型判断,给不同类型匹配对应色阶规则即可
  • 原有代码中addAttributeAPI在新版Three.js中已替换为setAttribute,上述代码已同步修复该兼容问题

内容的提问来源于stack exchange,提问作者Moh-Spark

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:18:14