generating random fractal terrain · chaos and fractals, new frontiers of science...

29
a.有些词语是我擅自理解翻的,也许保留它们的原文更好(相应在文中以后缀(斜体)出)。另外有些不太容易理解的关键信息也保留了原文,但在这里不会列出。词语如下: Generating Generating Generating Generating Random Random Random Random Fractal Fractal Fractal Fractal Terrain Terrain Terrain Terrain 随机分形地形生成 The The The The Definition Definition Definition Definition and and and and Rendering Rendering Rendering Rendering of of of of Terrain Terrain Terrain Terrain Maps Maps Maps Maps 地形图的定义和渲染 diamond-square diamond-square diamond-square diamond-square algorithm algorithm algorithm algorithm “菱形-正方形” 算法 (开始我以为是什么“钻石-广 场”算法,搞得一头雾水....-_-!tessellate tessellate tessellate tessellate 镶嵌 Mandelbrot Mandelbrot Mandelbrot Mandelbrot set set set set 曼德勃罗特集 Midpoint Midpoint Midpoint Midpoint Displacement Displacement Displacement Displacement in in in in One One One One Dimension Dimension Dimension Dimension 一维“中点替换”算法 fractal fractal fractal fractal image image image image compression compression compression compression 分形图像压缩 Chaos Chaos Chaos Chaos and and and and Fractals, Fractals, Fractals, Fractals, New New New New Frontiers Frontiers Frontiers Frontiers of of of of Science Science Science Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么) b.另外是我 lookof 自己,译者,擅自补充了一些话来让语句变得更通顺或更易理解的,或者 是对此作出的一些解释。不得不说明原文并没有这么写,而且兴许我的理解有问题。不过我 所作的补充大多都是无关紧要的环节,不会影响大家理解关键的算法思想。对于是关键环节 而拿捏不准的地方,我都提供了原文。补充以后缀(下划线)给出。(注:这可不是超链 ..... -_-!) 如: 查阅本文末尾的参考书目(对学习分形知识而言)是一个好的开始。 C.参考书目不译。

Upload: others

Post on 25-Jul-2020

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

a.有些词语是我擅自理解翻的,也许保留它们的原文更好(相应在文中以后缀(斜体)给

出)。另外有些不太容易理解的关键信息也保留了原文,但在这里不会列出。词语如下:

GeneratingGeneratingGeneratingGenerating RandomRandomRandomRandom FractalFractalFractalFractal TerrainTerrainTerrainTerrain 随机分形地形生成

TheTheTheThe DefinitionDefinitionDefinitionDefinition andandandand RenderingRenderingRenderingRendering ofofofof TerrainTerrainTerrainTerrain MapsMapsMapsMaps 地形图的定义和渲染

diamond-squarediamond-squarediamond-squarediamond-square algorithmalgorithmalgorithmalgorithm “菱形-正方形” 算法 (开始我以为是什么“钻石-广场”算法,搞得一头雾水....-_-!)

tessellatetessellatetessellatetessellate 镶嵌

MandelbrotMandelbrotMandelbrotMandelbrot setsetsetset 曼德勃罗特集

MidpointMidpointMidpointMidpoint DisplacementDisplacementDisplacementDisplacement inininin OneOneOneOne DimensionDimensionDimensionDimension 一维“中点替换”算法

fractalfractalfractalfractal imageimageimageimage compressioncompressioncompressioncompression 分形图像压缩

ChaosChaosChaosChaos andandandand Fractals,Fractals,Fractals,Fractals, NewNewNewNew FrontiersFrontiersFrontiersFrontiers ofofofof ScienceScienceScienceScience 《混沌和分形,科学的新前沿》

