# Generating fantasy maps——来生成虚拟地图吧！【未完】

英文教程（作者著）：https://mewo2.com/notes/terrain/

GitHub地址：https://github.com/mewo2/terrain

对特定段落的描述，在英文教程页面使用的对应的模拟器，会产生极佳的阅读效果。

本人出于兴趣，翻译英文教程。文中测试片段无法搬运，建议具备英文能力的小伙伴直接看英文教程。

（转载本文，需注明本文链接。请让我涨个粉，蟹蟹[滑稽脸]）

# Generating fantasy maps

制作虚构地图

These are some notes on how I generate the maps for my Twitter bot @unchartedatlas, which is based on a generator I originally produced during NaNoGenMo 2015. There's JavaScript code for the generator on Github here, and the original messy Python generator code can be seen here.

这里是一些关于我的推特(@unchartedatlas)中生成的地图的注解。这基于我早前在NaNoGenMo 2015（*译者注：这个链接无需翻墙，是NaNoGenMo的介绍*），我将这些JavaScript代码存放在Github上，另外，还有一些原始的Python代码，如果你对它感兴趣可以点这里。

You may also be interested in this companion piece, which describes the placename generation.

或许你也对有趣的姊妹篇感兴趣，它描述了地名生成器。

## Inspiration

灵感

I wanted to make maps that look like something you'd find at the back of one of the cheap paperback fantasy novels of my youth. I always had a fascination with these imagined worlds, which were often much more interesting than whatever luke-warm sub-Tolkien tale they were attached to.

我想制作一种地图：它看起来像是我年轻时低价买过的简装本魔幻小说，我一直都很喜欢那些魔幻世界，这些世界通常更为有趣——相比较它所描述的托尔金故事。

At the same time, I wanted to play with terrain generation with a physical basis. There are loads of articles on the internet which describe terrain generation, and they almost all use some variation on a fractal noise approach, either directly (by adding layers of noise functions), or indirectly (e.g. through midpoint displacement). These methods produce lots of fine detail, but the large-scale structure always looks a bit off. Features are attached in random ways, with no thought to the processes which form landscapes. I wanted to try something a little bit different.

同时，我想制作一个具备“**物理基础**”的地形生成器。互联网上，很多文章都介绍了地形生成器，无论是直接生成（譬如：通过噪音函数增加图层），还是间接生成（譬如：通过中点位移方法），他们大多使用变量来模拟不规则噪音。这些方法都生成了许多优秀的细节，但在宏观层面，它们多数仍然是失调的。那些产品用了一些随机方法，但不考虑景观。而我想尝试一些不一样的东西。

There are a few different stages to the generator. First we build up a height-map of the terrain, and do things like routing water flow over the surface. Then we can render the 'physical' portion of the map. Finally we can place cities and 'regions' on the map, and place their labels.

通过一些不同的阶段来完成这个地图生成器。首先，我们构建一个地形高度图，做一些关于地表水流脉络的工作。接着，我们可以在地图上渲染“物理”部分，最后，我们可以在地图上放置城市和地区，以及它们的标记。

# Grids

栅格化

To represent the heightmap, first we need a grid of points. Although it can be simpler to work on a regular square grid, I much prefer to work on an irregular set of points for something like this. With a regular grid, it's very easy to run into weird artifacts, and you often have to do a lot of postprocessing to hide the effects of the grid. If you use an irregular grid, then there are a few things which are more complicated, but the structure of the grid helps to give the map a rough, organic feel, and you never have to worry about nasty linear artifacts in the finished product.

为表现一个高度图，首先我们需要一个点状网格。尽管一个整齐的方形网格很简单，但我更倾向于设置一个不规则的点状图。关于这个不规则栅格，它非常容易出现奇葩情况，所以你需要做很多后期处理工作，用于隐藏这些情况。此时，仍有一些较为复杂的事情，但这个栅格结构已经可以给地图形成一种有轮廓、组织的感觉，而不必担心出现难以忍受的做作痕迹（*译者注：linear artifacts本意是线性的人工产物*）。

*Note: this shows 256 (2^8) points, to make viewing easier, but the real generator uses 16,384 (2^14) points. I have a programmer's superstitions about always using powers of 2, which are more pleasing to the spirit of the machine.**注释：为了方便观察，这里展现了256(2^8)个点，生成器使用的是16384(2^14)个点的图像。我有一个程序员的迷信：认为2会象征力量，这或许就是机器的鼓舞吧。*

