Planetfall:用技术复刻游戏世界地图
Daniel Huffman Uncategorized 2025年5月20日 2025年5月20日 24 分钟
各位读者,我刚刚完成了一个有趣的副项目,我相信只有极少数人会对它感兴趣。这是我职业生涯中最具技术挑战性的项目之一,我非常高兴能与大家分享。
大多数人可能想知道这是什么地方,但我希望对于一小部分人来说,这些名称能唤醒你们的记忆。这里是 Chiron 星球,是《Sid Meier’s Alpha Centauri》这款游戏的背景地(也是主角之一)——这是一款1999年出品的电脑游戏,拥有着一批忠实的追随者;我也身在其中。
我可以继续介绍这款游戏——它深刻、发人深省,并且拥有非常精美和经过深思熟虑的视觉语言——但我今天主要想谈谈这张地图。这个项目将我的技能推向了一些新的领域,所以即使您对这款游戏不感兴趣,我认为讨论它背后的技术细节也是值得的。
但在我们深入了解这些细节之前,我想提一下这个项目帮助我理解的关于真实地图和虚构地图之间区别的一些东西。最近,在我告诉别人我是制图师之后,他们问我绘制的是真实还是虚构的地点。对于外行人来说,将这两件事放在同等地位是合理的。但是对我来说,它们感觉完全不同。除了上面的地图之外,我几乎完全专注于现实世界,而且我发现我的大多数同事也是如此。当我努力思考这个人的问题时,我意识到这种分裂可能是因为幻想地图需要与真实地图不同的(但重叠的)技能组合。如果一位幻想或科幻作家要求我为他们绘制地图,我需要坐下来从头开始绘制一些新的东西。我没有接受过那样的工作方式的培训。我所有的经验都集中在操纵和设计地理数据上,而不是创建地理数据。
有些制图师可以并且确实可以处理虚构和真实的地点,但我们中的很多人,包括我自己,主要擅长用别人递给我们的食材烹饪,而不是自己种植食材。因此,我能够制作虚构地图的唯一原因是因为有实际的、预先存在的数据集可供我用来构建它。
在我们开始之前,我想提一下,虽然这张地图可以免费下载,但它非常费力。您的支持有助于我继续做这样的事情,所以如果您喜欢它,请考虑点击下面的按钮,和/或与他人分享我的作品——这对我有很大的帮助!
获取数据
《Alpha Centauri》这款游戏是在地图上进行的。该程序可以为您创建一个新的、随机的地图,但也有一个官方的、内置的星球地图,由游戏设计师 Chris Pine 精心制作。
官方地图是 128 × 64 个菱形像素。重要的是,每个像素都有几个属性:海拔、降雨量、岩石程度等等。这些属性是我绘制地图的基础。我从游戏地图中采样了各种数据集,然后用它们来构建我的海报。虽然,这通常说起来容易做起来难。
游戏地图的一部分。请注意地图中心突出显示的像素。在左下角,我们可以看到有关突出显示像素的数据:其海拔、它是干旱和多岩石的事实、原生生物(异种真菌)的存在,以及它位于坐标 (16, 50)。
我首先处理海拔数据。虽然游戏(当时或现在)拥有 3D 地形并不罕见,但《Alpha Centauri》在我看来有点不寻常,因为它实际上告诉您每个地图图块的确切海拔值。您可以在上图的左下角看到这一点。所以,我浏览了地图并记录了每个图块的海拔值。总共 8,192 个。然后我仔细检查了它们以确保我正确地转录了它们。正如您所能想象的那样,这花费了很长时间——很多个小时。我大约在 2022 年的某个时候开始了这一过程,并不时地进行调整,直到 2025 年完成。
如果您回顾上面的游戏地图片段,您会注意到一些图块上似乎有植被。这是游戏以可视方式象征图块平均降雨量的方式。只有三个等级:多雨、潮湿和干旱。幸运的是,我不需要手动采样每个像素来获取此信息。我的《Alpha Centauri》版本有一个模组,在原始游戏中找不到,它会向您显示降雨等级的主题地图。
我截取了整个地图的屏幕截图,并做了一些 Photoshop 工作来将不同的颜色分成三张地图,每张地图对应一个降雨等级。
然后我将每个地图导入 QGIS,并在顶部放置一个矢量点网格,每个网格正方形一个点。每个点都采样了底层的栅格并吸收了它的降雨等级。该数据集仍然需要一些仔细检查和纠正,因为还有其他地图项目阻碍了降雨数据。但是,我能够毫不费力地完成它。
我对岩石程度也做了相同的处理,岩石程度同样分为三个等级——但我最终没有使用该数据集。
最后是异种真菌,它是 Chiron 星球上的原生生物之一。那是上图中粉红色的地面覆盖物(尽管根据游戏手册,它是“深红色”)。该数据集是二进制的:有异种真菌,或没有。游戏中没有可用的主题地图,所以我只是导入了正常的游戏地图并用我的网格点对其进行采样,并寻找与粉红色/红色颜色范围相匹配的像素,然后进行了一些校正。
有了这些,我就拥有了制作地图所需的所有材料。虽然海拔数据花费了我数年的时间来完成采样,但其余部分都在几个小时内完成。
投影
与许多游戏地图一样,《Alpha Centauri》实际上发生在圆柱体上。您可以向左或向右无限移动,但是一旦到达顶部或底部,您就会停止;地图/世界结束。
尽管我们的地图是圆柱形的,但设计师们当然希望将这个星球理解为球形的。各种游戏内媒体,包括游戏标题画面,都显示 Chiron 是圆形的。所以我假设游戏地图就像大多数地图一样,是地球的 2D 表示。但是它的投影是什么?
每次您在游戏中建造基地时,该基地都会为您提供对特定区域的控制,并且无论它建在哪里,该区域的大小始终是相同数量的像素。因此,可以合理地假设每个像素代表地球上相同的空间量——因此,游戏地图位于等面积投影上。并且考虑到地图的直线形状(以及上面的图表),我们可以合理地假设圆柱投影。因此,我将游戏地图分配了一个 圆柱等面积投影。
圆柱等面积投影的纵横比根据您设置标准平行线的位置而有所不同。在这里,在我的 投影连接 海报的摘录中,您可以看到此投影的一些已命名的变体。每个变体仅在您设置标准平行线的位置上有所不同。
将其设置为 37.4° 使我获得了与游戏地图匹配的纵横比,并且该设置还意味着我们正在使用 Trystan Edwards 投影。为了让您更好地了解所有这些,下面是在相同投影下游戏地图和地球地图的比较。
右侧的地图由 daan Strebe 通过 Wikipedia 提供。
所有这些投影工作实际上都发生在数据采样之前。因此,当我说我将游戏地图导入 QGIS 并在其上放置矢量点时,我已经设置了一个投影来处理我正在生成的新数据集。
准备 DEM
此时,我有一个投影,并且在非常低分辨率 (128 × 64) 网格中有一堆数据。我处理这个项目的部分兴趣在于弄清楚如何制作比原始游戏地图更详细的地图,同时仍然符合关于 Chiron 星球的已知游戏内事实。因此,我想探索如何将我的 8,192 个海拔值转换为更详细的网格。
我尝试了很多很多事情。我花费了足够多的时间在上面,以至于实际上我不记得我的尝试中的很多细节。但是,经过许多死胡同之后,这是我最终采用的技术。我从海拔网格开始——每个图块一个海拔值,就像我们在游戏中一样。
原始游戏海拔的片段,靠近 Planetneck。
然后我在上面散布了一堆随机点,同时强制执行它们之间的最小距离,这样它们就不会太拥挤。我最终在每个网格图块上获得了大约 1-3 个点。
少数网格图块最终没有点。我添加了额外的点来覆盖这些点(我只是找到了每个图块的质心)。然后我给每个点它所在的网格图块的海拔值。所以,我有效地将我的原始海拔网格图块变成了一堆分散的点,有些海拔值重复多次。
然后我运行了一个 TIN(三角不规则网络)插值来生成一个初始海拔模型:
现在,如果我没有进行任何点散射,而只是使用了我的原始海拔网格,那么它看起来会像这样:
请注意它如何更像网格和更规则。通过移动所有点,有时复制它们,我能够打破该网格,并随机地给某些区域与另一些区域相比更多的权重。第一个插值看起来比第二个插值更有机,但它仍然使用原始海拔值。
这只是该过程的第一次迭代。实际上,我回去并多次改进它。虽然移动所有海拔点并复制它们很有帮助,但它不够详细。我想要更多的高程点,这样我就可以拥有更精细的地形比例。为此,我首先在我的随机点上进行了 Delauney 三角剖分。这只是在所有点之间绘制三角形。然后我找到了每个三角形中心的点。
然后我为每个新点分配一个海拔值,只需取附近三个海拔值的平均值即可。最后,我添加了一些随机噪声:我将每个点的高程值随机向上或向下稍微移动一点,以便它们代表地形中的新颠簸,而不是现有值的平滑延续。这是它的外观。
我重复了几次三角剖分和碰撞的过程(每次减少碰撞量),直到我有了大量的点。
这是当插值为高程表面时的外观:
与我们开始时相比,这是一个很大的改进。它是有机的和详细的,虽然它不是完美的,但它是朝着正确方向迈出的一大步。
在进行最终插值之前,我对高程点字段进行了一些其他调整。这些是为了帮助我的结果与 Chiron 星球的标准地图相匹配。有时,由于插值的性质或我引入的随机噪声,事情并没有完全按计划进行。
上面,您可以看到标准地图,上面覆盖了我插值的陆地/水域掩模。在标准地图的顶部中心有一个岛屿,但在我插值的高程模型中,它实际上(几乎)与大陆相连。这仅仅是因为我在该区域有一些高程点高于海平面,而另一些高程点低于海平面,而高于海平面的那些点在插值中胜出。
这是另一种需要调整的情况:
标准地图上的一个岛屿在我插值的高程模型中只有几个像素宽。我希望它更大一点,以便在最终地图上可以注意到它。
我手动查看了地图以找到像这样的地方,并添加了一些额外的高程数据点,以纠正像封闭海峡、微小岛屿等问题。
我还必须手动调整原始地图的一个主要特征:Garland Crater。这是它在标准地图上的外观:
但请记住,该地图基于一个底层菱形网格。当查看实际海拔值时,火山口更像是一个正方形。
但是,火山口显然应该是一个圆形。游戏设计师甚至绘制了特殊的地图图块来将其表示为圆形。因此,我取消了地图这一区域的平方。我做了一些橡胶板处理来移动我的原始高程点,直到我得到更圆形的东西。
它在这里看起来有点垂直拉伸,但这仅仅是由于投影。
添加手动点并使火山口圆形化后,我准备再次执行三角剖分和插值步骤。我在这里按错误的顺序解释事情——在这个整个过程中有许多小时的试错和修订。但这就是我所做的。
一旦我进行了插值,我就对它进行了一些平滑处理。我不记得确切的公式——它有点像混合油漆,因为我一直在玩,直到我得到看起来正确的东西。我通过 Karika 插件 进行了一些焦点统计(平均值和中位数),一些线积分卷积,我还添加了一点 Perlin 噪声。我将平滑层与未平滑层混合在一起,然后再次平滑。同样,这是一个非常随意的过程。
最后,这是我想出的:
将其与仅对原始、未修改的高程点(在任何随机化、手动补丁、三角剖分等之前)进行简单 TIN 插值进行比较:
在图像中心附近,并在其左侧一点,您可以看到原始的正方形火山口形状。
我认为所有这些调整的过程产生了一些更有机的东西。
但是,我还没有完全完成高程模型。这种插值对于大多数区域来说都很好,但是极点需要注意。鉴于它们在上面的地图上被拉伸的程度,我知道我需要使用在这些区域中失真较小的投影来重新进行南北极的标高插值。例如,这是我提出的北极 DEM,以及原始高程点位置。
由于极点的大部分都没有游戏数据,因此我在中间撒了一些主要随机的噪声。然后,我按照处理主要高程模型的方式,继续随机化我的点位置并添加额外的三角剖分点。
我为南北极制作了单独的 DEM,以用于未来可能关注这些区域的任何制图。我还将它们混合回主 DEM 中。这样,我的高程模型就完成了!
除了它没有完成。当我完成地图制作过程的其余部分时,我不断发现小的错误:丢失的湖泊、微小的岛屿等。与我在上面显示的那些非常相似,只是这次我在完成 DEM 之前没有发现它们。我尽我所能地修补了这些东西,要么通过对高程模型进行微小的更改,要么更常见的是,通过在制图过程的后期手动绘制湖泊和岛屿。说到这里,让我们实际开始用所有这些数据制作地图……
投影的乐趣
对于布局中的主地图,我决定我想使用 orthoapsidal (Raisz Armadillo) 投影 显示 Chiron,它恰好是我最喜欢的投影之一。
我从 Wikipedia 窃取的另一张 daan Strebe 地图。
我喜欢它明确地提醒读者他们正在处理一个曲面。鉴于游戏玩家习惯于只看到平面表示,我认为尝试展示更圆的东西来使其栩栩如生特别重要。
我以前从未有机会使用这种投影制作地图。它在 QGIS 中不可用,所以我必须创建自己的小投影脚本。幸运的是,Wikipedia 具有转换公式。这些信息,加上一些 Python 知识(在 ChatGPT 的帮助下,因为我仍然不是很擅长 PyQGIS),让我开始使用 orthoapsidal 投影。
我最终修改了投影。为了更好地适应 Chiron 的陆地,我调整了垂直倾斜量和方向(默认值为 20°,但我将其更改为 -10°)。我还减少了侧面的曲率。orthoapsidal 的缺点是,通过显示曲率,可以很好地感觉到圆形,我们失去了一些区域(澳大利亚被切断在上面的地图上,南极洲也是如此)。但是,通过巧妙的调整,我能够减少曲率。我首先水平缩小我正在投影的任何内容:
然后,当它被投影时,它在环面上占据了更少的水平空间,因此没有几乎扩展到侧面:
我在这里使用地球陆地,以便您可以使用更熟悉的形状来了解投影。
因此,我避免了在左/右边缘发生的严重透视失真。请注意上图的右边缘,当陆地从观看者处弯曲时,陆地如何开始真正压缩在投影边缘。但是,绿色版本没有那么糟糕。通过缩小陆地,我将其更多地保留在最直接面向观看者的环面的部分上。另一种思考方式:实际上,我有点扩大了环面,相对于地图而言。
完成投影后,我将事物水平拉伸了一点,以撤消我所做的缩小。
不幸的是,我的脚本只处理矢量,我太不耐烦了,无法深入研究并使代码与栅格一起工作。因此,为了投影像高程模型这样的东西,我做了一个 hacky GIS 的事情,只是将每个像素变成一个多边形,投影它,然后重新栅格化它。那不是理想的,这意味着很长的处理时间,但它工作得很好。
此时,我们可以开始逐层分解我的地图,就像我经常在这个博客中所做的那样。
最后,实际制图
因此,让我们分解一下主要地图,该地图主要在 Photoshop 中构建。首先我们从测深开始。
这是一个黑白栅格,显示了我的(重新投影的)DEM 中海拔低于零的部分。对此,我应用了一个渐变图来将所有内容重新着色为蓝色阴影。
Photoshop 中的渐变图会根据其灰度值重新着色底层栅格中的每个像素。因此,它可以(除其他外)将黑到白栅格转换为新的颜色斜坡。
接下来:初始陆地。我拍摄了我的重新投影的高程模型,剪掉了任何水下值,并通过 Eduard 运行它,Eduard 是一个模拟瑞士风格手绘阴影浮雕的程序。
我花了一段时间来调整设置,以试图找到一个好的平衡。请记住,我编写了一些此信息,因为我想要一个现实细节的浮雕模型。但是,我也不想显示太多我添加的噪声。完成后,我添加了一点沿海内部发光,以帮助将陆地与水域分开。
它还具有隐藏海岸线附近浮雕的优势,因此当我们到达海岸时,附近的任何山脉都会逐渐消失。这给了我们一点点沿海平原。我的地图中几乎没有平坦区域——这是游戏中非常崎岖的高程值的结果。山脉突然出现和消失,通常就在海岸旁边。我想让海岸稍微不那么崎岖。
接下来,我使用色相/饱和度层来着色浮雕:
然后我在上面撒了一些颜色噪声,以帮助它感觉更自然。
我只是通过使用几个调整层来做到这一点:另一个色相/饱和度,和一个亮度/对比度,并且让这些层中的每一个都由一个包含一些小斑点噪声的通道进行掩蔽:
接下来是绿色。就像标准地图一样,我打算使用降雨强度数据来显示植被。首先,我复制了浮雕并将其着色为绿色,包括这些颜色的一些嘈杂变化。
我以与之前完成棕褐色/棕色浮雕完全相同的方式做到这一点:一个色相/饱和度层来着色它,加上几个调整层来添加一些嘈杂的较浅/较暗区域斑点。
由于较深的绿色,一些浮雕正在丢失,因此我还添加了一些更多调整以强调它。这些只是更多的调整层,可以使底层图像变亮/变暗。
我使用了这些调整层中的掩模中的浮雕阴影/高光,以便它们仅使正确的区域变亮/变暗。这是其中一个掩模的内部视图:
现在我将绿色植被版本的浮雕与原始棕色浮雕混合。
有些区域仍然是棕色的(在游戏地图上标记为“干旱”),有些区域是绿色的(在游戏地图上标记为“多雨”或“潮湿”)。但是,我是如何进行这种混合的?这是控制绿色图层不透明度的掩模的视图:
在宏观层面上,它来自我很久以前收集的那些降雨数据。请记住,游戏中的每个网格图块都有 3 个降雨等级之一,并且我有一个该数据集的点表示。我对这些点进行了薄板样条插值(在重新投影之后)。
那是我的 Photoshop 掩模的基础。但是,我也使它有点嘈杂,如果您放大 Photoshop 版本,您可以看到:
这是使用一系列 Photoshop 滤镜完成的。首先,我利用了 溶解 混合模式。这是我 之前 在这个博客上讨论过的一种模式。下面,我有一个纯黑色图层,它用降雨数据进行了掩蔽。
在溶解模式下,Photoshop 基本上使用掩模图层来决定像素出现的可能性。没有半透明像素:它们要么完全透明,要么完全不透明。因此,这会将我们的降雨数据转换为点散射。
我将这种点散射平铺(带有白色背景)成一个图层,然后制作了一堆副本。我以不同的数量模糊了每个副本,并将它们全部堆叠在一起。然后进行了一些重新锐化。最后,我留下了一个斑点状但柔软的噪声纹理。
而 这 就是我用来控制绿色图层混合到棕褐色中的位置。如果我们仔细放大,您可以看到它的工作原理。
我完成了所有这些工作,以确保植被图层看起来更像 植被(即单个植物)。虽然,老实说,除非您有放大镜,否则很多东西都不会显示在地图的打印版本上。但是,大多数人会在屏幕上观看它,因此他们希望仍然会欣赏它。
如果我只是使用原始的、未斑点、平滑的降雨图层作为掩模,它会是什么样子:
现在,当我们从多雨的区域到干燥的区域时,植被只是逐渐消失。它看起来不太自然。在沙漠的边界,个别树木不会突然变得透明。相反,您仍然有树木,但您拥有的树木越来越少。这就是斑点完成的事情。
接下来:异种真菌。这是 Chiron 的原生生物之一。它在游戏地图和各种游戏内媒体上显示为粉红色或红色。我使用与绿色植物几乎完全相同的过程将其添加到地图中。我插值点数据,稍微平滑一下,使其变得嘈杂,并将其用作红色浮雕的掩模。
接下来:河流。其中有少数出现在游戏地图上。它们非常方形,因为它们仅限于地图网格。为了使它们看起来更真实,我在 QGIS 中手动重新绘制了它们。
对于制图方面,我在 Adobe Illustrator 中给了每条河流的源端略微的锥度,然后将它们带到 Photoshop 中,对它们进行着色,并给它们一个小小的向下斜面,使它们看起来像刻在陆地上。这是一件小事,在最终印刷品上没人会真正注意到,但如果您 不 这样做,您就可以分辨出来。
我 确实 看了看根据我的 DEM 生成一组新河流。我运行了一些水文分析工具,但是,由于地形的崎岖性质(既由于我的噪声,也由于原始游戏地图高程),我主要得到了一堆内陆盆地和短粗河流。绘制我自己的更容易(并且我看着我的浮雕模型以确保我没有越过任何山脊)。
这结束了陆地上的一切。我将剪贴蒙版应用于所有这些东西,因此真菌/绿色/棕褐色浮雕等只会出现在它们应该出现的地方。我还给陆地涂了一点外发光,以照亮沿海水域。这是我们现在在 Photoshop 堆栈中的位置:
只剩下几个图层了!接下来是更多的异种真菌。真菌不仅发生在陆地上,而且也发生在水中。我不想使用完全相同的颜色,所以我单独添加了水真菌。只是一个简单的紫色图层,它使用与陆地真菌相同的斑点状掩模,但也在一个仅将其裁剪到水的图层中。它设置为通过 线性加深 混合模式混合到底层水中。
接下来,我对整个地图应用饱和度提升,并添加一个方格网络以帮助可视化曲率。饱和度的增加是我最初偶然完成的,只是在 Photoshop 中移动了图层。但是,我喜欢它的外观,所以我保留了它。
结果是一种有点超出我的舒适区的配色方案。我倾向于倾向于饱和度较低的颜色,并且以我喜欢在 单色 中工作而闻名。但是,这是一个副项目,所以现在是我进行实验的好时机。
接下来,我给了事物一个半油漆的外观。我复制了地图,将其平铺到一个图层中,将其缩小,然后在 Photoshop 中运行了一个 干画笔 滤镜。这给了事物一个我真正喜欢的有些油漆的外观。
上面,您可以看到之前和之后。在中心,我们有预过滤的版本。在外面,您可以看到应用效果后更柔和的版本。我一直想做一段时间关于这项技术的教程,今年可能会发生。
柔和度有点 太 模糊了,所以我实际上将它与原始地图 50-50 混合,我认为这给出了细节和绘画般的柔和度的良好平衡。
我们