(网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

b.另外是我 lookof自己,译者,擅自补充了一些话来让语句变得更通顺或更易理解的,或者

是对此作出的一些解释。不得不说明原文并没有这么写,而且兴许我的理解有问题。不过我

所作的补充大多都是无关紧要的环节,不会影响大家理解关键的算法思想。对于是关键环节

而拿捏不准的地方,我都提供了原文。补充以后缀(下划线)给出。(注:这可不是超链

接..... -_-!)

如: 查阅本文末尾的参考书目(对学习分形知识而言)是一个好的开始。

C.参考书目不译。

Page 2: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

GeneratingGeneratingGeneratingGenerating RandomRandomRandomRandom FractalFractalFractalFractal TerrainTerrainTerrainTerrain

随机分形地形生成

目录:

第一部分:随机分形地形生成

���� 引言

���� 自相似“一维中点替换”算法

���� 高度图

���� “菱形----正方形”算法

���� 天空云图

���� 其他方法

第二部分:关于示例程序及其源代码

���� 安装

���� 使用程序

���� 代码结构

���� 下载程序

参考书目

Page 3: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

PartPartPartPart I:I:I:I: GeneratingGeneratingGeneratingGenerating RandomRandomRandomRandom FractalFractalFractalFractal TerrainTerrainTerrainTerrain

第一部分:随机分形地形生成(GeneratingGeneratingGeneratingGenerating RandomRandomRandomRandom FractalFractalFractalFractal TerrainTerrainTerrainTerrain)IntroductionIntroductionIntroductionIntroduction

引言

Ten years ago, I stumbled across the 1986 SIGGRAPH Proceedings and was awestruckby one paper in particular, entitled The Definition and Rendering of Terrain Maps by GavinS. P. Miller 1 . It described a handful of algorithms for generating fractal terrain, and theauthors also introduce a new method which they considered to be an improvement.

10年前,我偶然发现了 SIGGRAPH小组于 1986年的会议记录,对其中的一篇文章感到特

别得肃然起敬。这篇文章的题目是《地形图的定义和渲染》(Definition and Rendering ofTerrain Maps),作者是 Gavin S.P.Miller1 。该文描述了很多分形地形生成的算法,而且作

者也介绍了一种新的算法。小组其他成员认为,这种新算法较之以前是一种改进。

Initially I was impressed that these algorithms (even the algorithms considered "flawed" bythe authors) could create such incredible landscape images! Then, upon reading the paper,I was floored by the simplicity of these algorithms.

起初,我对这些算法的印象非常深刻(尽管作者认为这些算法依然有“瑕疵”),它们竟然

能创造出如此令人难以置信的风景图片!然而接下来随着深入文章,我被这些精巧绝妙的算

法“惊呆”了。

I've been a fractal terrain addict ever since.

自那以后我就对分形地形上瘾了。

The math behind the algorithm can get quite complex. However, completely understandingthe math is not a prerequisite for grasping the algorithm. And that's good. Because if I hadto explain all the math to you before explaining the algorithm, we'd never get to thealgorithm. Besides, there is literally tons of material out there on the mathematicalconcepts involved in fractals. See the references at the end of this article for a good start.

隐藏在该算法背后的数学知识可谓相当复杂。尽管如此(幸运的是),能不能完全弄清楚这

些数学理论对学习该算法并无影响。这很好。因为假如我在解释算法之前,不得不先解释所

有这些数学的话,那我们这辈子也接触不到这个算法了。除此之外,涉及到分形的数学概念

光从字面上统计就有成吨的那么多。查阅本文末尾的参考书目(对学习分形知识而言)是一

个好的开始。

For the same reasons that I won't go into the math details, I can't include a broad overviewof fractals and everything they can be used for. Instead, I'm going describe the concepts

Page 4: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

behind fractal terrain generation, and give a focused and detailed description of mypersonal favorite algorithm: the "diamond-square" algorithm. I'll demonstrate how to usethis algorithm to statically tessellate an array of height data that can be used for geometricterrain data, terrain texture maps, and cloud texture maps.

出于同样的原因,我不会深入到数学细节里,我无法在这里包含一个分形理论的概观,也无

法罗列出每一件它们能够做的事情。取而代之的是,我将描述隐藏在“分形地形生成”背后

的概念,然后着重给出一个我个人最喜欢的算法:“菱形-正方形”算法(diamond-squarealgorithm) 。我会告诉大家如何利用这个算法来静态地“镶嵌”(tessellate)一组高度数

据,这组高度数据可以用来生成几何地形数据,地形纹理贴图,以及云团纹理贴图。

What can you do with a fractal terrain? I assume you already know that; that's why you'rereading this. Random terrain maps are great for flight simulators or making texture maps touse as a background (showing a distant mountain range, for example). The samealgorithm that makes terrain can also be used to generate texture maps for partly cloudyskies.

利用分形地形你可以做什么?我假设你已经知道了答案,因为这就是你为什么会读本文的原

因。随机地形图对于飞行模拟器,以及制作一块纹理贴图来当做背景(比如显示一座遥远的

山脉)来说,具有非常好的效果。同样的算法也可以用来为天空云团生成纹理贴图。

Before I go further, a disclaimer: I am not a game programmer. If you are reading thisbecause you want algorithms for rendering terrain quickly, you've come to the wrong place.I'll only describe the process of generating the terrain model. How you render it is up to you.

在我继续讲下去前,发布一条免责声明:我不是一个游戏程序员。如果你是因为想要一个更

快的渲染地形的算法而读此文的话,你来错了地方。我只是介绍生成这种地形模型的过程。

至于你如何渲染,取决于你自己。

Self-SimilaritySelf-SimilaritySelf-SimilaritySelf-Similarity

自相似

The key concept behind any fractal is self-similarity. An object is said to be self-similarwhen magnified subsets of the object look like (or identical to) the whole and to eachother.2

藏在任何“分形”背后的一个关键概念是:自相似。当一个物体放大它自己后,它的局部子

集相似于(或者相同于)整体和周围的局部子集。2

Consider the human circulatory system. This is a fine example of self-similarity in nature.The same branching pattern is exhibited from the largest arteries and veins all the waydown to the smallest capillaries. If you didn't know you were using a microscope, youwouldn't be able to tell the difference between capillaries and arteries.

想一想人体的循环系统,这就是一个自然界中“自相似”的好例子。从最大的动脉和静脉开

始,一路直下到最小的毛细血管,都是一模一样的枝杈结构。如果你不知道你是在用显微镜

观察它们的话,你就会分不清哪个是毛细血管,哪个是动脉。

Now consider a simple sphere. Is it self-similar? No. At significantly large magnification, itstops looking like a sphere altogether and starts looking like a flat plane. If you don't believe

Page 5: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

me, just take a look outside. Unless you happen to be in orbit while reading this, you'll seeno indication that the Earth is a sphere. A sphere is not self-similar. It is best describedusing traditional Euclidean geometry, rather than a fractal algorithm.

现在考虑一个简单的球体。它是自相似的吗?不是。 非常显著地放大很多倍以后,它就不

再像个球了,而是像一个平面。如果你不相信我,只要看看外面好了。除非你阅读本文的时

候正好位于外太空的轨道上(汗。。。太幽默了),否则你将看不出任何关于“地球是圆

的”这一迹象。 球体是非自相似的,描述它的最佳途径是传统的欧几里德几何,而非分形

几何。

Terrain falls into the "self-similar" category. The jagged edge of a broken rock in the palm ofyour hand has the same irregularities as a ridgeline on a distant horizon. This allows us touse fractals to generate terrain which still looks like terrain, regardless of the scale in whichit is displayed.

地形算是“自相似”这一范畴。你手中握着的岩石断面的锯齿边有着相同的不规律性,就像

遥远地平线的一条山脊一样。(因此地形的这个特点)就允许我们利用分形思想来产生仍然

像地形的地形,而不用考虑地形呈现时的缩放比例。

A side note on self-similarity: In its strictest sense, it means self-identical, that is, exactminiature copies of itself are visible at increasingly small or large scales. I actually don'tknow of any self-identical fractals that exist in nature. But the Mandelbrot set is self-identical. I won't even go into describing the Mandelbrot set. Go dig up any of thereferences for more info.

一条关于“自相似”的旁注:最严格的意义,它指的是“自相同”,就是说,无论放大或是

缩小多少倍,它自己的精确的微观拷贝版本都是可见的。(that is, exact miniature copiesof itself are visible at increasingly small or large scales.)(我自己的理解是,它的局部是

严格相同于整体的,即局部和整体一模一样,并且可以无穷无尽细分下去)。实际上我不知

道自然界中是否存在“自相同”的分形现象。但是曼德勃罗特集(Mandelbrot set)是自相

同的。我不能再深入去讲曼德勃罗特集了,更多信息请查阅参考书目。

MidpointMidpointMidpointMidpoint DisplacementDisplacementDisplacementDisplacement inininin OneOneOneOne DimensionDimensionDimensionDimension

“一维中点替换”算法(MidpointMidpointMidpointMidpoint DisplacementDisplacementDisplacementDisplacement inininin OneOneOneOne DimensionDimensionDimensionDimension )

The diamond-square algorithm, which I will describe later, uses a kind of midpoint-displacement algorithm in two dimensions. To help you get a grip on it, we'll look at it first inone dimension.

我稍后会讲到的“菱形-正方形”算法,使用了一种二维“中点替换”算法。为了帮助你掌

握它,我们首先看看在一维下的“中点替换”算法。

One-dimensional midpoint displacement is a great algorithm for drawing a ridgeline, asmountains might appear on a distant horizon. Here's how it works:

一维“中点”替换算法是画遥远的地平线上一条山脊线的极好算法。它是这样工作的:

Start with a single horizontal line segment.Repeat for a sufficiently large number of times {Repeat over each line segment in the scene {

Page 6: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

Find the midpoint of the line segment.Displace the midpoint in Y by a random amount.Reduce the range for random numbers.}}

以一条水平线段开始.

重复很多次{

对场景中的每一条线段{

找到线段的中点.

在 y 轴上用一个随机值替换中点值.

缩小随机值的取值范围.

}

}

How much do you reduce the random number range? That depends on how rough youwant your fractal. The more you reduce it each pass through the loop, the smoother theresulting ridgeline will be. If you don't reduce the range very much, the resulting ridgelinewill be very jagged. It turns out you can tie roughness to a constant; I'll explain how to dothis later on.

你缩减随机值取值范围的幅度是多少?这取决于你想要的效果有多“粗糙”。每一轮你缩减

的越多,最后的山脊线就会越“平滑”。如果你基本不缩减,最后的山脊线就会像锯齿一样

参差不齐。这表明你可以把“粗糙度”设定为一个常数,我呆会儿会解释怎么做。

Let's look at an example. Here, we start with a line from -1.0 to 1.0 in X, with the Y value ateach endpoint being zero. Initially we'll set the random number range to be from -1.0 to 1.0(arbitrary). So we generate a random number in that range, and displace the midpoint bythat amount. After doing this, we have:

我们来看一个例子。现在我们有一条线段,X轴上它在区间[-1.0,1.0]里,Y轴上每一处都

是 0 。 开始的时候,我们设定一个随机值的取值范围[-1.0, 1.0](可以随意设)。 所以我

们将在这个范围里产生一个随机值,然后用这个随机值替换中点值。完成这步后,我们有:

Now the second time through the outer loop, we have two segments, each half the lengthof the original segment. Our random number range is reduced by half, so it is now -0.5 to0.5. We generate a random number in this range for each of the two midpoints. Here's theresult:

Page 7: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

现在开始第二轮循环。我们这时有两端线段,每一段都是原始线段的一半。我们让随机值取

值范围也减半,所以现在变成[-0.5,0.5] 。我们为两个中点值各自在这个范围里生成一个

随机值以之替换。现在的结果是:

We shrink the range again; it is now -0.25 to 0.25. After displacing the four midpoints withrandom numbers in this range, we have:

我们再次缩减随机值取值范围,因此现在它变成了[-0.25,0.25] 。在这个范围里再生成四

个随机值,用来替换现在的四个中点值,完成后我们有:

Two things you should note about this.

你应该注意两点。

First, it's recursive. Actually, it can be implemented quite naturally as an iterative routine.For this case, either recursive or iterative would do. It turns out that for the surfacegeneration code, there are some advantages to using an iterative implementation over arecursive one. So for consistency, the accompanying sample code implements both theline and surface code as iterative.

首先,它是递归的。而事实上,它也可非常自然地用迭代手法来实现。因此在(一维画线)

这个例子里,无论是递归还是迭代都可行。但是对于生成“表面(即地形)”的代码而言,

用迭代实现比用递归实现有更多的好处。因此为了(画“线”和画“面”)一致起见,给出

的示例程序中,无论画“线”还是画“面”,都是用迭代方法实现的。

Second, it's a very simple algorithm, yet it creates a very complex result. That is the beautyof fractal algorithms. A few simple instructions can create a very rich and detailed image.

其次,这是一个非常简单的算法,然而,它却创造处一个非常复杂的结果。这就是分形算法

的魅力所在。几条非常简单的指令就能创造出一个非常丰富而且细腻的图像。

Here I go off on a tangent: The realization that a small, simple set of instructions can createa complex image has lead to research in a new field known as fractal image compression.The idea is to store the simple, recursive instructions for creating the image rather thanstoring the image itself. This works great for images which are truly fractal in nature, sincethe instructions take up much less space than the image itself. Chaos and Fractals, NewFrontiers of Science 3 has a chapter and an appendix devoted to this topic and is a great

Page 8: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

read for any fractal nut in general.

这里我扯句题外话:只要用一组精巧的指令集就能创造出一幅复杂图像的实现,引领起一个

新技术领域的研究,被称为“分形图像压缩( fractal image compression)”技术。该技

术的思想是,存储用来创造图形的简单而又递归的指令,而不是存储图像本身。这种技术对

于创造出自然界中真正有“分形”现象的图像而言,效果是极其好的,因为指令比图像数据

占据的空间要小得多。《混沌和分形,科学的新前沿》3 (Chaos and Fractals, NewFrontiers of Science 3 ) 有一个章节和一个附录是专门讲这个话题的,一般来讲对任何一

个“分形迷”而言,都值得一读。

Back to reality.

言归正传。

Without much effort, you can read the output of this function into a paint program and comeup with something like this:

不必费多少劲儿,你就可以把这个(一维“中点”替换)函数的输出结果读入到一个绘图程

序里,来生成点类似这样的东西:

This could be used as scenery outside a window, for example. The nice thing about it is thatit wraps, so you can keep around one relatively small image and completely wrap a scenewith it. That is, if you don't mind seeing the same mountain in every direction.

这种图像可以用来,打个比方说,作为一幅窗外的风景。于此一件不错的事情是,它是环绕

的(即把两个边对接起来是契合的),这样你就可以用一张相对较小的图片来包裹整个场

景。当然,如果你不介意无论从哪个方向看,都会看到同一座山的话。

OK, before we go into 2D fractal surfaces, you need to know about the roughness constant.This is the value which will determine how much the random number range is reducedeach time through the loop and, therefore, will determine the roughness of the resultingfractal. The sample code uses a floating-point number in the range 0.0 to 1.0 and calls it HHHH.2222(-H)(-H)(-H)(-H) is therefore a number in the range 1.0 (for small HHHH) to 0.5 (for large HHHH). The randomnumber range can be multiplied by this amount each time through the loop. With HHHH set to1.0, the random number range will be halved each time through the loop, resulting in a verysmooth fractal. With HHHH set to 0.0, the range will not be reduced at all, resulting in something

Page 9: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

quite jagged.

接下来,在我们进入“二维分形表面”这一话题之前,你需要了解下什么是“粗糙度常

数”。这是一个决定每次循环时随机值取值范围应当缩减多少的值,因而这个值会决定最终

结果的粗糙程度。示例程序中我们使用了一个在区间[0.0,1.0]之间的浮点型数,我们称它

为 HHHH 。那么 2222(-H)(-H)(-H)(-H) 就是一个在区间[0.5,1.0]之间的值。随机值取值范围在每一轮循环中

都会乘以这个值(指的是 2(-H))。当 HHHH设置为 1时,随机值取值范围每轮循环都会减半,

得到的结果是一个非常光滑的不规则形状;当 H设置为 0时,取值范围则根本不会缩减,

得到的结果将是非常参差不齐的锯齿状形状。(这里关于 H的理解有点搞头,不过经过本

人详细推敲,这段话是没有问题的。H无疑是指粗糙度常数,但随机值取值范围每次乘的数

是 2(-H) 。比如就像上面说的,如果你定的这个常数为 1,那么就会使随机值取值范围每次

都乘 1/2,即第一次在[0,1]内取一个随机值,第二次在[0,0.5]内取,第三次在[0,0.25]内取。这样看来,H是通过控制 2(-H) 来间接控制随机值取值范围的。如果兄弟们还有不明白

的可来信询问。:-))