译者注：这里在附赠一张图，省得萌新无法理解点图为何与栅格化联系在一起：

The approach I use is the same as in this article, which is one of the better references out there on how to do non-fractal terrain generation. I won't go into too much detail here because that article explains it very clearly, with lots of diagrams.

我使用的方法源于这篇文章，这是一篇关于的生成分形地形的极佳参考，我不想在这里描述过多细节，这篇文章已经用大量的图表清晰描述。

I start by selecting points at random within the map. These points tend to be a bit clumpy and uneven, so I use Lloyd relaxation to improve the point set. For speed, I only use one iteration of this process, but you can repeat it as many times as you like. There are rapidly diminishing returns after a few iterations though.

我从在地图上随机选点开始，这些点通常都比较散碎和不均匀。所以，我是用艾劳德放松算法改善这些点的设置。为了效率，我只对这个程序迭代了一次，如果你喜欢，你可以继续下去，尽管数次迭代后，能获得的成效就会迅速降低（边际收益递减）。

All of the calculations are actually carried out on the 'dual points' of the original point set, which correspond to the corners of the Voronoi polygons. This has the advantage that the number of neighbours per node is fixed at three, which helps in some parts of the code.

实际上，所有的运算都源于“对偶点”的原始点集，它被运用到泰森多边形的角。这是一种优势：每个邻居的节点被固定为三个，这将有助于代码的某部分（额，哪部分？黑人问号.jpeg）。

# Rough outlines

大致轮廓

One of the difficulties of creating landscapes in a realistic way is that real landscapes aren't created all at once. Instead, they evolve from earlier landscapes, which in turn evolved from even earlier landscapes, and so on back for billions of years. There's no good way to simulate this process in a reasonable amount of time, so we need to cheat slightly.

有些困难的是，我们要通过模拟现实的方式，创造一种自然真实的地形。原因就是：因为更早地形的出现，如今的地形才得以产生和进化，而这个更早的地形则源于再早一些的地形，如你所见，这样的迭代发展持续了数亿年。如果要模拟这样的过程，可就一点也不高效方便了。

Rather than an infinite regress of older landscapes, I start with a simple 'proto-landscape', built with geometric primitives. This lets me control the broad outlines of the terrain, while leaving the details for the more physical processes to fill in later.

所以我们需要稍稍作弊一下^{1}，而非疯狂的追朔早期地形迭代。通过构建几何元素，我从一个简单的原形地形开始。这让我们得以控制地形的边界轮廓，同时留下更多物理过程的细节。

Some useful primitives which we can add together:

使用一些元图形，我们可以将其叠加：

- Constant slope - if you want to pretend this is physically motivated, think of it as tectonic uplift on one side of the map
- Cone shapes - these can be islands or mountains, or if inverted, lakes or seas
- Rounded blobs - these make better hills, and can be scattered all around to make a noisy surface

- 斜坡 - 如果你需要模拟在地图的一边构造隆起地表的物理情景。
- 锥形 - 这些可以生成岛屿、山脉，或反转它的海拔，使之成为湖泊。
- 圆形斑点 - 这将制造更好的丘陵，它可以四散分布，从而产生更为真实的地形。

We also have a few operations which are handy:

我也提供了一些比较便利的操作：

- Normalize - rescale the heights to lie in the range 0-1
- Round - normalize, then take the square root of the height value, to round off the tops of hills
- Relax - replace each height value with the average of its neighbours, to smooth the surface
- Set sea level - translate the heightmap up or down so that a particular quantile is at zero

- 标准 - 重置(边界)线的高度，范围是 0 - 1。
- 圆 - 规范(？)，获取高度值的平方根，以此环绕丘陵的突出部分。
- 缓和 - 通过遍历，以相邻高度的平均值替换高度值，用以产生平滑的曲线。
- 设置海平面 - 将特定位转译成 0，来提升或降低高度。

The particular sequence of primitives and operations used can be varied to produce different kinds of landscape, such as coastlines, islands and mountain ranges.

使用特定顺序的基本操作，能产生多样的风景，比如海岸线、岛屿与山脉。

*Note: the black line indicates the zero contour, which we treat as 'sea level'. Also, this map uses 4,096 (212) points, for speed.**注释：那条黑线显示了 0 水平单位的等高线，这将被当作海平面。为了效率，张地图采用的是4096(2^12)个点。*

# Erosion

