- 前端
- 图形学
- 3D渲染
【免费下载链接】Fantasy-Map-Generator
Web application generating interactive and highly customizable maps
本文围绕 Azgaar's Fantasy Map Generator v1.73.2 引入的**预定义高度图(Pre-defined Heightmaps)**功能展开:它让用户可以像选择模板一样直接挑选基于真实世界数据制作的高度图(如地球、欧亚大陆、地中海等),一键生成形态贴近真实地理的新地图。读完本文,你将掌握内置高度图库的完整清单、在界面中的操作入口、底层图像转高度图的实现原理、与"高度图模板"机制的区别,以及如何按官方规则自建高度图并入自己的地图。
功能由来:为什么需要"内置"高度图
在 v1.73.2 之前,社区里经常有人问"去哪里找一个能生成地球或指定区域地图的 seed?"。虽然项目一直支持导入图片并转换为高度图(Import Heightmap),但"导入 → 转换 → 生成"这条链路对新用户并不直观,很多人根本不知道存在这个功能。
开发者因此在生成器内部直接内置了一批预定义高度图,其使用方式与已有的"高度图模板(heightmap templates)"完全一致:选择高度图 → 创建新地图。这批图片由作者基于公开的真实世界地理数据制作(数据源为 tangrams.github.io,即tangrams/heightmapper在线地形渲染工具),因此每一张都是可考证的真实地貌而非随机噪声。
从当前仓库的代码结构看,这一功能的数据层、UI 层与生成层是分离的:
- 数据清单:src/data/precreated-heightmaps.ts
- 图片资源:public/heightmaps/
- 选择对话框:src/controllers/heightmap-selection.ts
- 核心转换逻辑:src/generators/heightmap-generator.ts
内置高度图库:23 张真实世界地形一览
precreatedHeightmaps是一个以资源文件名(key)为键的注册表,每个 key 精确对应public/heightmaps/<key>.png这张灰度高度图,并附带一个用于 UI 展示的name:
| key | 显示名称(name) | id |
|---|---|---|
africa-centric | Africa Centric | 0 |
arabia | Arabia | 1 |
atlantics | Atlantics | 2 |
britain | Britain | 3 |
caribbean | Caribbean | 4 |
east-asia | East Asia | 5 |
eurasia | Eurasia | 6 |
europe | Europe | 7 |
europe-accented | Europe Accented | 8 |
europe-and-central-asia | Europe and Central Asia | 9 |
europe-central | Europe Central | 10 |
europe-north | Europe North | 11 |
greenland | Greenland | 12 |
hellenica | Hellenica | 13 |
iceland | Iceland | 14 |
indian-ocean | Indian Ocean | 15 |
mediterranean-sea | Mediterranean Sea | 16 |
middle-east | Middle East | 17 |
north-america | North America | 18 |
us-centric | US-centric | 19 |
us-mainland | US Mainland | 20 |
world | World | 21 |
world-from-pacific | World from Pacific | 22 |
注册表末尾还通过window.precreatedHeightmaps = precreatedHeightmaps暴露到全局,这是为兼容旧版 JS 调用保留的桥接(见 src/data/precreated-heightmaps.ts)。
以world为例,这张基于真实地球数据制作的全景高度图直观展示了功能的核心产出——真实大陆轮廓与海拔分布:
europe则体现了同一机制在区域尺度上的效果,适合生成欧陆风格奇幻世界:
图片为
public/heightmaps/目录下的原始灰度数据文件,生成地图时即由渲染管线按此灰度采样海拔。
宽高比的现实约束
v1.73.2 更新说明中特别强调了一个限制:这些高度图是按常见屏幕比例(约 2:1)制作的。当时作者的原话是"目前我们只能假设大多数屏幕是 2:1 或类似比例,否则高度图会被扭曲(heightmaps will be screwed)"。
这个约束在代码中得到了印证:转换时图片会被强制拉伸到与目标网格完全一致的尺寸(见下文"图像转高度图的实现原理"),因此如果新建地图的长宽比与源图差异过大,大陆形态就会在水平或垂直方向被明显拉伸变形。建议做法:在选项面板中保持地图比例为 2:1(例如默认的 960×540),或选择与目标比例相近的高度图。
使用指南:三步选好高度图并生成新地图
预定义高度图与"高度图模板"共用同一个选择入口,位置在Options(选项)面板 → Heightmap(高度图)行。该行的下拉框默认是pointer-events: none(不可直接操作),点击该行任意位置会打开完整的选择对话框,相关绑定见 src/components/options/tabs/options-tab.ts 中templateInputContainer的点击监听。
高度图选择对话框(Select Heightmap)
对话框由 src/controllers/heightmap-selection.ts 中的HeightmapSelection.open()渲染,包含两个并排的网格区与一组预览选项:
- Heightmap templates(高度图模板):每个模板用固定种子渲染一张预览图,模板特性是"每次生成结果不同但风格相似"(见
data-tip:"template provides unique, but similar-looking maps on generation")。 - Precreated heightmaps(预定义高度图):按
precreatedHeightmaps注册表顺序渲染 23 张缩略图,其特性是"每张地图的形态完全一致"("it will be the same for each map")——因为它直接来自真实数据的采样结果,不随种子变化。 - Options(预览选项):
Redraw preview:重绘全部预览缩略图;Render ocean heights:是否绘制水体单元的海拔(默认关闭);Color scheme:预览使用的配色方案,来自heightmapColorSchemes;Edit Templates:打开模板编辑器(会清空当前地图,需二次确认);Import Heightmap:打开图片转换器(同样会清空当前地图)。
对话框底部有三个动作按钮:
- Cancel:关闭对话框,不做任何修改;
- Select:把所选高度图的 key 写入
options.generation.template并通过Pins.set("template", …)记录,供下一次生成使用; - New Map:在 Select 的基础上,用当前种子立即触发
regeneratePrompt({ seed, graph, ...config }),即马上用所选高度图生成一张新地图。
交互细节:点击任意缩略图即选中(setSelected高亮该卡片);模板卡片右上角的旋转图标可单独重新生成该模板的预览(regeneratePreview会更换该卡片的种子)。
关键调用链
Options 面板点击 Heightmap 行 └─> Controllers.HeightmapSelection.open() ├─> 读 options.generation.template 同步选中态 ├─> 按 graphConfig 设置预览宽高比 --preview-aspect-ratio ├─> 模板区: HeightmapGenerator.fromTemplate(graph, key, config) → drawHeights() 渲染预览 └─> 预定义区: HeightmapGenerator.fromPrecreated(graph, key, config) → drawHeights() 渲染预览 └─> 点击 New Map └─> Options.set(generation.template = id) + Pins.set("template") └─> regeneratePrompt({ seed, graph, ...config }) → 生成新地图测试用例 src/controllers/heightmap-selection.test.ts 验证了"预览高度与最终生成高度完全一致":选择 1600×900 / 800×600 等不同尺寸与点数后,对话框内drawHeights收到的最后一份预览高度数组,与generate()之后grid.cells.h的逐元素结果相等(expect(Array.from(generated)).toEqual(previewHeights)),保证所见即所得。
图像转高度图的实现原理:从 PNG 灰度到海拔数组
预定义高度图的核心转换发生在 src/generators/heightmap-generator.ts 的HeightmapModule.fromPrecreated()(第 592-623 行),流程如下:
- 建 Canvas,1 像素对应 1 个网格单元:以目标网格
cellsX × cellsY为画布尺寸创建 canvas; - 加载图片并强制重采样:
ctx.drawImage(img, 0, 0, cellsX, cellsY)把源 PNG(无论原始分辨率)拉伸到网格尺寸,随后getImageData逐像素读出 RGBA; - 灰度转海拔:调用私有方法
getHeightsFromImageData()(第 583-590 行),对每个单元取红色通道作为亮度lightness = imageData[i * 4] / 255,再套用非线性功率曲线提升暗部对比度:
const powered = lightness < 0.2 ? lightness : 0.2 + (lightness - 0.2) ** 0.8; this.heights[i] = minmax(Math.floor(powered * 100), 0, 100);即:亮度低于 0.2(海洋/近海)保持线性映射,高于 0.2(陆地)则按 0.8 次幂压缩,使中等海拔的陆地获得更丰富的细节层次,最终归一到0–100的海拔范围。
- 写回网格:
graph.cells.h = heights即完成高度图落地,后续生成管线(气候、水系、生物群系等)全部基于这套海拔工作。
其中fromPrecreated返回 Promise(图片异步加载),并有onerror兜底报错Cannot load heightmap <id>。而generate()的统一入口(第 557-565 行)会先以Math.random = Alea(options.map.seed)重置 PRNG,再根据id in heightmapTemplates判定走模板脚本还是图片路径,最后clearData()释放占用的预览网格。
与高度图模板(Templates)的本质区别
理解二者差异是正确选型的前提:
| 维度 | 高度图模板(Templates) | 预定义高度图(Precreated) |
|---|---|---|
| 数据来源 | 内置脚本语言描述的步骤序列(Hill/Pit/Range/Trough 等 10 种工具) | 真实世界灰度 PNG 图片 |
| 生成结果 | 同模板 + 不同种子 → 不同但风格相似 | 同图 → 每张地图形态固定一致 |
| 是否受种子影响 | 受种子影响(AleaPRNG 驱动) | 不受种子影响(纯图像采样) |
| 实现函数 | fromTemplate()逐行解释脚本 | fromPrecreated()Canvas 采样 |
| 可编辑性 | 可在Edit Templates中直接改脚本 | 需通过Import Heightmap导入自定义图片 |
模板的脚本语法(每个工具一行:工具 参数)定义在 src/data/heightmap-templates.ts,例如volcano模板:
Hill 1 90-100 44-56 40-60 Multiply 0.8 50-100 0 0 Range 1.5 30-55 45-55 40-60 ...每种内置模板还带probability权重(用于随机生成时按权重挑选),14 个模板合计构成随机世界的多样性来源——而预定义高度图不参与随机挑选,始终由用户显式指定。
自建高度图:官方制作流程与导入
public/heightmaps/import-rules.txt给出了作者制作这批高度图时的完整操作规范,按此流程可以做出海拔比例正确(明暗对应真实地形)的自定义高度图:
- 打开 https://tangrams.github.io/heightmapper;
- 关闭自动曝光(Toggle off auto-exposure)——避免亮暗被算法重映射;
- 设置最高海拔为 2000(
max elevation); - 设置最低海拔为 -500(
min elevation); - 框选目标区域;
- 渲染导出图片;
- 可选:把图片缩放到较小尺寸(如 500×300 px)——高分辨率并不会被利用,因为生成时会重采样到网格尺寸,小图反而节省加载与处理开销。
导出后即可在Select Heightmap 对话框 → Import Heightmap导入这张图片(该入口会先弹出确认:"Opening the tool will erase the current map"),转换逻辑与内置图完全一致——这正是"导入 → 转换 → 生成"那条旧链路,也是内置功能的实现基础。
种子、模板与可复现性
预定义高度图与种子机制配合时有一个值得注意的规律:种子决定的是种子点分布、水系、国家与文化等随机内容,而大陆轮廓完全由高度图锁定。因此:
- 同一张高度图 + 不同种子 → 共享同一套真实海岸线,但国家、文化、城市各不相同;
- 高度图的 key 会作为
template选项被写入地图历史与 URL 参数(Pins机制见 src/components/pins.ts),通过 src/components/seed.ts 的restoreSeed()可从"Seed history"对话框还原"种子 + 尺寸 + 模板"组合,从而精确复现地图。
在选项面板中,"Heightmap"下拉框的显示名称由syncOptionInputs()统一处理:heightmapTemplates[id]?.name || precreatedHeightmaps[id]?.name || id(src/components/options/tabs/options-tab.ts),即模板与预定义高度图共享同一选项槽位,二者的 key 互不冲突(模板 key 如volcano,预定义 key 如world)。
小结与扩展阅读
预定义高度图是 v1.73.2 对"想生成真实世界地图"需求的一站式回应:数据侧内置 23 张真实地形灰度图,交互侧与模板共用Select Heightmap对话框,实现侧通过 Canvas 重采样 + 功率曲线映射把图片转化为 0–100 的海拔数组。它绕过了"先找图再导入"的隐性门槛,也让固定地理形态的创作(如复原地球、欧陆、地中海世界)变得可复现、可分享。
相关资源与源码索引:
- 高度图资源目录:public/heightmaps/
- 高度图注册表:src/data/precreated-heightmaps.ts
- 模板注册表(脚本语法):src/data/heightmap-templates.ts
- 转换与生成实现:src/generators/heightmap-generator.ts
- 选择对话框 UI:src/controllers/heightmap-selection.ts
- 选择对话框测试:src/controllers/heightmap-selection.test.ts
- 自建高度图规范:public/heightmaps/import-rules.txt
- 高度图编辑器(含模板编辑与图片转换):src/controllers/heightmap-editor.ts
- 种子与历史恢复:src/components/seed.ts
- 前端
- 图形学
- 3D渲染
【免费下载链接】Fantasy-Map-Generator
Web application generating interactive and highly customizable maps
相关推荐
10个技巧快速掌握Fantasy-Map-Generator自定义地图样式:终极指南
10个技巧快速掌握Fantasy Map Generator自定义地图样式:终极指南 Fantasy Map Generator是一款强大的开源Web应用程序,
前端图形学3D渲染Fantasy Map Generator Vignette 图层完整指南:SVG 遮罩原理、六种官方预设与自定义实战(v1.95.0)
Fantasy Map Generator Vignette 图层完整指南:SVG 遮罩原理、六种官方预设与自定义实战(v1.95.0) 导读 Fantasy
前端图形学3D渲染Fantasy Map Generator自定义模块开发终极指南:如何创建个性化地图生成器
Fantasy Map Generator自定义模块开发终极指南:如何创建个性化地图生成器 Fantasy Map Generator是一款功能强大的Web应用
前端图形学3D渲染
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考