Here are three ridgelines, each rendered with varying HHHH values:

以下是随着 HHHH值的变化,所渲染出来的不同的山脊线:

HeightHeightHeightHeight MapsMapsMapsMaps

高度图

The midpoint displacement algorithm described above can be implemented using a one-dimensional array of height values which indicate the vertical location of each line segmentvertex. This array is a one-dimensional height map. It maps its indices (X values) to heightvalues (Y values).

上面描述的“中点替换”算法使用了一个表征高度值的一维数组来实现。这个高度值指的是

每条线段端点的垂直位置。该数组实际上就是一维的高度图。它反应的是 X轴索引到高度

值 Y的映射。

To simulate random terrain, we want to extrapolate this algorithm into 3D space, and to doso we need a two-dimensional array of height values which will map indices (X and Z

Page 10: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

values) into height values (Y values). Note that although our end goal here is to generatethree-dimensional coordinates, the array only needs to store the height (Y) values; thehorizontal (X and Z) values can be generated on the fly as we parse through the array.

为了模拟随机地形,我们欲把上面那个算法(指“中点替换”算法)推广到三维空间,而且

为了达到这个目的,我们需要一个表征高度的二维数组。这个数组把 X轴和 Z轴的联合索

引映射到表征高度的 Y轴上去(就是说 Y是 X与 Z的多元函数,高数里的内容)。注意,

虽然我们的最终目标是要产生一个三维坐标信息,但这里这个二维数组仅仅存储 Y轴上的

值,X轴和 Z轴的值我们可以在解析数组的时候即时动态地生成。

By assigning a color to each height value, you could display a height map as an image.Here, high points in the terrain (large values) are represented by white, and low points(small values) are represented by black:

通过为每一个高度值分配一种颜色,你可以把一幅高度图显示成一幅图片。下面的这幅高度

图,用白色表示地形中高的地方(Y值大),用黑色表示地形中低的地方(Y值小):

Rendering a height map this way is useful for generating cloud texture maps, which I'lldiscuss later. Such a representation could also be used to seed a height map.

用这种方式渲染一幅高度图对生成“云团纹理贴图”也是有效的,这个呆会儿我会解释。同

样,这种方式也可以用来随机生成一幅高度图(个人认为,这才是这篇文章的精髓。因为后

面介绍的算法就是在解决“随机生成一幅高度图”这个问题的)。

Now I'll describe how to tessellate our two-dimensional height map array.

现在,我来介绍如何生成“存储高度图信息”的二维数组。(即如何随机生成一幅高度

图。)

TheTheTheThe Diamond-SquareDiamond-SquareDiamond-SquareDiamond-Square AlgorithmAlgorithmAlgorithmAlgorithm

“菱形----正方形”算法

As I mentioned at the start of this article, I was first introduced to the concept of generatingrandom terrain in Gavin S. P. Miller's paper 1 . Ironically, Miller describes the diamond-square algorithm as flawed in this paper, and he then goes on to describe a different

Page 11: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

algorithm based on weighted averaging and control points.

正如我在本文开始的时候提到的,我是在 Gavin S. P. Miller 的文章中第一次知道了“随机

地形生成”这个概念。具有讽刺意义的是,在文中,Miller把“菱形-正方形”算法看作一种

存在“缺陷”的算法,之后他就开始介绍另一种基于“加权平均和控制点”的算法了。

(and he then goes on to describe a different algorithm based on weighted averaging andcontrol points. )

(下面一段话,考虑到自己翻功拙劣,于是原文附上。因为我并不能太好地表达他的意思。

好在这段是评论 Miller对该算法的看法的,不是理解算法的关键。)

Miller's complaints with the diamond-square algorithm stem from his attempt to force thealgorithm into creating a mountain, that is, with a peak, by artificially increasing the heightof the grid center-point. He lets all other points in the array generate randomly. If Miller hadsimply generated the center-point randomly, then even he would've had to admit that thealgorithm works pretty decently as a terrain generator. The Diamond-Square algorithm canbe used to force a mountain with a peak, by "seeding" the array with values. More than justthe center point of the array must be seeded to achieve acceptable results. He complains ofsome inherent creasing problems as well. But you judge for yourself. The algorithm isoriginally described by Fournier, Fussell, and Carpenter 4.

Miller对“菱形-正方形”算法的抱怨根源于他尝试利用该算法来生成一座山。(山会坐落在

X轴和 Z轴所代表的平面网格中)。他要求平面网格的中心位置的高度(也就是峰值)是人

工赋值的,而其它所有点的高度都是随机产生的。如果 Miller当初仅仅让中心点的高度也同

样随机产生,那么连他也会不得不承认,该算法就像一个地形生成器一样工作得那么优雅。

“菱形-正方形”算法可以用来生成一座带有一个峰顶的山,这是通过对数组“种入”随机

数来办到的。当然不仅仅是只对数组的中心点这一个点植入一个种子数来达到可接受的结

果。(呃,太绕了,就是说其它的点也需如此吧。建议大家看原句,我好菜。)他也对其它

