ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

three.js 3D城市地图可视化实战:从建模到性能优化的完整流程

three.js 3D城市地图可视化实战:从建模到性能优化的完整流程 这个系列写到第二篇了。上一篇我把城市底图和建筑楼体从零搭了出来用 ExtrudeGeometry 挤出楼块、铺上底图纹理说实话跑起来那一刻确实挺兴奋的。但兴奋劲过了之后问题也接踵而至数据一多就掉帧、建筑没有层级感、鼠标点不到楼、光照一打整个场景闷成一片。这篇文章就把我从“能显示地图”到“能拿得出手做演示”这一段踩过的坑、换过的思路、最终定下来的方案完整记录一遍。内容主要围绕真实地理数据接入、视觉风格落地、交互拾取、性能优化和问题排查这几块展开适合已经会一点 three.js、正准备做 3D 地图可视化或者数字孪生场景的朋友参考。放心所有代码都是可跑通的思路讲得比代码多因为我自己就是被“看着代码会、一跑就废”坑过来的。1. 数据准备把经纬度变成 three.js 世界里的坐标1.1 GeoJSON 解析与坐标投影的底层逻辑做 3D 地图第一步不是建模是处理数据。大部分城市建筑轮廓数据都是 GeoJSON 格式里面存的是经纬度坐标典型的 Feature 长这样{ type: Feature, properties: { name: 某大厦, height: 120, floors: 30 }, geometry: { type: Polygon, coordinates: [ [ [116.391, 39.907], [116.392, 39.907], [116.392, 39.908], [116.391, 39.908] ] ] } }这里有个关键问题three.js 的世界坐标系是笛卡尔坐标系单位是米或者说我们习惯把它当米用而经纬度是球面坐标。直接把经纬度塞进 Vector3 里场景会变成一个极细长的奇怪形状因为经度 1 度和纬度 1 度对应的实际距离不一样纬度越高差别越大。我踩过的第一个坑就在这里。最早我图省事直接把 lng 当 x、lat 当 z减掉中心点就丢给 three.js结果出来一个完全比例失调的地图楼和楼之间的距离跟实际情况完全对不上。后来老老实实做了投影换算。最简单有效的方案是等距投影加比例系数换算。思路是这样先拿到所有数据的经纬度范围算出中心点(centerLng, centerLat)。把每个点减去中心点得到相对于中心的偏移量。经度方向乘以一个固定的 scale纬度方向乘以另一个 scale也可以直接都乘同一个值因为我们很多场景不做真实距离测量只求视觉比例正确。一段实际的代码长这样function projectToLocal(coordinates, center, scale 1000) { const [lng, lat] coordinates; const x (lng - center[0]) * scale; const z (lat - center[1]) * scale; return { x, z }; }这个scale是什么意思其实就是每度经纬度对应多少单位。如果你希望地图里 1 个单位约等于 1 米那就需要用墨卡托投影或者 Web Mercator 的公式换算。如果想偷懒只做视觉还原那就根据建筑物的原始轮廓尺寸手动调一个看起来舒服的 scale。我项目里用的源数据跨度大约是 0.05 个经度scale取 8000 到 12000 之间看起来比较舒服具体数值看你要展示的范围大小。如果你想要更严谨的投影推荐用d3-geo库里的geoMercator方法它内部做了标准墨卡托换算出来的结果经纬度比例真实后续叠加路网数据也不会出现明显偏差。import { geoMercator, geoPath } from d3-geo; const projection geoMercator() .center([centerLng, centerLat]) .scale(80000) .translate([0, 0]); function projectToLocal(coordinates) { const [x, y] projection(coordinates); return { x, z: y }; }注意transcale和translate的组合translate([0, 0])让投影结果以中心点为原点转换出来的坐标就可以直接丢给 three.js 使用。我对比过d3 投影出来的楼间距和真实地图的视觉比例几乎一致后续如果要叠加标记点和路线整体协调感好很多。1.2 用 ExtrudeGeometry 批量生成楼体模型坐标投影搞定之后下一步就是生成楼体。最直接的方式是遍历 GeoJSON 的每个 Feature用THREE.Shape描出建筑轮廓再用ExtrudeGeometry挤出高度。这一步看着简单实际上有两处特别容易翻车。第一处是孔洞。城市建筑轮廓很多都带内庭院GeoJSON 里coordinates数组的第一项是外环后面几项是内环洞。如果用THREE.Shape的holes属性不处理好挤出来的模型会直接破面。正确做法是把外环坐标放进shape里内环坐标用holes.push()加进去const shape new THREE.Shape(); const coordinates feature.geometry.coordinates; const outer coordinates[0]; shape.moveTo(outer[0][0], outer[0][1]); for (let i 1; i outer.length; i) { shape.lineTo(outer[i][0], outer[i][1]); } const holes coordinates.slice(1); holes.forEach(hole { const holePath new THREE.Path(); holePath.moveTo(hole[0][0], hole[0][1]); for (let i 1; i hole.length; i) { holePath.lineTo(hole[i][0], hole[i][1]); } shape.holes.push(holePath); });第二处是坐标回环闭合。GeoJSON 规范里多边形首尾坐标是相同的即最后一点等于第一点如果你在画 Shape 时没做去重会在闭合处产生一个极小的退化面渲染时出现一条细细的黑色裂缝。我一般加个判断如果最后一点和第一点距离小于 0.001就跳过最后一点。挤出参数里depth或amount决定建筑高度bevelEnabled: false关闭倒角。很多教程会推荐开启倒角让建筑圆润但在地图场景里小体量建筑一旦开倒角面数直接翻倍而且近距离看容易产生高光异常影响“科技感”的干净风格。我最后统一设置bevelEnabled: false省性能也省心。还有个容易忽略的点如果数据里建筑物高度单位不统一有的写“米”有的写“层数”必须在生成模型前统一换算。层数按每层 3.5 米估算但商业楼和住宅楼的层高差异很大最好根据数据类型设置一个 scale 系数住宅 3 米、写字楼 4.2 米这样出来的模型高矮比例才符合真实观感。2. 地图视觉风格从“白模”到“可视化大屏”的进阶2.1 暗色底图与发光材质的搭配思路3D 地图可视化项目里客户要的通常不是仿真模型而是那种“一看起来就很高级” 的大屏风格深色背景、发光边缘、透明感、科技蓝或者科技青。这个风格说白了就是靠材质参数堆出来的但堆法有讲究。我试过三种材质方案第一种是MeshPhongMaterial效果比较死板高光点是圆形亮点像塑料。第二种是MeshStandardMaterial物理渲染真实但对光源要求高需要环境贴图配合不然容易发灰。第三种是MeshLambertMaterial加 emissive这是我最常用的方案因为地图场景对真实光照要求不高带动画效果的 emissive 加上发光边缘视觉足够漂亮而且性能开销小。看一下我当时给建筑做的材质配置const material new THREE.MeshLambertMaterial({ color: 0x1a2b4c, emissive: 0x122a4a, emissiveIntensity: 0.8, transparent: true, opacity: 0.95, side: THREE.DoubleSide });这里有个关键技巧颜色不要选纯黑要选带一点蓝紫色的深色emissive选稍微亮一点的同色系这样在暗色背景下轮廓自然发光但又不刺眼。如果把color设成 0x000000建筑会显得特别死没有体积感。柱状楼的渐变高度效果可以在顶点着色器里做但对大多数项目来说用代码控制颜色分层更方便。我处理的方式是根据建筑高度动态生成颜色矮楼偏暗、高楼偏亮甚至可以做“数据越高亮色越暖”的效果比如用 HSL 颜色根据楼层数映射const hue 0.6 - Math.min(height / maxHeight, 1) * 0.3; const color new THREE.Color().setHSL(hue, 0.8, 0.45);这样整个城市从空中看会有明显的层次递进高价值建筑一眼就能识别出来而不是所有楼都一样颜色糊成一团。2.2 建筑轮廓描边与顶部发光细节只靠材质颜色建筑边缘还是容易模糊尤其是多个建筑挨得近的时候。最开始我没做描边镜头拉远时所有楼像一坨色块。后来我扒了些成熟数字孪生项目的渲染思路发现他们都会用描边或者顶面高亮来强化轮廓。我最终采用的是“顶面高亮 底边光带”方案操作很简单生成楼体时把每个建筑的顶面单独提取出来做一个浅色平面放在楼顶上方 0.1 单位处再把底面轮廓向外偏移 0.5 单位铺一层亮色平面当作光带。这样设置的好处是视角拉高时每个建筑顶部都有一条明显的亮边底下的光带又能把建筑和地面区分开。更高级一点的做法是使用EdgesGeometry加LineSegments把建筑轮廓线真正画出来。不过城市级建筑数量动辄几千线条对象多了性能撑不住我一般不把它当作默认方案只在小范围突出展示的几栋楼上做。顶部高亮平面的代码参考const topShape new THREE.Shape(); // ... 用建筑外环坐标填充 shape同前面一样 const topGeometry new THREE.ShapeGeometry(topShape); const topMaterial new THREE.MeshBasicMaterial({ color: 0x66ccff, transparent: true, opacity: 0.6 }); const topMesh new THREE.Mesh(topGeometry, topMaterial); topMesh.rotation.x -Math.PI / 2; topMesh.position.y height 0.1;注意旋转角度要设置正确ShapeGeometry默认在 XY 平面需要旋转到 XZ 平面否则楼顶会立起来。2.3 环境光、区域光与雾效的调节经验很多新手做 3D 地图光打出来总是两个极端要么全亮没有层次要么全黑看不见细节。地图场景的光源配置其实很有讲究我建议至少用三个光源第一个是HemisphereLight半球光负责整体环境照明让暗部不完全是死黑skyColor和groundColor的搭配很关键我常用 0x224488 和 0x0a0a1a这样蓝色调统一。第二个是DirectionalLight平行光负责给建筑一个主受光面制造明暗对比。位置不要正上方应该稍微偏 45 度左右让建筑侧面有一半受光、一半被环境光照亮立体感一下就出来了。第三个是AmbientLight环境光强度不要太高0.2 到 0.3 即可它的作用是把阴影里的细节稍微提亮一点但不破坏主光源产生的对比。雾效是很容易被忽略的一个点。地图场景如果范围很大镜头拉远后远处的建筑和背景融为一体必须靠scene.fog来处理。我用的是FogExp2密度设为 0.002颜色和背景色一致深蓝黑色。注意雾的颜色一定要和场景背景色一致否则远处建筑会泛白看起来很脏。这里有个小细节值得留意FogExp2密度调太大建筑从远到近的过渡会非常突兀调太小又没有效果。我建议先把镜头放到想要的最远视角慢慢调密度直到远处建筑“刚好快看不见”的状态。这个位置通常会让你在拉近镜头时有明显的视觉惊喜。3. 地图交互与事件拾取让场景“活”起来3.1 Raycaster 射线检测的原理与实现地图不能互动那只能算半成品。我给场景加的第一组交互是鼠标悬停高亮和点击弹出信息面板核心用的就是THREE.Raycaster。Raycaster 的原理一句话说清楚从相机位置发射一条射线经过鼠标在屏幕上的位置射向场景中的物体然后判断射线与哪些物体相交。就像你伸手往屏幕里指摸到哪栋楼就是哪栋楼。代码结构很简单但有几个容易踩坑的细节const raycaster new THREE.Raycaster(); const mouse new THREE.Vector2(); function onMouseMove(event) { mouse.x (event.clientX / window.innerWidth) * 2 - 1; mouse.y -(event.clientY / window.innerHeight) * 2 1; } function checkIntersection() { raycaster.setFromCamera(mouse, camera); const intersects raycaster.intersectObjects(buildingMeshes, false); if (intersects.length 0) { const hit intersects[0].object; // 高亮处理 } }第一个坑是mouse.y的正负号。three.js 的屏幕坐标原点在画布中心y 轴朝上浏览器的坐标原点在左上角y 轴朝下。所以必须用-(event.clientY / window.innerHeight) * 2 1反转这个负号我写过不止三次每次忘掉都会发现鼠标和拾取位置上下颠倒。第二个坑是intersectObjects的第二个参数。如果传true会递归检查所有子物体。当建筑Mesh的子节点特别多时性能会明显下降如果传false只检查传入数组中的 Mesh性能好很多。前提是确保建筑 Mesh 就是你要拾取的对象不要把组对象传进去。我的做法是维护一个buildingMeshes数组每个元素就是 ExtrudeGeometry 生成的那个 Mesh这样检查又快又准。第三个坑是透明材质也可以被射线拾取。如果建筑材质设置了transparent: true射线检测也会命中哪怕透明度极低。如果你不想让某些装饰性物体响应鼠标事件要么把它从拾取数组里移除要么在射线检测后判断物体的userData.isInteractive属性。3.2 悬停高亮与选中状态的视觉反馈悬停高亮不只是把emissive颜色变亮那么简单。如果只是改材质颜色建筑边缘直接变成一坨纯色一点都不精致。我试过几种方案最终效果最好的是“边线高亮 透明度变化 高度微浮起”三件套把建筑材质的emissive从深蓝改成亮青色emissiveIntensity从 0.8 提到 1.5。给建筑加一条描边线用EdgesGeometry加LineBasicMaterial颜色从透明变成亮蓝。让建筑沿 Y 轴升高 2 个单位模拟“浮起来”的交互状态结束后再缓慢回落。不过要注意如果对单个建筑施加上升位移会破坏场景整体的视觉连续性特别是贴在一起的建筑群某个楼突然抬高会显得很突兀。城市级地图场景我建议只做前两项颜色和边缘变化不做位移如果是做园区级或者单栋建筑的展示位移效果可以保留。悬浮浮动的动画我用的是 TWEEN 库可以直接修改建筑的position.yimport * as TWEEN from tweenjs/tween.js; function highlightBuilding(mesh) { new TWEEN.Tween(mesh.position) .to({ y: 2 }, 200) .easing(TWEEN.Easing.Cubic.Out) .start(); } function unhighlightBuilding(mesh) { new TWEEN.Tween(mesh.position) .to({ y: 0 }, 200) .easing(TWEEN.Easing.Cubic.Out) .start(); }注意在渲染循环里加上TWEEN.update()否则动画不会执行。这个 API 在 3.x 版本里变化不大老项目迁移也方便。3.3 点击弹窗与相机控制的冲突解决弹窗这件事纯 three.js 实现比较麻烦我采用的方式是点击建筑后把建筑信息和屏幕坐标传给 HTML 覆盖层用绝对的 HTML 元素来展示。这样样式可以随便调不用在 WebGL 里画 UI。拿到建筑在屏幕上的位置用Vector3.project(camera)const screenPosition new THREE.Vector3(); mesh.getWorldPosition(screenPosition); screenPosition.project(camera); const x (screenPosition.x * 0.5 0.5) * window.innerWidth; const y (-screenPosition.y * 0.5 0.5) * window.innerHeight; infoPanel.style.left ${x}px; infoPanel.style.top ${y}px;这里坑也明显如果相机旋转了建筑会跑出屏幕范围或者弹窗位置跟着转走。解决方案是在弹窗显示期间锁定相机旋转或者每帧更新弹窗位置。每帧更新的性能开销不大但要注意弹窗位置变化时不能有卡顿否则看着很难受。相机控制和点击事件的冲突是另一个高频问题。OrbitControls 的默认行为是“按下鼠标拖拽旋转”但“按下鼠标”同时也会触发 click 事件。实际操作中用户想旋转视角结果不小心触发了弹窗体验很糟。解决办法是在 mousedown 时记录鼠标位置mouseup 时判断鼠标位移是否超过阈值比如 5 像素超过就视为拖拽而不是点击let isDragging false; let startX 0, startY 0; container.addEventListener(mousedown, e { isDragging false; startX e.clientX; startY e.clientY; }); container.addEventListener(mousemove, e { if (e.clientX - startX 5 || e.clientY - startY 5) { isDragging true; } }); container.addEventListener(mouseup, e { if (!isDragging) { // 执行点击拾取逻辑 } });这段代码看着简单但非常实用几乎每个 three.js 交互场景都要写一遍。识别拖拽还是点击的方式有很多阈值只要大于鼠标本身的抖动范围就行我试下来 5 像素比较合理。4. 性能优化一个真实项目的调优实战记录4.1 几何体合并从每秒20帧到50帧的关键一步当建筑数量超过 2000 个时three.js 场景卡得没法看Drawcall 数量差不多就是建筑数量一个建筑两个 Material 就是两次绘制调用2000 个建筑就是 4000 多次。浏览器对 Drawcall 的承受力大概在一两千次超了就是掉帧。解决的思路是几何体合并。把同一种材质的建筑合并成一个BufferGeometry这样 2000 栋楼只需要一次绘制调用Drawcall 直接降到个位数。合并原理是把每个建筑 Mesh 的顶点数据按世界坐标转换后全部塞进一个大几何体里。const mergedGeometry new THREE.BufferGeometry(); const geometries []; const material new THREE.MeshLambertMaterial({ color: 0x1a2b4c }); buildingMeshes.forEach(mesh { mesh.updateMatrix(); geometries.push(mesh.geometry.clone().applyMatrix4(mesh.matrix)); }); const merged mergeBufferGeometries(geometries); const cityMesh new THREE.Mesh(merged, material);mergeBufferGeometries方法在three/examples/jsm/utils/BufferGeometryUtils.js里直接引入就行。合并后要注意原来每个建筑如果想要独立的颜色或者交互拾取就得额外想办法我通常的做法是保留一份原始buildingMeshes列表用于射线检测合并后的 Mesh 只负责显示。射线检测依然用原始 Mesh因为它们有独立的位置和几何信息拾取精度不受影响。这一步优化效果立竿见影。我项目里的数据大约 3000 栋楼合并前帧率 20 到 25 帧合并后直接飙到 60 帧。如果你做的是城市级别地图合并是不可跳过的步骤。4.2 纹理压缩与重复利用的细节地图场景里地面纹理、背景贴图如果用大尺寸 PNG内存会被吃得很厉害。一张 4096×4096 的 PNG 解码后占用内存差不多 64MB再加上透明通道就是 128MB两张就能让移动端崩溃。我建议把贴图尺寸压到 1024 或者 2048格式用 JPEG 或者 WebP。大面积暗色底图用 JPEG 完全够因为细节本来就不多。另外多个建筑如果需要不同纹理可以尽量控制在 2 到 3 张图内让所有建筑在 UV 映射时复用这几种纹理而不是每栋楼加载一张。加载纹理时用texture.colorSpace THREE.SRGBColorSpace否则颜色会整体偏暗。这个坑我印象很深之前所有贴图都没设置 colorSpace画面一直是灰蒙蒙的查了半天才发现是色彩空间的问题。纹理用完之后如果不再需要记得调用texture.dispose()释放 GPU 内存。这个操作容易被忽略但长期跑地图应用时泄漏的内存量会非常可观。4.3 LOD 与远距离裁剪策略镜头拉远后几千栋楼全画出来没有任何意义因为远处的楼在屏幕上只有几个像素。我给建筑加了一套极简的 LODLevel of Detail方案根据相机距离切换三种显示精度。远距离500 米外显示合并后的低模版本建筑简化为盒子BoxGeometry不显示细节纹理。 中距离50 到 500 米显示合并后的精细版本材质完整。 近距离50 米内显示单栋建筑的高模版本可以交互。three.js 的THREE.LOD对象可以管理这个逻辑但要注意切换时不要出现明显的“弹跳”感我给切换距离加了 20% 的滞后区间远到近的阈值和近到远的阈值分开防止频繁切换。同时配合frustumCulled属性默认情况下 three.js 会自动判断物体是否在视锥体内不在就不渲染。合并后的城市模型如果太大这个判断效率反而会低我一般会手动把城市分成 4 到 8 个区块每个区块设置自己的边界渲染时只绘制可见区块。还有个容易被忽视的点OrbitControls的maxDistance和minDistance一定要设置。限制相机最远距离可以保证远处的模型不会被加载到近处也不会穿模走进楼体内部。我的经验是maxDistance设成整个城市包围盒对角线长度的 2 倍minDistance设为 50 左右。5. 常见问题与排查技巧实录5.1 坐标偏移、模型错位与数据清洗做 3D 地图大概率会遇到模型整体偏移或者个别建筑跑到莫名其妙的位置。排查这种问题第一步不是看代码而是确认原始数据。我建议把 GeoJSON 加载之后先做一次坐标范围检查打印出经纬度最小值、最大值和预期范围比对。有一次我加载一个区级数据结果地图整体偏移了几百米排查半天发现是数据里有几个坐标点把小数点和逗号写错了导致个别点经纬度出现异常值。GeoJSON 数据质量参差不齐清洗数据这一步不能省至少要做三件事删除或校正经纬度越界的点比如 lng 超过 180 或者 lat 超过 90。去掉坐标数量过少的无效多边形比如只有 2 个点的线。统一坐标系确保所有数据都是 WGS84 经纬度而不是火星坐标或者地方坐标系。第二个常见问题是模型错位但坐标看起来没错。这种情况十有八九是投影方式不一致有的数据是 GCJ-02 坐标你直接当作 WGS84 投影出来的结果就偏移了。高德、腾讯等国内地图服务提供的坐标基本都是 GCJ-02而 OpenStreetMap 用的是 WGS84混用就会偏差几百米。判断方法很简单拿一个已知地标经纬度对比一下如果差了三百米左右基本就是坐标加密问题。5.2 建筑闪烁、材质变黑与 z-fighting 问题建筑边缘闪烁或者地面和建筑底面交替出现这是 z-fighting 的典型特征原因是两个面在空间上几乎重合深度缓存无法判断哪个在前。我项目里出现最多的情况是建筑底面和地面在一个平面上镜头转动时狂闪。解决办法有几种最省事的方案是把建筑整体抬高 0.1 到 0.2 个单位和地面稍微拉开距离。如果不想移动建筑也可以把地面的多边形向下偏移一点。注意偏移量不能太大否则会看到明显的悬空感。材质变黑的问题查 log 时经常发现是光源没设置好。建筑材质用了 Lambert 材质必须有一个DirectionalLight才能产生受光面如果只有一个AmbientLight物体看起来就是个没有立体感的黑影。另外如果材质side: THREE.FrontSide且模型顶点法线方向反了从某些角度看整个模型会消失或者发黑。这时候把材质side改成THREE.DoubleSide可以直观判断问题是不是法线方向导致的。5.3 加载缓慢与纹理闪烁的缓解办法3D 地图数据量大的时候加载小圆圈转圈转到怀疑人生。除了压缩纹理和几何体我还用了 two-pass 加载策略先加载底图和不带纹理的简化场景让用户立刻看到东西再异步加载建筑纹理和细节模型完成后就地替换。这样用户感知到的加载速度明显变快虽然总加载时间没怎么变但体验好很多。纹理闪烁是另一个顽固问题。有些显卡 mipmap 生成不完整摄像机慢慢移动时贴图上会出现摩尔纹或者高亮条纹。这类问题一般靠调整纹理的anisotropy参数缓解const texture new THREE.TextureLoader().load(texture.jpg); texture.anisotropy 8; texture.generateMipmaps true; texture.minFilter THREE.LinearMipmapLinearFilter;anisotropy数值越高倾斜视角下的纹理质量越好但性能也有损耗我建议设成 4 或者 8 就够了再高基本看不出区别。5.4 内存占用过高和移动端适配经验3D 地图放在 PC 上跑得挺流畅一拿到移动端就崩这是很多项目的通病。首先是纹理数量移动端 GPU 显存有限一个大场景把所有纹理都塞进去基本必崩。我的经验是移动端场景纹理总量控制在 50MB 以内超过就压缩图片质量或者减少同屏模型数量。其次是像素比设置。three.js 默认的renderer.setPixelRatio(window.devicePixelRatio)在手机上容易把渲染分辨率拉得很高iPhone 的 devicePixelRatio 通常是 3意味着实际渲染像素数是屏幕的 9 倍。我一般限制最高 2const pixelRatio Math.min(window.devicePixelRatio, 2); renderer.setPixelRatio(pixelRatio);肉眼几乎看不出差别但性能能提升不少。最后是渲染器的antialias选项在移动端可以考虑关掉改用renderer.setPixelRatio(2)来抵消锯齿感。实测下来关掉抗锯齿后帧率提升明显画面质量在手机屏幕上完全可以接受。6. 从“能跑”到“能展示”的最后几点建议如果你读到这里说明你已经把 3D 地图的核心链路摸得差不多了。这套记录写到最后我想再分享几个实际操作里的判断标准。第一场景里物体数量多的时候先合并再优化材质这个顺序别反。先合并几何体能解决 80% 的性能问题剩下的材质和纹理优化只是锦上添花。第二交互功能尽量放在模型合并和优化之后再加否则你会在“高亮某个建筑”和“这个建筑已经合并成大模型”之间反复纠结。第三遇到奇怪的问题先怀疑数据再怀疑代码最后怀疑 three.js 本身这个库的 API 在你用对的前提下是极其稳定的。我现在这个项目的数据已经跑到了 5000 多栋楼配合 LOD 和区块裁剪PC 端和移动端都能相对稳定地跑满 60 帧。后续我计划把建筑选中后的详情弹窗做成可配置组件顺便把城市漫游相机动画整理出一套统一 API到时候再来更新这个系列。如果你也在做类似的地图可视化项目希望这篇记录能帮你少走几个我已经踩过的弯路。
返回列表