侵蚀作用

The results of this process can be a little bit on the blobby side, which means they rarely look good on their own. We want to scuff them up a bit, so they look more like real landscapes. We do this by applying an erosion operation.

上一段的结果将在地形边缘产生「点滴状」的地理边界，这意味着大部分的地形都不太可信。我想增加一些它的磨损，令它看起来更真实。我们使用侵蚀操作来完成这一步。

*（译者注：这句实在太难翻译了，想象一些水滴落在地上的画面，稍微带点锯齿的模样，增强「磨损」就是一把锉刀，将锯齿打磨的更为尖细曲折。如果还是无法理解，强烈推荐你去使用一下作者为上一段提供的模拟器——在英文教程的页面）*

In most of the world, by far the largest influence on the shape of landforms is fluvial (water-based) erosion. Water flows downhill, carrying sediment along with it, carving out valleys and river basins. This is a massively complex phenomenon, and modelling it correctly is a very active research area, but we can get a long way by sketching a simple version of the process.

在这个世界的大部分地区，迄今为止对塑造地形影响最大的就是河流（基于水流）的侵蚀。水携带着水底与两岸的东西，向下流动，从而冲刷出山谷与流域。

We need to start by tracing the routes that water would take over the grid. For each grid point, we say that water flows to its lowest neighbour, and so on down until we reach the edge of the map. This gives a map of water flow.

There's an obvious problem when we reach gridpoints which are lower than all of their neighbours. Do we route the water back uphill? This will probably lead to cycles in the water system, which are trouble. Instead, we want to fill in these gaps (often called sinks or depressions), so that the water always runs downhill all the way to the edge.

It's easy to see how to fill in a single gridpoint, but as the depression gets bigger, and possibly links up with other depressions, the number of possible cases multiplies enormously. Luckily, there's an algorithm for filling depressions, called the Planchon-Darboux algorithm.

## Aside: the Planchon-Darboux algorithm

The algorithm works by finding the lowest surface with the following two properties:

- The surface is everywhere at least as high as the input surface
- Every non-edge point has a neighbour which is lower than it
To calculate this, we start with an infinitely high surface everywhere except on the edge, where we use the original heights. Then, on each iteration, we find points which have a neighbour which is lower than them, and set their height to their original height, or the height of their lowest neighbour (plus a small amount), whichever is higher. We halt when we can go a full iteration without changing any point.

There are various ways of speeding up this algorithm, mostly by tweaking the order in which points are visited. For more details, and a proof of correctness, you can read the original paper.

With the water routing calculated, we can work out how much water is flowing through each point. I assume that rainfall is constant across the whole map, and iterate through the points in descending order, passing the rainfall, plus the accumulated water flux, from each point to its 'downhill point'. This gives a map of water flux, which usually converges into a nice branching river structure, with lots of small streams feeding a larger central channel.

To calculate erosion, I combine the water flux with the slope at each point, as calculated based on the triangle of its neighbours. The exact formula I use is the product of the slope with the square root of the water flux. This isn't necessarily very physical, but it does give nice-looking results. I also add a small term which is proportional to the slope squared. This prevents deep gorges from forming, which might be physically realistic, but don't look good in the graphical style I've chosen.

I find it's very important to cap the erosion rate, otherwise strange things can happen. A little goes a very long way with this. Also, erosion always lowers the surface, so it usually helps to drop the sea level afterwards to match.

A final tweak to the heightmap is to smooth out the coastlines slightly. The erosion tends to produce quite rough terrain, which becomes tiny islands when cut off by sea level. A few of these can look good, but too many just looks messy. I repeatedly apply a filter where points which are below sea level, but a majority of whose neighbours are above sea level, get pulled up, and vice versa for points which are above sea level and have undersea neighbours. A couple of repeats of this produces a much cleaner coastline.

# Rendering terrain

地形渲染

# Cities, borders

城市，边界

# Placing labels

放置标签

那些专业而令我敬畏的美国、欧洲的编剧和作者们，他们最为精彩的著作，总是将人的志向和欲望加以正当化，却又用他所在乎的人来阻止他的成功，加以掺杂和反复，像一根锋锐刻薄的银针，无时无刻不在刺破受众地内心，令他们心潮迭起，久久难平。

——时隔多年，再读《基督山伯爵》。与观《Billions》有感

- 此句英文属于上一段，为了翻译顺畅，将中文挪到本段 ↩

0 条评论