固有的折痕问题发了点牢骚。但是(不管怎样),你该有自己的判断。这个算法最初由

Fournier, Fussell, 和 Carpenter 所描述 4。

(Miller's complaints with the diamond-square algorithm stem from his attempt to force thealgorithm into creating a mountain, that is, with a peak, by artificially increasing the heightof the grid center-point. He lets all other points in the array generate randomly. If Miller hadsimply generated the center-point randomly, then even he would've had to admit that thealgorithm works pretty decently as a terrain generator. The Diamond-Square algorithm canbe used to force a mountain with a peak, by "seeding" the array with values. More thanjust the center point of the array must be seeded to achieve acceptable results. Hecomplains of some inherent creasing problems as well. But you judge for yourself. Thealgorithm is originally described by Fournier, Fussell, and Carpenter 4.)

Here's the idea: You start with a large empty 2D array of points. How big? To make it easy,it should be square, and the dimension should be a power of two, plus one (e.g. 33x33,65x65, 129x129, etc.). Set the four corner points to the same height value. If you look atwhat you've got, it's a square.

算法思想是这样的:你以一个大而空的代表点的二维数组阵列开始。有多大?简单起见,这

个阵列应该是个正方形。而且每一维的尺寸应该是 2的几次方再加 1(比如 33x33,65x65, 129x129等等)。将四个角落的点设成相同的值。看看你得到的东西,它是一个

Page 12: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

正方形。

As a simple example, let's use a 5x5 array. (We'll be referring to this image later on in thearticle, so don't forget about it.) In figure a the four corner "seed" values are highlighted inblack:

举一个简单的例子,让我们使用一个 5x5的数组。(我们将在后面谈到这幅图像,所以别

把它忘到脑后。)在图 a中,被赋值的四个角落的点用黑色高亮显示:

This is the starting-point for the iterative subdivision routine, which is in two steps:

下面是算法的起点,共分为两个阶段:

The diamond step: Taking a square of four points, generate a random value at thesquare midpoint, where the two diagonals meet. The midpoint value is calculated byaveraging the four corner values, plus a random amount. This gives you diamondswhen you have multiple squares arranged in a grid.

“菱形”阶段:利用构成正方形的四个点,在这个正方形的中点,也就是两条对角线

交汇的地方,生成一个随机值。中点值等于,四个边角点值求平均后再加上这个随机

值。当在网格中有多个正方形时,这样就会为你产生菱形。(这里讲的很模糊,我也

是摸索了很久才弄明白作者指的是什么意思。这里给一点提示,看着图 b,用眼睛把

左边的两个边角点,连同那个中心点连起来,顺序是:左上点->中心点->左下点。这

样你就得到了一个菱形的右半部分。看出来了吗?它的左半部分呢?其实是循环顺延

到了右边,也就是右上点->中心点->右下点构成了这个菱形的左半部分。不过,在作

者看来,半个菱形就可以算是菱形了。所以,这里一共有四个菱形:左上点->中心点

->左下点、右上点->中心点->右下点、左上点->中心点->右上点、左下点->中心点->右下点。)

The square step: Taking each diamond of four points, generate a random value atthe center of the diamond. Calculate the midpoint value by averaging the cornervalues, plus a random amount generated in the same range as used for the diamondstep. This gives you squares again.

“正方形”阶段:利用构成菱形的四个点,在这个菱形的中点生成一个和上一步相同

取值范围区间的随机值。同样这个中点值等于,四个边角点值求平均后再加上这个随

机值。这样就又会给你产生正方形。(看着图 c,左上点->中上点->中心点->左中点

构成了一个正方形,容易看出该图一共排布着 4个这样的正方形。)

So if you were to seed a square and make a single pass through the subdivision routine,you would end up with four squares. Running it twice would yield 16 squares. A third passwould result in 64 squares. It gets big fast. The number of squares generated is equal to2222(I+2)(I+2)(I+2)(I+2), where IIII is the number of iterations through the recursive subdivision routine.

Page 13: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

所以,如果你“种下”1个正方形并且只做了一轮这样的循环后,你将最终得到 4个正方

形。两轮循环你就得到 16个正方形,三轮就有 64个了。它增长的很快。正方形的数量等

于 2(i*2) ,i是循环迭代的轮数。(The number of squares generated is equal to 2(I+2),where I is the number of iterations through the recursive subdivision routine.)(必须指

出,我翻的和原文并不一致,尤其是在那个关键公式上有些出入。原因是,无论我怎么理

解,我都搞不清楚 2(I+2)的意义。如果 2(I+2)指的是得到的正方形的个数, I无论指什么意

思,都不能满足 1,4,16,64 这样的增长规律。因为 I增长必是按 1累加的,即 1,2,3这样的顺序,可是当 I等于 1时,结果就会出现 8,这是不可能的。如果出现预期的结果,

那么 I必须以 2累加,即 0,2,4等等,可是这时 I又表明什么意思呢?无法解释。唯有把

公式解释为 2(I*2),一切疑问都烟消云散。这时 I指的是迭代次数。I=0时(还没有迭代),

只有 1个正方形;I=1时(第一次迭代),出现 4个正方形;I=2时(第二次迭代),16个

正方形;I=3时(第三次迭代),64个正方形。这样就很好,一切都非常和谐。关于此疑问

我已给原作者发了询问邮件。不过,对于他是否能够回复,我和兄弟们一样期待。。。。-_-!)

Referring to the previous five figures, Here's what happens as we make two passes overthe array with our diamond-square algorithm.

现在我们对照前面 5幅图,逐一看看当我们执行“菱形-正方形”算法的那两个阶段时发生

的一切。

For the diamond step of the first pass, we generate a value at the center of the array basedon the height of the four corner values. We average the four corner values (really notnecessary if they were all seeded to the same value), and add a random value from therange -1.0 to 1.0. In figure b, the new value is shown in black, and the existing cornervalues are shown in gray.

对于第一轮的“菱形”阶段,我们在这个数组的中心(即正方形的中心点)生成了一个值,

这个值是基于四个边角值得出的。我们计算四个边角值的平均值(如果这四个值相等的话,

这一步其实是不必做的),然后加上一个范围在区间[-1.0,1.0]里的随机值。如图 b所示,

新产生的点用黑色表示,已经存在的点则用灰色表示。

For the square step, we use the same range for generating the random values. There arefour diamonds at this stage; they all meet in the center of the array, so we calculate fourdiamond centers. The corners of the diamonds are averaged to find the base for the newvalues. Figure c shows the new values in black and existing values in gray.

接下来是“四边形”阶段。我们在和上一阶段相同的随机数取值范围内生成一个随机值。在

这个阶段一共有四个菱形。它们全部相交在原正方形的中心点。所以我们要分别计算这四个

菱形的中心点。对每一个菱形,求它的四个边角值的平均值,再加上那个随机值,就是各自

中心点的值。如图 c所示,新产生的点用黑色表示,已经存在的点则用灰色表示。

That's the first pass. If you were to connect these 9 points with lines, you might get awireframe surface which looks like this:

这就是第一回合。如果你用线把这九个点连起来,你会得到像下面这样的线框图:

Page 14: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

Now we perform the second pass. Again start with the diamond step. The second pass isdifferent from the first pass in two ways. First, we now have four squares instead of one, sowe need to calculate four square centers. Second, and this is key,the range for generatingrandom numbers has been reduced. For the sake of example, let's say we are using an HHHHvalue of 1.0. This will reduce our random number range from (-1.0, 1.0) to (-0.5, 0.5). Infigure d, the four square center values we calculate at this step are shown in black.

现在让我们进入第二回合。我们再次以“菱形”阶段开始。第二回合在两个方面区别于第一

回合。首先,我们这次有了 4个正方形,而不是 1个。所以我们需要分别计算这 4个正方

形的中心点。其次,同时这也才是最关键的,这次随机值的取值范围区间要被缩减。比如拿

这个例子来说,我们设置的 HHHH值为 1的话,那么随机值的取值范围就会从(-1.0 ,1.0),缩

减到(-0.5,0.5)。如图 d所示,我们计算的四个正方形的中心点用黑色表示。

Finally, we perform the square step for this second pass. With 12 diamond centers, we nowneed to calculate 12 new values. Figure e shows them in black.

最后,我们进行这一回合的“正方形”阶段。因为一共有 12个菱形中心点,所以我们现在

需要计算 12个新值。图 e中用黑色标出了它们。

Now, all 25 elements of the array have been generated. We might now have a wireframesurface which looks like this:

