易恺铭
EN

把真实世界叠进过程生成地形:从 DEM 到 OSM 的 polygon

约 3 分钟

过程生成最容易被质疑的一点是”看着假”。

不是不好看,是没有故事 —— 山脉、河流、平原都在合理的位置上,但它们不属于任何地方。

这一个多月做的事情,就是给它接上真实世界。

混合,而不是替换

原来的 procedural terrain 直接从 noise generator 取高度。

现在中间加了一层:高度由真实地理数据和 noise generator 混合得到。

关键在于这层是可退化的 —— 地理数据为空时,noise generator 按之前的配置走,结果就是原本的全过程化地形。 所以我不需要为”有数据”和”没数据”维护两套流程,也不需要给全球每一寸地面都准备数据。

做出 RealGeography node 之后做了一次快速 check:之前那个海岛,变成了一个只有高程的成都平原。龙泉山经过高度侵蚀,体现出了一些特别的感觉 —— 那种感觉是纯噪声给不出来的。

后来把 loc 重新 base 到了重庆。重庆的地形起伏更适合验证这套东西。

尺度

9 chunk 的 full detail 结构配合服务器缓存,得到一个 324×324 公里的大地形。按 1:5 的缩放比例,单边也有 60 多公里的真实尺度 —— 开车跑 60 码,跑完一边得一小时。

后来把 UE5 端支持到了自由缩放比例,调到 2:1。站在重庆的嘉陵江边,有一丝丝置身现实的感觉了。

缩放带来一个直接后果:原本设置的震幅和侵蚀参数就不够用了。这类参数是绑在尺度上的,换比例就得重调,没有捷径。调完之后最终生成的细节地表还是比较漂亮的。

OSM 的 multi-polygon 是个坑

landuse、water relation、二级道路、building block 都画上地图之后,重庆整块远看已经很接近 GTA 的地图了。

但 river 和 lake 的 polygon 处理,是这一段最耗时间的一块。

OSM 的 multi-polygon relation 有个前提不成立:relation 的 member 不一定是首尾连接的。 你不能按 member 的顺序把它们接起来当成一个闭环。

最后只能依靠每条 way 的头尾节点,自己重建连接关系。

重建之后大部分是对的,但在几个跨 chunk 的大 polygon 上还有翻车的情况。目前先屏蔽大 polygon 了事,具体问题在哪还要再查。

水面现在还是假的

river 和 lake 现在没有生成实体 polygon,而是直接在地图上着色。

结果就是:大部分水面虽然有了 normal 朝上的反射,但它的实际位置并不是一个平面,而是跟着地形起伏。远看没事,一走近就穿帮。

要修的方向是清楚的 —— 直接生成 polygon 作为水面,给水面一个专门的 shader,同时给湖底或河床加上对应的着色,形成更好的水体效果。这件事还没做。

building 把内存打爆了

生成瑞士 Interlaken 的建筑时,解压 building 的 json compress 直接 crash 了。

表面原因是 puerts 虚拟机开的内存不够大,根子还是 building 的 json 太大。

所以顺手把之前挂着的一个需求做了:把 building 写入为纯数据记录下来。既加速 building 的解析过程,也降低内存消耗。

顺带一提,香港的数据很完备,小房子基本能全部还原,只是带 height 的建筑物不算特别多。

数据源换了一次

jspacesystem 的 free request 估计是不会恢复了。

现在改成从 earthdata.nasa.gov 直接抓:通过 js 的 api 拿到下载用的 access token,然后从 S3 直接下 DEM。而且拿到的直接是源文件,连解压都省了。

这个站点使用量很大,短期内不太可能挂 —— 对一个业余项目来说,数据源的存活概率和数据质量一样重要。

意外的是它上面不只有 30m DEM。雪覆盖、地表颜色、湿度、温度都有。

一个还没验证的想法:抓四个时间点的地表颜色、湿度、温度,应该就能做到相当丰富的生成模拟。这需要继续深化 node 后端,把常用地点的数据都抓下来 cache 住,再处理成适合生成的形式。

还没解决的

  • 跨 chunk 的大 polygon 还是会翻车,现在是屏蔽了事;
  • 水面还是着色,不是几何;
  • 1:1 尺度会怎么样,没试过;
  • 多源数据(湿度、温度、地表颜色)怎么统一编排,目前只是想法。
评论