现在,这个数组中的 25个点已经全部生成。现在我们也许会得到如下的一幅线框图:

Had a larger array been allocated, we could have continued to make more passes, addingmore detail in each pass. For example, after five passes, our surface might look somethinglike this:

如果分配一个更大的数组,我们就能继续循环更多次的回合,在每轮回合中增加更多的细

节。比如,经过 5轮回合,我们的表面也许看起来会像这样:

Page 15: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

I previously mentioned that the array dimensions need to be a power of two plus one. Thisis because the number of floats needed in the 2D array is equal to (2(2(2(2IIII+1)+1)+1)+1)2222. Eight iterationswould require a 257x257 array of floats, more than 256 Kbytes of memory for standard 32-bit IEEE floating-point numbers.

我之前提过,数组中每一维的尺寸应该是 2的几次方再加 1。这是因为,一个二维数组需要

的浮点型数的个数是(2I+1)2,8轮回合下来,就会需要一个 257x257大的数组。这对于 32位的 IEEE浮点数类型来说,会吃掉比 256KB还多的内存。

OK, so it's big. Using chars instead of floats would help. The sample code uses floats; ifmemory is really a concern and you must use chars, it should be easy to modify the samplecode to use a range of -128 to 128. But don't forget to clamp those values as you generatethem. Even if you limit your first pass to generating values between -128 and 128,subsequent passes could result in values outside this range, resulting in an overflowcondition. This is especially likely for small values of HHHH.

这是一笔很大的开销。使用字符类型代替浮点类型会有所帮助。示例程序用的是浮点型。但

如果内存对你来说真的很吃紧的话,你就得用字符型,用一个范围在-128到 128的有符号

字符类型来替换示例程序中的浮点型应该很简单。但是当你生成它们时,要小心夹紧这些

值:即便在第一回合你把它们限制在-128到 128内,在随后的循环中,仍然有可能超出这

个范围,由此产生一个“溢出条件”。这在当 HHHH较小时尤其可能发生。

The sample code demonstrates another way to deal with the size problem. A large array isallocated and tessellated with the diamond-square algorithm. It is then rendered from a top-down orthographic view. This image is read back and used as a texture map on a secondarray tessellated to a lesser extent. Although the sample code doesn't do this, once theimage is read back from the framebuffer, the first array can be freed.

示例代码揭露了另一种解决空间问题的方法。首先我们分配一个很大的数组,然后用“菱形

-正方形”算法来为其赋值。之后用“从顶部到底部”的正投影视角将这个数组渲染成一幅

图像,然后把这幅图像作为纹理贴图来回读给第二个只需占用较小内存的数组。一旦渲染出

来的那副图像从帧缓冲区那里被回读完毕,你就可以释放第一个占用很大内存的数组了,尽

管示例程序并没有这么做(指释放内存)。

Here's an example of one such texture map:

下面是这种纹理贴图的一个例子:

Page 16: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

The map is artificially colored with white at the peaks, green in the valleys, and gray inbetween. Feel free to experiment with your own color scheme using the example sourcecode provided.

这张图人为地把峰顶涂成白色,把峡谷涂成绿色,二者之间的部分则涂成灰色。你可以用示

例程序提供的的源代码来随意实施你自己的配色方案。

(下面这段位于两个分界线的文字我仍然附上原文(汗颜....),并且原文在上,翻译在

下,上下对比以作参考。原因是作者文字之简单但意图之模糊相当打击本人信心。我觉得自

己没有能力很好地理解他到底是什么意思,但依然试图翻一翻,并且根据意思和一些有限的

推理作出大胆假设。这段信息后面又有一段位于两个分界线的文字,那是我对此段文字的一

些理解。如果只看原文就懂的朋友可绕过理解文字不看,不过我希望还是看看,帮忙校正一

下是不是我在扯淡。)

-------------------------------------------

Earlier I had mentioned that there are advantages to implementing this routine iterativerather than recursive. Here's why: A recursive implementation might take the form:

Do diamond step.Do square step.Reduce random number range.Call myself four times.

That's a nice simple implementation, and I have no doubt that it would work. But it requiresthat some points be generated with insufficient data. Why? After the first pass, you'll becalled upon to perform the square step without having all four corners of a diamond.

Instead, I've implemented this iteratively, and the basic pseudocode looks like this:

While the length of the side of the squaresis greater than zero {

Page 17: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

Pass through the array and perform the diamondstep for each square present.Pass through the array and perform the squarestep for each diamond present.Reduce the random number range.}

This eliminates the problem of missing diamond corners found in the recursiveimplementation. But you'll run into this problem again anytime you generate a point on theedge of the array. It turns out this is only a concern in the square step. You can easilyovercome this and simultaneously make the surface wrappable by taking into account thatone of the four diamond corner points lies on the other side of the array. (Another key tomaking the surface wrappable is to remember to seed the four corners with the samevalue.)

之前我说过,用“迭代”比用“递归”更好。因为,一个“递归”手法的实现很可能是这样

的形式:

执行一步“菱形”阶段的操作

执行一步“正方形”阶段的操作

缩减随机值取值范围区间

调用自己四次

这个实现很简单,而且我也不怀疑它能运行。但这个方法的漏洞是,其中有一些点要在数据

不足的情况下被生成。为什么呢?在执行完第一阶段后,你还尚未得到菱形的全部四个边角

值时,就要执行“正方形”阶段了。

相反,我用“迭代”手法来实现这个算法的话,基本的伪代码类似这样:

当正方形的变长大于0时{

传递数组,为当前每个正方形执行“菱形”阶段。

传递数组,为当前每个菱形执行“正方形”阶段。

缩减随机值取值范围区间

}

这样就解决了用“递归手法实现时“丢失的菱形角落”的漏洞问题。但是(即便你用“迭

代”方法实现)只要你在数组的边缘上生成一个点时你会再次碰上这个问题。不过这个问题

只会在“正方形”阶段出现。你可以很容易地解决这个问题,你可以把这个表面“包裹”起

来,假想菱形其中的一个角落(丢失的那个角落)落在了数组的另一边上。(另外使表面可

以被“包裹”的一个关键是(在数组的四个角上)“种下”相同的四个值。)

-------------------------------------------

Page 18: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

(以下是我对上面文字做的理解)

-------------------------------------------

我对作者的话存在两点疑惑:

1、作者先说递归也是行得通的( I have no doubt t hat it would work. ),但又说这样会生

成一些“残疾”的点,因为这些点是在数据不足时产生的( it requires that some points begenerated with insufficient data.)。点是生成了,但却是残疾的,那么这个实现方法到底算

“成”还是算“不成”呢?作者告诉我们,采用“迭代”比采用“递归”更优,开始我以为

是性能效率上的比较,因为我们知道,“非递归”的开销确实要小于“递归”的开销,“递

归”仅仅是代码上看上去简洁了不少而已。但是作者介绍到这里时,却到了伤筋动骨的地

步,简直感觉如果用“递归”的话就会残疾了似的。到底是怎么回事呢,弄不清楚。

2、对比递归的伪代码和迭代的伪代码,我没看出什么本质的不同,如果是区别在于后者传

递了数组的话,我完全可以把数组设计成一个全局变量,然后同样传递给递归函数,这不同

样可以办到么?因此我想这不是递归的死穴。另外,我不清楚为什么要“调用自己四

次”??

经过一段时间思考,我忽然得出这样的猜测(不保证正确,这里正是需要大家拍砖校正的地

方):

这就像是当初学数据结构时关于“图的遍历”的两种方法似的:深度遍历和广度遍历。这里

的“递归”实现就像是深度遍历一样,它会盯住一个根“菱形-正方形”结构,逐步分解成

更小的“菱形-正方形”结构(通过不停压栈),直到到达最小(这里调用四次即到达最

小),然后返回上一级,然后遍历完这一级所有的结构,然后再返回上一级,直到栈被弹

空。这样带来的问题是,当要求其中某一个结构的中心点时,会遇到围绕这个结构的四个边

角,有一个角落点是不属于这个大结构的情况(那个角落点属于邻近的那个大结构里的

点),但是由于“递归”的算法,你在当前大结构未被返回前,是无法计算邻近的那个大结

构的点的值的,因此就带来了“丢失的角落”问题。这属深度遍历的特性所致,可谓死穴。

而迭代就像广度遍历那样,先把当前一级的所有结构都遍历完毕,才深入到下一层去遍历更

小的结构,这样当下一层的小结构需要用到邻近的边角值来计算中心点时,因这些边角值属

上一层的点,已被完全计算出来,因此就不存在什么障碍了,这样就很好地解决了递归的死

穴问题。

-------------------------------------------

Here's an example of taking a diamond corner from the other side of the array. In thefollowing figure, we generate a point in the square step, and it just happens to fall right onthe edge. The four locations in the array which comprise the diamond corners arehighlighted in gray. They need to be averaged to find the base for the new value, shown

Page 19: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

here in black.

下面是一个“菱形的一个角落出现在数组的另一边”的例子。图中,我们在“正方形”阶段

生成了一个点,而它恰好落在了数组的边上。灰色表示的是包含了菱形四个角落的位置。这

四个位置的值需要求均,以此作为中间新值的基值,图中那个新值用黑色表示。

Note that two values are highlighted in black. They are actually the same value. Every timeyou calculate a value on the edge during the square step, make sure to also store it on theopposite side of the array. These points need to be exactly the same in order for seamlesswrapping to occur.

注意,图中用黑色表示的有两个点。实际上它们是相等的。每次你在“正方形”阶段计算一

个落在数组边上的点时,记得在数组对面的一边也要存储它一遍。为了能够无缝对接,这些

点必须完全相同。

This means that in figure e earlier, we really didn't need to calculate 12 separate values,since four of them are repeats of other values on the opposite side of the array. So actually,only 8 values needed to be calculated.

这就意味着,在前面的图 e中,我们其实不必把那12个点值全都各自算一遍,因为其中有4个点值是另外一边对应点值的重复。因此我们只有8个点需要计算。

I'll leave this as an exercise to the interested reader: Take the source code and make itwork without having repeated elements on the edges. It's really not necessary for thealgorithm to work; it just happens to be the way I wrote it.我将给有兴趣的读者留一个练习:修改示例程序中的源代码,让它在不需要计算“数组边上

的重复点值”的情况下也能运行。(“计算重复点值”这件事)对于算法来说真的不是必须

的,只是我恰好在代码里写成了那个样子。

If you haven't played with the sample program yet, now might be a good time to open it upand have a look. It starts up with a surface generated with two iterations. It is rendered inwireframe, simply by connecting the values of the array with line segments. The arrayvalues are treated as Y values, while the X and Z coordinates of each vertex are generatedon the fly as the array is parsed. This could easily be rendered as triangles by breakingeach square up into two triangles. Triangles are generally good primitives to use since theyare always convex and always planar.

如果直到现在你还没有运行过示例程序,也许现在你该打开它看一看。这个程序的初始状态

是一个经过两次迭代而生成的表面。它是用“线框模式”渲染的,也就是简单地用线段连接

起数组中的每个点而已。数组中的值被当作 Y值对待,而每个点的 X轴和 Z轴的坐标信息

在数组解析的过程中动态生成。用三角形(作为基本图元)可以很容易渲染图像,你可以把

Page 20: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

每一个正方形刨切成两个三角形(连接正方形的对角线即可得到)。“三角形”一般来说是

用来渲染图形的非常优秀的基本图元,因为它永远都是“凸面”的,而且保证三个点绝对在

同一平面。

Go into the View Options dialog box and tweak the Random seed value. This should causea different surface to be generated. Tweak the Iterations value a little higher to add moredetail to the surface. The code limits this value to 10, which is a little much for my 32 MegRAM Pentium Pro system and just looks black anyway. (Five years from now, people willrun this code on new processors and higher resolution screens and wonder why on Earth Ilimited that to 10...)

点击进入“View Options ”对话框。改变“Random seed ”值可以让你生成一个不同的表

面,稍稍调高点“Iterations”值,可以为表面增加更多的细节。代码中规定这个值的上限为

10,此上限对于我的32M内存,奔腾 Pro系统而言已经有点吃不消了,不管怎么看都是黑

黑的一片(which is a little much for my 32 Meg RAM Pentium Pro system and just looksblack anyway. )(Meg ,应该是 megabyte的缩写,意为“兆字节”)。 (五年后,人们

会在新的处理器和更高分辨率的屏幕上运行这段代码,然后他们会奇怪干嘛我非要把这个值

卡在10以内....) (实际上,这篇文章是写于997年。如今12个年头过去了。我在我的1GM内存,Pentium M 1.73GHz,ATI X600 的机子上运行这段程序,把“Iterations”调到10后得到的依然是一片黑色.....)

The first H value controls the roughness of the surface. By default it is set to 0.7. Try settingit higher and lower and note the results.

第一个 H值控制着表面的粗糙度。默认值设为0.7 。 试着调高或调低这个值,然后注意观

察结果。

Yes, this algorithm does occasionally produce localized spikes and some creasing. But Itend to like the surreal nature of the spikes, and the creasing is not obvious depending onwhat angle you are viewing it from, or how fast you're flying over it.

是的,这个算法偶尔会出现局部“突起”和一些“褶皱”。但是我比较喜欢这些超现实的

“突起”效果,而“褶皱”并不明显,这取决于你从哪个角度去观察它,也和你从上面飞过

它时有多快有关系(估计是用来作为飞行模拟的场景时)。( and the creasing is notobvious depending on what angle you are viewing it from, or how fast you're flying overit.)

CloudyCloudyCloudyCloudy SkiesSkiesSkiesSkies

天空云图

Now we know how to generate the surface. We can either generate and render tons oftriangles, or we can generate a high-res texture map and apply it to a low-res surface.Either way, it looks pretty cool. Now how do we generate some sky overhead? It's easierthan you think.

现在我们知道如何来生成一个表面。我们既可以生成并渲染成千上万个三角形,也可以把一

Page 21: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

个高解析度的纹理贴图贴到一个低解析度的表面上。(这两种方式)无论采用哪一种,都可

以营造出非常酷的效果。但是现在我们该怎么生成头顶上的云团呢?实际上它比你想的要简

单的多。

The array, after being completely tessellated by the diamond-square algorithm, is ideallysuited for representing a texture map of a cloudy sky. Instead of thinking of the array as acollection of Y values in a height map, think of it as cloud opacity data. The smallest arrayvalues represent the bluest, clearest parts of the sky, and the largest array values representthe whitest, cloudiest part of the sky.用“菱形-正方形”算法进行“镶嵌”过的数组,非常适合用来描绘一幅天空云团的纹理贴

图。只不过这时数组的值,不再代表高度图里 Y轴的值,而是表示云团的透明度。最小的值

表示“最蓝”,是天空中最晴朗的部分;而最大值表示“最白”,是天空中云层最密布的部

分。

It's trivial to parse through the array and generate a texture map like so:

轻轻松松解析完数组,你将得到这样一幅纹理贴图:

This is very similar to the height map image earlier in this article, but I have clamped the lowand high values to create patches of clear and clouded sky.

这幅图和本文前面说过的“高度图”很类似,但我把低端和高端的值保持在某个范围,以此

来生成一片“云彩斑斓”的天空(but I have clamped the low and high values to createpatches of clear and clouded sky.)。

You can produce an image like this with the sample code. Set the Select rendering typepulldown menu to 2D mesh / clouds. (By default it will look pixelated. Try setting the Clouditerations value to eight or higher to fix this.) Try different settings for the H value,immediately preceding the Cloud iterations value, to get different cloud effects.

运行示例程序,你也可以生成像上面的这样一幅图像。选择 Select rendering type 下拉

框,选中2D mesh / clouds选项。(默认状态下它看上去类似像素那样一格一格的,试着把

Cloud iterations值调到8或者更高看看)。接下来设定不同的 H值,这样就能得到不同的云

Page 22: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

团效果。

If you go back up to the top of this article, the very first image puts together much of whatI've discussed here. The sky is made with a texture map as shown above, tiled multipletimes over an eight-sided pyramid. The surface geometry is rendered with a high-restexture map. This texture map was generated by rendering a highly-tessellated lit surfacefrom a top-down orthographic view. The image was then read back and used as the texturemap.

如果你回到这篇文章刚开头的地方,你会发现我把所有曾讨论过的东西都组装在了第一张图

里。天空是用上面展示的纹理贴图生成的,在一个八棱锥上平铺了多次(The sky is madewith a texture map as shown above, tiled multiple times over an eight-sided pyramid. )

(意思是,天空的效果是用八棱锥做的一个天空盒,然后在上面平铺了很多这个贴图而做成

的)。表面的几何体是用高解析度纹理贴图渲染的。该纹理贴图由“自顶向下”的正投影视

角渲染。这幅图像经过“回读”后被当作一张新贴图使用。(就是前面说过的那个节省内存

的技巧)

The accompanying example program was used to generate nearly all of the images thatappear in this article.示例程序会展示几乎所有在本文中所提到的图像。

OtherOtherOtherOther MethodsMethodsMethodsMethods

其他方法

You'll probably want to have a little more control over the surface generation than what thesample code has provided. You might, for example, want to initially seed the first fewpasses of the array with your own values, so that mountains, valleys, etc., can be placedaccording to your own design. Then, fill out the rest of the detail using the diamond-squarealgorithm.你也许想对“地形生成”施加更多的控制,超过示例程序所提供的功能。比如说,你也许想

在开始的几轮回合中先在数组中“种下”一些你自己规定的值,这样像山、峡谷、等等什么

的都可以按你的设计来放置。然后再用“菱形-正方形”算法来填充剩余的细节。

Don't just kick the central array point upwards like Gavin Miller did, to create a mountain. Toget reasonable results, you'll need to seed at least two or three passes of the array.

不要像 Miller那样,想要生成一座山,却仅仅只对数组的中心点赋一个很大的值。想要产生

合理的结果,你起码应该要(用“菱形-正方形”算法)为这个数组“播种”两到三轮。

This is easily accomplished by altering the code to skip over assigning values to elementsof the array that already have values. Initialize your array to, say, -10.0, seed the first fewiterations with your own values, and enhance the fractal generation code to only assignvalues where the current value is -10.0. The first few iterations will not generate any values,since your seed values are already there. Subsequent passes will generate new valuesbased on your seed values.

你很容易就可以通过改写代码,让数组中已经赋了值的元素不再被分配新值。首先,初始化

你的数组,比如说把每个元素都设为-10。接着,最初的几轮迭代用你自己指定的值来“播

Page 23: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

种”。然后,用那段改写过的代码来只对值为-10的元素分配新值。最初的几轮迭代不会生

成任何值,因为你自己规定好的值已经在那了。随后的迭代中,才会根据你规定好的这些值

来生成新值。

How to get the seed values? Well, if the shape you want follows a known mathematicalform, such as a sine curve, then simply use that function to generate the values. Otherwise,you'll need to find some other creative way to do it. One method I have seen used is to paintyour own height map with gray values. Map the gray values to height values and store themin your array. Then use the diamond-square algorithm to add more detail.如何创造自己的“种子”? 如果你想要的形状是某种已知的可以用数学公式描述的形式,

比如正弦曲线,那么直接使用公式就可以生成这些值。但要是别的情况,就得想一些“创造

性的办法”来完成它了。我曾见过的一个办法是用“灰度值”来填充你自己的高度图。把

“灰度值”和“高度值”进行一一映射,然后存储在你的数组中。接下来再用“菱形-正方

形”算法来增加更多的细节。

Besides the diamond-square algorithm, there are plenty of other methods for tessellatingsurfaces.除了使用“菱形-正方形”算法,你还可以使用许多其他算的法达到同样的目的。

With successive random addition, a random region of the 2D array is incremented by asmall amount. Repeat this process over and over, adding a small amount into eachrandomly chosen region of the array. This generates good results but is not computationallylinear. If compute time is not a concern, I encourage you to try this algorithm out.

(以下两段话我不太明白什么意思,不保证翻译质量,遂附上原文。并请大牛拍砖指正。)

(有一种算法是这样的:)在二维数组中随机选取一段范围,为该范围内每一个元素赋一个

很小的值。不停地重复这个过程,分别为每一段随机选中的范围里的所有元素都增加一个很

小的值。这样也会产生不错的结果,但是该算法的复杂度并不是线性的。如果计算的时间成

本对你来说无所谓,那我鼓励你试试这种算法。(With successive random addition, arandom region of the 2D array is incremented by a small amount. Repeat this process overand over, adding a small amount into each randomly chosen region of the array. Thisgenerates good results but is not computationally linear. If compute time is not a concern, Iencourage you to try this algorithm out.)

Another similar method involves making a "fracture" across the array and incrementing oneside of it, as if an earthquake had occurred. Again, repeat several times. This is also not alinear algorithm and takes several passes to get acceptable results.

另一种相似的算法是在数组中制造出一个“断层”,然后为其中一边的所有元素赋值,就像

“地震”发生时那样。这个过程也需要重复好多次。同样该算法的复杂度也不是线性的,而

且你必须迭代几轮才能得到像样的结果。(Another similar method involves making a"fracture" across the array and incrementing one side of it, as if an earthquake hadoccurred. Again, repeat several times. This is also not a linear algorithm and takes severalpasses to get acceptable results.)

Refer to the references for many other different approaches.

欲了解更多其他的方法,请查阅后面的参考书目。

Page 24: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

PartPartPartPart II:II:II:II: AboutAboutAboutAbout thethethethe ExampleExampleExampleExample SourceSourceSourceSource CodeCodeCodeCode

第二部分:关于示例程序及其源代码

InstallationInstallationInstallationInstallation

安装

The example source code comes in a zip file. Use your favorite zip software to unzip thearchive. If you don't have a zip utility, try PKware.示例程序及其源代码打包在一个 zip文件里。使用你最喜欢的 zip解压缩软件来解开它。如

果你还没有一个 zip解压缩软件,可以试试 PKware. (我们使用 winrar就能打开。)

The source code uses the OpenGL API for rendering. If you do not already have OpenGLon your machine, you'll need to get it. Both Microsoft and SGI have OpenGL available forWindows 95, however I encourage you to get SGI's (from here). It is superior to Microsoft'sin terms of performance and robustness. Also, the sample code is linked with SGI'simplementation, and since SGI and Microsoft chose to give different names to their DLLfiles, the code will expect SGI's DLL names.

源代码使用 OpenGL API 作为渲染的图形接口。如果你的机器还没有安装 OpenGL,你应

该先得到它。Microsoft和 SGI 都提供了支持Windows 95 的 OpengGL版本。但我劝你选

用 SGI的,因为它在性能和程序健壮性方面都比 Microsoft的强出去不少。示例代码链接的

是 SGI实作版本。因为 SGI和 Microsoft选择了不同的名字来命名它们的 DLL文件,所以

代码使用的是 SGI的 DLL文件。

Here's a "how do I make a cool picture without knowing what I'm doing" guide:

下面是“傻瓜式制作酷图像”的说明向导。

Double click the Fractal Example icon.

双击 Fractal Example 图标。

Open the View Options dialog box.

打开 View Options 对话框。

From the Select render type pulldown menu, select 2D mesh / rendered.

从 Select render type下拉菜单中选择2D mesh / rendered。

Enter an Iterations value of 4.

在 Iterations 框中输入4 。

Enter a Tile value of 3.

在 Tile 框中输入3 。

Click OK.

点击 OK 。

UsingUsingUsingUsing thethethethe ProgramProgramProgramProgram

Page 25: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

使用程序

By default, the code starts up displaying a 2D mesh in line mode. It has been tessellatedwith the diamond-square algorithm. Two passes were made over the surface resulting ineight squares.

默认状态下,程序显示的是二维网格线框模式。它已经是用“菱形-正方形”算法生成了

的。两轮迭代能产生16个正方形。(Two passes were made over the surface resulting ineight squares.)(这又是个错误,原文中说是8个正方形,但是按照正确的思路,两轮下来

能产生16个正方形。运行程序你也能看到,确实是16个而不是8个。这里应该是作者的一个

笔误。)

You can change your viewpoint with the arrow keys. Rotate the surface around using theleft and right arrow keys, move the surface up or down by shifting the up and down arrowkeys, and move forwards or backwards over the surface using the (unshifted) up and downarrow keys.

你可以使用箭头键来改变视角。使用←和→键可以旋转场景。按住 Shift键再按↑和↓键可

以令场景向上或向下运动。松开 Shift键再按↑和↓键可以令场景向前或向后运动。

Now bring up the View Options dialog box. Use the View menu, or Ctrl-O. This is whereyou change what you are viewing and how it is generated. The dialog will look somethinglike this:现在解释 View Options 对话框。你可以在菜单栏里点击 View 选项,或者按 Ctrl-O(注意

是 O不是 0)来打开它。这里你可以改变你要想看到的东西,以及为“如何生成它们”设定

其属性值。对话框看起来像这个样子:

The Select rendering type pulldown menu determines what is displayed. 1D midpointdisplacement renders a line segment tessellated using the midpoint displacement algorithm.elect rendering type 下拉框菜单控制展示什么图形。1D midpoint displacement 渲染了一

条用“中点替换”算法渲染的线段。

All render types starting with 2D mesh use the diamond square algorithm to generate the

Page 26: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

image. 2D mesh / lines renders a surface in line mode. 2D mesh / rendered displays the2D surface, texture mapped with the current surface texture map (which the program refersto as the "teximage"), under a sky texture mapped with clouds. 2D mesh / clouds allowsyou to display only the cloud texture map. This is simply a 2D height map, using blue forsmall values and white for large values. 2D mesh teximage allows you to display only theteximage that covers the surface when in rendered mode. This is a lit surface with a top-down orthographic projection, using different colors to represent different heights on thesurface.所有标明有 2D mesh 的类型都是利用“菱形-正方形”算法生成的一幅图像。2D mesh /lines 是用线框模式渲染的一个表面。2D mesh / rendered 展示了一个“二维地形”,这个

面是用当前纹理贴图(就是选项“2D mesh teximage ”中生成的那个贴图)贴出来的,而

天空是用“云团贴图”贴出来的。2D mesh / clouds 允许你只观察“云团贴图”。这是一

个简单的二维高度图,蓝色表示小值,白色表示大值。2D mesh teximage 允许你只观察

在 rendered 模式中展出的那个覆盖地形的纹理贴图。这是一个“自顶向下”观察的正投影

表面。使用了不同的颜色来区别这个面不同的“高度值”。

You can control the generation of all these images using the parameters on the right half ofthe dialog box.

你可以使用对话框右半边的参数,以此控制这些图形的生成。

The Tile parameter determines how many surfaces or lines are tiled. By default this value isone. For lines, the value determines how many lines to tile. For surfaces, setting thisvalue to two creates a 2x2 tiling of surfaces; three creates a 3x3 tiling, etc.

参数 Tile 表示需要“排布”多少个表面或者线段。默认值是1。对于线段,这个参数决定了

会平铺多少条单位为“1”的线段;而对于表面,把这个参数设为2则表示有2x2个单位为

“1”的表面,3则表示有3x3个。如此等等。

The Random seed parameter sets a new random seed, thus creating a different surface.参数 Random seed设置了一个新的“随机种子”,以此可以创造出不同的表面。

The Iterations parameter determines how many tessellation passes are made over thesurface or line being displayed. Bigger numbers make more detail. By default it is set to two.The value can be in the range 1 through 10.参数 Iterations 决定会迭代多少次。数值越大则细节越多。默认值是 2,这个参数的取值范

围是从 1到 10。

You can set the level of detail of the cloud texture map using the Cloud iterations parameter.Likewise, the level of detail of the texture map used on the surface is set with the Teximageiterations parameter.你可以调节参数 Cloud iterations来设置“云团贴图”的细节度。类似地,你也可以调节参

数 eximage iterations 来设置“地形贴图”的细节度。

Note there are three H values. The first is used to generate the surface, the second is usedto generate the cloud texture map, and the third is used to generate the surface's teximagetexture map.注意,对话框里一共有三个 H值。第一个 H值用来生成表面,第二个 H值用来“云团贴

Page 27: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

图”,第三个 H值用来生成“地形贴图”。

When Rendering type is set to 1D midpoint displacement or 2D mesh / lines, Antialiasedlines toggles antialiased line mode.

当渲染类型选择1D midpoint displacement 或者2D mesh / lines时,勾选 Antialiased lines复选框会采用抗锯齿模式。

Invert colors swaps the background and line colors.

勾选 Invert colors复选框会反转背景和线条颜色。

When Rendering type is set to 2D mesh / rendered, Texture lineartoggles bilinear texturingon and off. Having this on in general results in a slower but higher quality image.

当渲染类型选择2D mesh / rendered时,Texture linear复选框控制“双线性纹理滤波方

式”的开关。勾选此开关后,结果的生成会耗时更长,但最后的图像素质也会更高。

CodeCodeCodeCode StructureStructureStructureStructure

代码结构

fractmod.c and fractmod.h are the C code heart of this example program. They comprisethe fractal generation module.文件 fractmod.c 和文件 fractmod.h是这个示例程序的 C程序代码。 他们包含了“分形生

成”模块。

The CFractalExampleView class is derived from the COpenGLView class as found in theNovember 1996 Microsoft Journal: http://www.microsoft.com/msj. The COpenGLView classwas written by Ron Fosner, who describes it as a hacked version of his fully-blownCOpenGLView class. To get the real thing, buy his book OpenGL Programming forWindows 95 and Windows NT published by Addison-Wesley.微软 1996年 11月的期刊上(http://www.microsoft.com/msj)刊载了一篇文章,提到

CFractalExampleView 类是 COpenGLView 类的派生类。COpenGLView类的作者是 RonFosner,他把这个类描述成原成熟类的“黑客版本”(The COpenGLView class waswritten by Ron Fosner, who describes it as a hacked version of his fully-blownCOpenGLView class)。(直到现在,微软期刊上仍有这篇文章,点击这里查看。)

想要知道到底怎么回事,你得买一本他写的书《Programming for Windows 95 andWindows NT》,该书由 Addison-Wesley出版。

The COpenGLView class has a RenderScene virtual member function which we override inCFractalExampleView. Here we do most of the rendering work. The function first examinesthe setting for Rendering type. When set to 2D mesh / lines or 1D midpoint displacement,the work is handled here in RenderScene. Otherwise, another function is called.

该 COpenGLView类有一个虚拟成员函数 RenderScene,在 CFractalExampleView类中我

们重写了它。这里我们做了大部分的渲染工作。这个函数首先检查“渲染类型”。当设置为

2D mesh / lines 或者1D midpoint displacement时,那么仍由原 RenderScene来处理。否

则的话我们调用另一个函数。

Page 28: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

CFractalExampleView::OnViewDialog generates the View Options dialog box and handlessetting and retrieving data between the dialog box class and CFractalExampleView.

函数 CFractalExampleView::OnViewDialog 用来生成 View Options 对话框,以及在对话框

类和 CFractalExampleView类之间设定和检索数据。

CFractalExampleView::OnInitialUpdate handles setting all CFractalExampleView membervariables to their default values (including dialog box values).

函数 CFractalExampleView::OnInitialUpdate负责把所有 CFractalExampleView类中的成员

变量设定为默认值(包括对话框中的值)。

There's really not much point in explaining further how the code works. I assume you are acompetent programmer, and I've done my best to comment the code fully. If you are notfamiliar with OpenGL, you might like to know that all functions starting with "gl" are OpenGLAPI calls. Microsoft Visual C++ has some limited documentation on this API.

其实真没什么必要去更深地解释代码是如何工作的。我假设你是一名很有能力的程序员,而

我也尽了最大努力来全面地讨论这些代码了。如果你不熟悉 OpenGL,那你也许很想知道的

是,那些以“gl”开头的函数其实都是 OpenggL API 的调用。微软的 Visual C++ 对此 API有一些有限的文件。

There is one feature that is just begging to be added to this code. In the file FractalExampleView.cpp, there is a preprocessor constant called DEF_HEIGHT_VALUE. This ispassed in to the fractal generation functions in fractmod.c where it is used to scale theheight values. This should really be a variable, controllable by the View Options dialog box.Feel free to add this feature.我请你为这个程序增添一个功能(There is one feature that is just begging to be added tothis code)。在文件 Fractal ExampleView.cpp中,有一个预置的常量

DEF_HEIGHT_SCALE (there is a preprocessor constant called DEF_HEIGHT_VALUE)(作者笔误,其实应该是 DEF_HEIGHT_SCALE),这个值会被传给位于 fractmod.c文件

的控制“分形生成”的函数里,以此来达到“缩放”高度值的目的。其实它应该被设定为一

个由对话框来控制的变量。请放心地添加这个功能。(意思是,作者认为这个值如果体现在

对话框里,直接由用户控制的话会更好更合适,但他的程序没有这么做。因此他希望读者来

完成这个功能)。

ReferencesReferencesReferencesReferences

参考书目

1Miller, Gavin S. P., The Definition and Rendering of Terrain Maps. SIGGRAPH 1986Conference Proceedings (Computer Graphics, Volume 20, Number 4, August 1986).

2Voss, Richard D., FRACTALS in NATURE: characterization, measurement, andsimulation. SIGGRAPH 1987 course notes #15.

3Peitgen, Jurgens, and Saupe, Chaos and Fractals, New Frontiers of Science. Springer-Verlag, 1992.

Page 29: Generating Random Fractal Terrain · Chaos and Fractals, New Frontiers of Science 《混沌和分形,科学的新前沿》 (网上找了找这方面的资料,没发现有中译本,因此尚不知该书的准确译名是什么)

4Fournier, A., Fussell, D., Carpenter, L., Computer Rendering of Stochastic Models,Communications of the ACM, June 1982.