随机世界的生成

内容来源:http://accidentalnoise.sourceforge.net/minecraftworlds.html

 更多搜索  unity Perlin

其他文字 为Unity3D游戏生成2D和3D多边形随机地图: http://www.manew.com/thread-40880-1-1.html

3D立方体世界级别生成

部分由于游戏的受欢迎程度的Minecraft出现在世界游戏设置做出立方体,3D地形充满了有趣的东西,如洞穴,出挑,等等所构成的想法引起大家的兴趣死灰复燃最近。这样的世界是ANL风格产生的噪声的理想应用这个例子是基于我之前讨论这种技术的努力的讨论。(这篇链接文章后来被收录在2011年4月的“ 游戏开发者杂志”杂志中。)自编写以来,图书馆的结构发生了一些变化。

在(链接到我的世界帖子)中,我讨论了使用3D噪声函数来实现Minecraft风格的地形。从那时起,图书馆已经发展了一点,所以我将重新审视这些帖子并再次讨论它。由于我不得不回答很多关于这个系统的问题,所以我将尝试更清楚地了解所涉及的概念。为了澄清这些基本概念,我将开始创建一个2D地形的想法,就像你在Terraria和King Arthur's Gold等游戏中看到的那样,然后将其扩展到像Minecraft这样的3D案例中。这将允许我更有效地使用图像来演示这些概念。

该系统具有以下抽象目标:能够使用特定点或单元的坐标来馈送系统,并且能够确定该位置应该是什么类型的块。我们希望它是一个黑盒子; 我们给它一点,我们得到一个块类型。当然,请注意,这仅适用于世界的初始生成。这些类型的游戏中的块可以通过玩家动作来改变,并且此时尝试使用相同类型的系统来描述这些改变是不合适的。需要以其他方式跟踪这些更改。这个系统产生了初始的世界,原始的,没有人类或非人类的手。

这种技术也可能不适合某些系统(如草或其他生物实体)的建模,因为这些系统本身就是复杂的实体,不容易以隐式方式建模。与降雪,结冰等系统相同......这里介绍的技术代表了一种隐含的方法,即可以在某一点评估的一个,并且在给定点的值不依赖于周围的值。生物和其他类型的系统通常需要了解周围环境以便执行精确的模拟。一个街区接收多少阳光?附近有水吗?必须回答这些问题和其他问题,以模拟生物系统的增长和传播,并在较小程度上模拟其他类型的气候相关系统。它也不适合用于水模拟。该系统没有流动概念,不了解流体力学或重力。水是一个复杂的主题,需要大量复杂的处理。

这并不是说隐含方法在这些方面完全没用; 事实并非如此。但是,在这些系统中,隐式方法只是众多工具中的一种。它们可用于模拟区域,扰动区域等,但系统的核心将是执行环境分析和模拟的显式方法,这超出了本文和此库的范围。

所以,我们只是模拟污垢和岩石。我们想要一个功能,告诉我们一个给定的位置应该是污垢,沙子,空气,金,铁,煤等......但是,首先,我们将保持简单。我们想要一个能告诉我们给定块是Solid还是Air的函数。这个功能应该模拟我们周围的地球。也就是说,天空在上方,坚固在下面。因此,让我们承担将天空与地球分开的圣经任务。为此,让我们探索渐变功能。梯度函数在N维空间中被给予线段(即,在我们正在工作的任何坐标空间中,无论是2D,3D还是更高),并计算沿着该段对齐的梯度场。输入坐标投影在此线上,并根据它们相对于定义线段的端点所在线的位置计算其梯度值。投影到段之间某处的点被赋予范围为(-1,1)的值。所以这给了我们一个很好的起点。我们可以设置沿Y轴对齐的渐变函数。在范围的顶部,我们将渐变场映射到-1(打开),在底部我们将它映射到1(实心)。

terraintree =
{
	{name =“ground_gradient”,type =“gradient”,x1 = 0,x2 = 0,y1 = 0,y2 = 1}
}

(关于符号的快速说明。示例代码以Lua声明表的形式编写。有关格式的更多信息,请参阅Lua集成部分。基本上,格式旨在由特殊类解析,读取声明并将它们转换为噪声模块实例的实际树。我更喜欢这种格式而不是更加冗长的逐步C ++格式,因为它更简洁,更清晰。我认为,源代码更具人性化并且比C ++代码更简洁。在大多数情况下,声明应该易于阅读和理解。模块被命名,源在适当的时候通过名称或值指定。用于解析表声明的Lua代码包括在内在源代码分发中,如果要直接使用这些声明。)

对于2D情况,梯度函数接受(x1,x2,y1,y2)形式的线段,扩展到3D情况的(x1,x2,y1,y2,z1,z2)。由(x1,y1)形成的点描绘了线段的起点,其被映射到0.由(x2,y2)形成的点是映射到1的线段的末端。所以这里我们映射线段(0,1) - >(0,0)到梯度。这将梯度设置在函数的Y = 1和Y = 0区域之间。所以这个乐队形成了世界的Y范围。我们映射的任何一个世界都将在这个范围内。我们可以映射X中的任何区域(实际上是无穷大,但受到双精度的限制),但有趣的东西,即地面,将位于波段内。现在,这种行为可以调整,但就目前而言,我们有足够的灵活性。请记住,任何高于或低于此范围的值都可能是无趣的,因为上面的那些可能全部都是开放的,而下面的那些可能都是可靠的。(正如你将在短时间内看到的那样,这个陈述实际上可能证明是错误的。尽管如此,更多关于此。)对于本系列中的大多数图像,我将映射由框(0,1)定义的方形区域。 - >(1,0)在2D空间中。那时,我们的世界看起来像这样:1) - >(1,0)在2D空间中。那时,我们的世界看起来像这样:1) - >(1,0)在2D空间中。那时,我们的世界看起来像这样:

 

看起来不是很多,而且它肯定不回答“给定点是固定还是开放?”的问题。为了回答这个问题,我们必须应用所谓的步骤函数我们希望在一侧的所有位置都是开放的,而另一侧的所有位置都是实心的,而不是平滑的梯度。ANL中,我们可以使用Select函数完成此操作Select函数接受两个输入函数或值(在这种情况下它们等同于Solid和Open),并根据控制函数的值(在本例中为Gradient函数)在它们之间进行选择。Select模块有两个附加参数,即阈值衰减,它们会影响此过程。对于这一步,不需要衰减,因此我们将其设置为0. 然而,阈值参数决定了Solid和Ground之间的分界线的绘制位置。渐变函数中大于此值的任何位置都将为“实心”,并且任何小于阈值的位置都将打开。由于渐变映射我们在0和1之间的范围,因此放置阈值的逻辑位置为0.5。这将空间分成两半。我们将表示1的值为solid,而将0的值表示为open。所以我们将设置地平面功能如下:

terraintree =
{
	{name =“ground_gradient”,type =“gradient”,x1 = 0,x2 = 0,y1 = 0,y2 = 1},
	{name =“ground_select”,type =“select”,low = 0,high = 1,threshold = 0.5,control =“ground_gradient”}
}

像以前一样映射函数的相同区域,我们得到这样的结果:

 

它确实回答了“给定点是固定还是开放?”的问题。您可以在2D空间中使用任何可能的坐标调用该函数,结果将为1或0,具体取决于该点相对于地面的位置。然而,这不是一个非常有趣的功能。只是一条扁平线,延伸到无限远。为了使它活跃起来,我们将谈论一种通常被称为“湍流”的技术。

对于将值添加到函数的输入坐标的概念,湍流是一个奇特的词。想象一下,你用坐标(0,1)调用地面函数。它位于地平面之上,因为在Y = 1时,梯度值为0,小于阈值0.5。所以这一点将被计算为Open。但是,如果在调用地面函数之前,该点已经以某种方式被转换了呢?如果我们从该点的Y坐标中减去一个随机值,该怎么办?说,3?我们减去3,得到坐标(0,-2)。现在,如果我们用这个点调用地面函数,那么该点被评估为实体,因为Y = -2位于梯度段的末尾之外,该梯度段映射到1.所以突然之间,而不是打开,点(0,1)会突然变得坚固。你会在空中漂浮一块坚固的岩石。通过在调用ground_select函数之前在输入点的Y坐标中添加或减去随机数,可以对函数中的任何点执行相同操作。这是ground_select函数的图像,展示了这一点。在调用ground_select函数之前,每个坐标位置都有一个在(-0.25,0.25)范围内添加到Y坐标的值。

 

这比扁平线更有趣,但它看起来并不像地面。这是因为每个点都被一个完全随机的值扰乱,导致混乱的混乱模式。但是,如果我们使用连续随机函数,例如ANL的 分形函数,而不是混乱的混乱模式,我们将获得更多控制的东西。所以,让我们继续前进,将分形线连接到地面,看看我们得到了什么。

terraintree =
{
	{name =“ground_gradient”,type =“gradient”,x1 = 0,x2 = 0,y1 = 0,y2 = 1},
	{name =“ground_shape_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 6,frequency = 2},
	{name =“ground_scale”,type =“scaleoffset”,scale = 0.5,offset = 0,source =“ground_shape_fractal”},
	{name =“ground_perturb”,type =“translatedomain”,source =“ground_gradient”,ty =“ground_scale”},
	
	{name =“ground_select”,type =“select”,low = 0,high = 1,threshold = 0.5,control =“ground_perturb”}
}

这里有几点需要注意。首先,我们设置一个Fractal模块,并使用ScaleOffset模块对其进行链接ScaleOffset模块将分形的输出缩放到更易管理的级别。一些地形可能是多山的,需要更大的规模,而其他地形更平坦,规模更小。我们稍后会详细讨论不同的地形类型,但现在这将用于演示目的。如果我们现在输出函数,我们会得到这样的结果:

 

现在这比纯随机噪音更有趣,不是吗?至少,即使某些景观有点奇怪,它也像地面一样,那些浮岛绝对是奇怪的。这是因为输出图中的每个点随机偏移了一个不同的值,由分形确定。为了便于说明,这里是分形的输出实际上是失真的:

 

上面图像为黑色的任何地方代表-0.25,白色的任何地方代表0.25。因此,无论分形是最暗的黑色,地面函数中的对应点都会向下“扭曲”0.25。(0.25表示屏幕的1/4。)由于一点可能会稍微扭曲,而空间上方的另一点可能会更加扭曲,这会导致悬垂和浮岛的可能性。自然界中的悬垂自然发生。浮岛当然不会。(除了阿凡达,就是。科学!)如果你的游戏想拥有这样奇特的幻想景观,那么这很棒,但如果你想要一个更逼真的模型,那么我们需要驯服提供失真的分形函数一点点。幸运的是,ScaleDomain 功能提供了这样的手段。

我们想要做的是强制该函数更像高度图函数。想象一下2D高度图,其中地图中的每个点表示网格点的网格点中的上升或下降的高度或低度。地图中的白色值表示高山,黑色值表示低山谷。我们想要一个类似的行为,但为了做到这一点,我们必须基本上消除其中一个维度。在高度图的情况下,我们正在从2D高度图创建3D地形。同样,在我们的2D地形的情况下,我们需要有一维高度图。通过强制分形中具有相同Y坐标的所有点来评估相同的值,然后我们将使用相同的X偏移具有相同量的所有点,从而确保不会出现浮岛。我们可以使用ScaleDomain来设置的scaleY因子为0。因此,被称为ground_shape_fractal功能之前,我们称之为ground_scale_y按比例绘制在y 0.坐标这确保了Y的值具有分形的输出没有影响,从而有效地将其变成一维噪声函数。为此,我们进行以下更改:

terraintree =
{
	{name =“ground_gradient”,type =“gradient”,x1 = 0,x2 = 0,y1 = 0,y2 = 1},
	{name =“ground_shape_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 6,frequency = 2},
	{name =“ground_scale”,type =“scaleoffset”,scale = 0.5,offset = 0,source =“ground_shape_fractal”},
	{name =“ground_scale_y”,type =“scaledomain”,source =“ground_scale”,scaley = 0},
	{name =“ground_perturb”,type =“translatedomain”,source =“ground_gradient”,ty =“ground_scale_y”},
	
	{name =“ground_select”,type =“select”,low = 0,high = 1,threshold = 0.5,control =“ground_perturb”}
}

我们将ScaleDomain函数链接到ground_scale,然后将ground_perturb的源更正为ScaleDomain函数。这将改变从上面的图像扰乱地面的分形,而不是:

现在,如果我们查看输出,我们可以看到结果:

 

好多了。浮岛完全被消除,地形更像是连绵起伏的山脉和丘陵。然而,这种不幸的副作用是悬崖和悬崖的丢失。现在所有的地面都是连续的,滚动的。如果我们愿意,我们可以通过多种方式纠正这种情况。

第一种方法是使用另一个TranslateDomain函数和另一个Fractal如果我们在X方向上施加少量的分形湍流,我们可以稍微扰乱山脉的边缘和表面,足以形成一些悬崖和悬垂。让我们看看它在行动中。

terraintree =
{
	{name =“ground_gradient”,type =“gradient”,x1 = 0,x2 = 0,y1 = 0,y2 = 1},
	{name =“ground_shape_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 6,frequency = 2},
	{name =“ground_scale”,type =“scaleoffset”,scale = 0.5,offset = 0,source =“ground_shape_fractal”},
	{name =“ground_scale_y”,type =“scaledomain”,source =“ground_scale”,scaley = 0},
	{name =“ground_perturb”,type =“translatedomain”,source =“ground_gradient”,ty =“ground_scale_y”},
	{name =“ground_overhang_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 6,frequency = 2},
	{name =“ground_overhang_scale”,type =“scaleoffset”,source =“ground_overhang_fractal”,scale = 0.2,offset = 0},
	{name =“ground_overhang_perturb”,type =“translatedomain”,source =“ground_perturb”,tx =“ground_overhang_scale”},
	
	{name =“ground_select”,type =“select”,low = 0,high = 1,threshold = 0.5,control =“ground_overhang_perturb”}
}

结果是:

 

第二种方式就是设定的scaleY的参数ground_scale_y到的东西比0离开Y标尺的功能有点让一些变化大,虽然较高的设置规模,更多的地形会来类似于早期的,未扩展的版本。

这些结果肯定比滚山更有趣。然而,尽管它很有趣,它仍然会变得非常无聊,探索一个无尽的英里和相同的一般模式的地形。更重要的是,这样的地形将是非常不现实的。自然界有很多变化来保持事物的趣味性。因此,让我们看看我们可以做些什么来让世界变得更加多样化。

查看前面的示例代码,我们可以看到那里的模式。我们有渐变功能,在应用步进功能和地面给定坚固之前,由功能操纵以给出地面形状。因此,为了增加不同的地形而开始使事物变得复杂的自然地方是给出地形的部分。我们可以像我们喜欢的那样复杂(而且我们可以负担得起;每个分形都会增加处理开销,所以你应该只使用尽可能多的分数,而不是使用一个分形来扰乱Y和另一个扰乱X.需要,并尽量保守。)我们可以设置代表山脉,山麓,低地平原,荒地等的地形......并使用各种选择的输出函数,链接到低频分形,描绘每种类型的区域。那么,让我们来看看我们如何实现各种类型的地形。

为了便于说明,我们将设置三种类型的地形:高地(平缓起伏的山丘),山脉和低地(大多是平坦的)。我们将使用基于选择的系统在它们之间切换,并将它们全部拼接成复杂的挂毯。我们走了......

低地:

这个很容易。我们可以使用上面的设置,稍微降低山丘的振幅,甚至可以使它们比加法更具减法性,以降低平均海拔。我们可能会降低八度计数以使它们更平滑。

{name =“lowland_shape_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 2,frequency = 1},
{name =“lowland_autocorrect”,type =“autocorrect”,source =“lowland_shape_fractal”,low = 0,high = 1},
{name =“lowland_scale”,type =“scaleoffset”,source =“lowland_autocorrect”,scale = 0.2,offset = -0.25},
{name =“lowland_y_scale”,type =“scaledomain”,source =“lowland_scale”,scaley = 0},
{name =“lowland_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“lowland_y_scale”},

高原:

再次,简单。(实际上,这些都不是真的那么难。)但是,我们将使用不同的基础,使山丘像沙丘一样。

{name =“highland_shape_fractal”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 2,frequency = 2},
{name =“highland_autocorrect”,type =“autocorrect”,source =“highland_shape_fractal”,low = 0,high = 1},
{name =“highland_scale”,type =“scaleoffset”,source =“highland_autocorrect”,scale = 0.45,offset = 0},
{name =“highland_y_scale”,type =“scaledomain”,source =“highland_scale”,scaley = 0},
{name =“highland_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“highland_y_scale”},

山:

{name =“mountain_shape_fractal”,type =“fractal”,fractaltype = anl.BILLOW,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 4,frequency = 1},
{name =“mountain_autocorrect”,type =“autocorrect”,source =“mountain_shape_fractal”,low = 0,high = 1},
{name =“mountain_scale”,type =“scaleoffset”,source =“mountain_autocorrect”,scale = 0.75,offset = 0.25},
{name =“mountain_y_scale”,type =“scaledomain”,source =“mountain_scale”,scaley = 0.1},
{name =“mountain_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“mountain_y_scale”},

当然,你可以获得更多的创意,但这确立了一般模式。您可以考虑地形的特征,并构建适合您的噪声功能。所有这些都遵循相同的原则; 差异主要在于规模。现在,为了将它们组合在一起,我们将设置一些额外的分形来充当Select的控件然后我们将Select模块链接在一起以生成整体。

{name =“terrain_type_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 3,frequency = 0.5},
{name =“terrain_autocorrect”,type =“autocorrect”,source =“terrain_type_fractal”,low = 0,high = 1},
{name =“terrain_type_cache”,type =“cache”,source =“terrain_autocorrect”},
{name =“highland_mountain_select”,type =“select”,low =“highland_terrain”,high =“mountain_terrain”,control =“terrain_type_cache”,threshold = 0.55,falloff = 0.15},
{name =“highland_lowland_select”,type =“select”,low =“lowland_terrain”,high =“highland_mountain_select”,control =“terrain_type_cache”,threshold = 0.25,falloff = 0.15},

所以我们在这里为低地,高地和山脉建立了三种主要类型。我们使用一个分形来在所有三个之间进行选择,以便从低地 - >高地 - >山脉中自然发展。然后我们使用另一个分形随机将荒地插入地图。最终的模块链是:

terraintree =
{
	{name =“lowland_shape_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 2,frequency = 1},
	{name =“lowland_autocorrect”,type =“autocorrect”,source =“lowland_shape_fractal”,low = 0,high = 1},
	{name =“lowland_scale”,type =“scaleoffset”,source =“lowland_autocorrect”,scale = 0.2,offset = -0.25},
	{name =“lowland_y_scale”,type =“scaledomain”,source =“lowland_scale”,scaley = 0},
	{name =“lowland_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“lowland_y_scale”},
	{name =“ground_gradient”,type =“gradient”,x1 = 0,x2 = 0,y1 = 0,y2 = 1},
	{name =“highland_shape_fractal”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 2,frequency = 2},
	{name =“highland_autocorrect”,type =“autocorrect”,source =“highland_shape_fractal”,low = 0,high = 1},
	{name =“highland_scale”,type =“scaleoffset”,source =“highland_autocorrect”,scale = 0.45,offset = 0},
	{name =“highland_y_scale”,type =“scaledomain”,source =“highland_scale”,scaley = 0},
	{name =“highland_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“highland_y_scale”},

	{name =“mountain_shape_fractal”,type =“fractal”,fractaltype = anl.BILLOW,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 4,frequency = 1},
	{name =“mountain_autocorrect”,type =“autocorrect”,source =“mountain_shape_fractal”,low = 0,high = 1},
	{name =“mountain_scale”,type =“scaleoffset”,source =“mountain_autocorrect”,scale = 0.75,offset = 0.25},
	{name =“mountain_y_scale”,type =“scaledomain”,source =“mountain_scale”,scaley = 0.1},
	{name =“mountain_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“mountain_y_scale”},

	{name =“terrain_type_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 3,frequency = 0.5},
	{name =“terrain_autocorrect”,type =“autocorrect”,source =“terrain_type_fractal”,low = 0,high = 1},
	{name =“terrain_type_cache”,type =“cache”,source =“terrain_autocorrect”},
	{name =“highland_mountain_select”,type =“select”,low =“highland_terrain”,high =“mountain_terrain”,control =“terrain_type_cache”,threshold = 0.55,falloff = 0.15},
	{name =“highland_lowland_select”,type =“select”,low =“lowland_terrain”,high =“highland_mountain_select”,control =“terrain_type_cache”,threshold = 0.25,falloff = 0.15},
	{name =“ground_select”,type =“select”,low = 0,high = 1,threshold = 0.5,control =“highland_lowland_select”}
}

以下是这将产生的各种地形的随机全景照片:

你可以看到那里有一个非常好的变化。有些地方有高耸的锯齿状山脉,而其他地方有平缓的公寓。现在,我们想要添加洞穴,以探索地下的奇迹。

对于洞穴,我使用了一个针对ground_select的乘法系统也就是说,我想出了一个输出1或0的函数,并将它与ground_select的输出相乘这具有设置为在洞穴函数中打开函数中0的任何位置的效果。所以我想要一个洞穴出现的地方必须在洞穴功能中返回0,我希望它不是我设置为1的洞穴。至于洞穴的形状,我喜欢将洞穴系统基于1个八度音程山脊多重分形

{name =“cave_shape”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 1,frequency = 2},

这导致类似于:

如果将Select函数应用为阶梯函数,就像我们对地面渐变一样,使阈值的低边选择为1(无洞穴),高边选择为0(洞穴),然后结果看起来像这样:

{name =“cave_shape”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 1,frequency = 2},
{name =“cave_select”,type =“select”,low = 1,high = 0,control =“cave_shape”,threshold = 0.8,falloff = 0},

结果:

当然,这看起来相当平滑,所以让我们添加一些分形噪声来扰乱域。

{name =“cave_shape”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 1,frequency = 2},
{name =“cave_select”,type =“select”,low = 1,high = 0,control =“cave_shape”,threshold = 0.8,falloff = 0},
{name =“cave_perturb_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 6,frequency = 3},
{name =“cave_perturb_scale”,type =“scaleoffset”,source =“cave_perturb_fractal”,scale = 0.25,offset = 0},
{name =“cave_perturb”,type =“translatedomain”,source =“cave_select”,tx =“cave_perturb_scale”},

结果:

这样可以使洞穴稍微变暗,并使它们不那么平滑。让我们看看如果我们现在将洞穴应用于地形可能会是什么样子:

通过与该值打门限cave_select,我们可以使洞穴薄或厚。但我们真正应该做的一件事就是尽量做到这一点,以便洞穴不会从地表地形挖出如此巨大的,不敬虔的块状物。要做到这一点,我们可以回到highland_lowland_select函数,你会记得,这是用于扰乱地面渐变的最终地形函数。这里有用的功能是它仍然是一个梯度,随着函数深入地球而增加。我们可以使用这个梯度来减弱洞穴功能,以便在您深入地球时洞穴变得更大。幸运的是,这种衰减可以简单地通过乘以输出来实现highland_lowland_select函数使用cave_shape的输出,然后将结果传递给cave函数链的其余部分。现在,我们要做的一个重要改变是增加了一个Cache功能。缓存函数将存储给定输入坐标的函数结果,如果使用相同的坐标再次调用该函数,它将返回缓存的副本,而不是重新计算结果。这对于这样的情况很有用,其中一个复杂函数(highland_lowland_select)将在模块链中被多次调用。如果没有缓存,每次调用复杂函数的整个链时都会重新计算。要引入缓存,我们首先进行以​​下更改:

{name =“highland_lowland_select”,type =“select”,low =“lowland_terrain”,high =“highland_mountain_select”,control =“terrain_type_cache”,threshold = 0.25,falloff = 0.15},
{name =“highland_lowland_select_cache”,type =“cache”,source =“highland_lowland_select”},
{name =“ground_select”,type =“select”,low = 0,high = 1,threshold = 0.5,control =“highland_lowland_select_cache”},

这会附加Cache,然后重定向ground_select的输入来自缓存,而不是直接来自函数。然后我们可以修改洞穴代码来添加衰减:

{name =“cave_shape”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 1,frequency = 4},
{name =“cave_attenuate_bias”,type =“bias”,source =“highland_lowland_select_cache”,bias = 0.45},
{name =“cave_shape_attenuate”,type =“combiner”,operation = anl.MULT,source_0 =“cave_shape”,source_1 =“cave_attenuate_bias”},
{name =“cave_perturb_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 6,frequency = 3},
{name =“cave_perturb_scale”,type =“scaleoffset”,source =“cave_perturb_fractal”,scale = 0.5,offset = 0},
{name =“cave_perturb”,type =“translatedomain”,source =“cave_shape_attenuate”,tx =“cave_perturb_scale”},
{name =“cave_select”,type =“select”,low = 1,high = 0,control =“cave_perturb”,threshold = 0.48,falloff = 0},

我们首先添加了Bias功能。这是为了方便起见,允许我们调整梯度衰减函数的范围。然后我们添加了cave_shape_attenuate函数,它是anl :: MULT类型Combiner会将渐变乘以cave_shape然后将结果传递给cave_perturb函数。结果看起来像这样:

你可以看到洞穴在朝向地面的区域变得更薄。(忽略顶部的东西,它只是渐变的一个神器,对最终的洞穴没有影响。如果它成为一个问题 - 比方说,如果你把这个函数用于别的东西,那么渐变可以在被使用之前被钳制到(0,1)。)有点难以看出它是如何与地形相关的,所以让我们继续把所有东西合成在一起,看看我们得到了什么。到目前为止,这是我们的整个功能链。

terraintree =
{
	{name =“ground_gradient”,type =“gradient”,x1 = 0,x2 = 0,y1 = 0,y2 = 1},
	
	{name =“lowland_shape_fractal”,type =“fractal”,fractaltype = anl.BILLOW,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 2,frequency = 0.25},
	{name =“lowland_autocorrect”,type =“autocorrect”,source =“lowland_shape_fractal”,low = 0,high = 1},
	{name =“lowland_scale”,type =“scaleoffset”,source =“lowland_autocorrect”,scale = 0.125,offset = -0.45},
	{name =“lowland_y_scale”,type =“scaledomain”,source =“lowland_scale”,scaley = 0},
	{name =“lowland_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“lowland_y_scale”},
	
	{name =“highland_shape_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 4,frequency = 2},
	{name =“highland_autocorrect”,type =“autocorrect”,source =“highland_shape_fractal”,low = -1,high = 1},
	{name =“highland_scale”,type =“scaleoffset”,source =“highland_autocorrect”,scale = 0.25,offset = 0},
	{name =“highland_y_scale”,type =“scaledomain”,source =“highland_scale”,scaley = 0},
	{name =“highland_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“highland_y_scale”},

	{name =“mountain_shape_fractal”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 8,frequency = 1},
	{name =“mountain_autocorrect”,type =“autocorrect”,source =“mountain_shape_fractal”,low = -1,high = 1},
	{name =“mountain_scale”,type =“scaleoffset”,source =“mountain_autocorrect”,scale = 0.45,offset = 0.15},
	{name =“mountain_y_scale”,type =“scaledomain”,source =“mountain_scale”,scaley = 0.25},
	{name =“mountain_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“mountain_y_scale”},

	{name =“terrain_type_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 3,frequency = 0.125},
	{name =“terrain_autocorrect”,type =“autocorrect”,source =“terrain_type_fractal”,low = 0,high = 1},
	{name =“terrain_type_y_scale”,type =“scaledomain”,source =“terrain_autocorrect”,scaley = 0},
	{name =“terrain_type_cache”,type =“cache”,source =“terrain_type_y_scale”},
	{name =“highland_mountain_select”,type =“select”,low =“highland_terrain”,high =“mountain_terrain”,control =“terrain_type_cache”,threshold = 0.55,falloff = 0.2},
	{name =“highland_lowland_select”,type =“select”,low =“lowland_terrain”,high =“highland_mountain_select”,control =“terrain_type_cache”,threshold = 0.25,falloff = 0.15},
	{name =“highland_lowland_select_cache”,type =“cache”,source =“highland_lowland_select”},
	{name =“ground_select”,type =“select”,low = 0,high = 1,threshold = 0.5,control =“highland_lowland_select_cache”},
	
	{name =“cave_shape”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 1,frequency = 4},
	{name =“cave_attenuate_bias”,type =“bias”,source =“highland_lowland_select_cache”,bias = 0.45},
	{name =“cave_shape_attenuate”,type =“combiner”,operation = anl.MULT,source_0 =“cave_shape”,source_1 =“cave_attenuate_bias”},
	{name =“cave_perturb_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 6,frequency = 3},
	{name =“cave_perturb_scale”,type =“scaleoffset”,source =“cave_perturb_fractal”,scale = 0.5,offset = 0},
	{name =“cave_perturb”,type =“translatedomain”,source =“cave_shape_attenuate”,tx =“cave_perturb_scale”},
	{name =“cave_select”,type =“select”,low = 1,high = 0,control =“cave_perturb”,threshold = 0.48,falloff = 0},
	
	{name =“ground_cave_multiply”,type =“combiner”,operation = anl.MULT,source_0 =“cave_select”,source_1 =“ground_select”}
}

以下是此功能的随机位置选择:

现在看起来很不错。洞穴都是地下深处相当大的洞穴,但在地表,它们往往会衰减到小隧道。这有助于营造神秘气息。当您外出探索这片土地时,您会遇到一个小洞穴入口。它去哪儿了?它有多深?没有办法说,但随着你的探索,它开始扩大到广阔的洞穴,充满了黑暗和危险。当然还有战利品。永远是战利品。

您可以通过多种方式调整此方法以获得不同的结果。您可以修改阈值设置cave_select和设置cave_attenuate_bias,或更改cave_attenuate_bias到不同的功能来重映射渐变的范围,以更好地满足您的需求。您可以添加另一个在Y轴上扰乱洞穴系统的分形,以消除X方向上偶然出现的非自然光滑隧道(仅在X中扰乱洞穴形状)。您可以添加另一个分形作为另一个衰减源,设置为cave_shape_attenuate的第三个源在区域基础上缩放衰减,使得洞穴在某些区域(例如,可能是山脉)更密集地出现,而在其他区域则更不密集或根本不出现。该区域选择器将从terrain_type_fractal函数派生,以了解山区的位置。这一切只是考虑你想要什么,了解各种功能对输出的影响,并尝试各种参数,直到你得到你喜欢的东西。这不是一门完美的科学,通常有很多方法可以达到任何特定的效果。

缺点

这种地形生成方法存在缺陷。产生噪音可能相当慢。重要的是减少分形的数量,你使用的分形的八度计数,以及尽可能减少其他慢速操作。尽可能尝试重新使用分形,并缓存每个被调用多次的函数。在这个例子中,我非常自由地使用分形,为三种地形类型中的每一种提供单独的分形。通过使用ScaleOffset重新映射范围并将它们全部基于单个分形,我本可以节省大量处理时间。在2D中它并不是那么糟糕,但是当你开始进入3D并尝试映射大量数据时,时间真的会增加。

扩展到3D

现在,如果你制作像TerrariaKing Arthur's Gold这样的游戏,这一切都很棒,但如果你想制作像MinecraftInfiniminer这样的游戏呢?代替?我们要对功能链做出哪些改变?答案是“不多”。上面的功能链几乎可以直接用于3D地形。您所要做的就是使用生成器的3D变体映射3D体积,并将Y轴映射到体积的垂直轴,而不是2D区域。但是,需要进行一项更改,即洞穴的工作方式。你看,Ridged Multifractal适用于2D洞穴系统,但在3D中它雕刻了一系列弯曲的壳而不是隧道,效果不对。所以在3D中,有必要设置2个洞穴形状分形,包括1个八度岭脊多重分形噪声但具有不同的种子,将它们选择为1或0,并将它们相乘。这样,无论分形交叉哪里变成洞穴,

terraintree3d =
{
	{name =“ground_gradient”,type =“gradient”,x1 = 0,x2 = 0,y1 = 0,y2 = 1},
	
	{name =“lowland_shape_fractal”,type =“fractal”,fractaltype = anl.BILLOW,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 2,frequency = 0.25},
	{name =“lowland_autocorrect”,type =“autocorrect”,source =“lowland_shape_fractal”,low = 0,high = 1},
	{name =“lowland_scale”,type =“scaleoffset”,source =“lowland_autocorrect”,scale = 0.125,offset = -0.45},
	{name =“lowland_y_scale”,type =“scaledomain”,source =“lowland_scale”,scaley = 0},
	{name =“lowland_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“lowland_y_scale”},
	
	{name =“highland_shape_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 4,frequency = 2},
	{name =“highland_autocorrect”,type =“autocorrect”,source =“highland_shape_fractal”,low = -1,high = 1},
	{name =“highland_scale”,type =“scaleoffset”,source =“highland_autocorrect”,scale = 0.25,offset = 0},
	{name =“highland_y_scale”,type =“scaledomain”,source =“highland_scale”,scaley = 0},
	{name =“highland_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“highland_y_scale”},

	{name =“mountain_shape_fractal”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 8,frequency = 1},
	{name =“mountain_autocorrect”,type =“autocorrect”,source =“mountain_shape_fractal”,low = -1,high = 1},
	{name =“mountain_scale”,type =“scaleoffset”,source =“mountain_autocorrect”,scale = 0.45,offset = 0.15},
	{name =“mountain_y_scale”,type =“scaledomain”,source =“mountain_scale”,scaley = 0.25},
	{name =“mountain_terrain”,type =“translatedomain”,source =“ground_gradient”,ty =“mountain_y_scale”},

	{name =“terrain_type_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 3,frequency = 0.125},
	{name =“terrain_autocorrect”,type =“autocorrect”,source =“terrain_type_fractal”,low = 0,high = 1},
	{name =“terrain_type_y_scale”,type =“scaledomain”,source =“terrain_autocorrect”,scaley = 0},
	{name =“terrain_type_cache”,type =“cache”,source =“terrain_type_y_scale”},
	{name =“highland_mountain_select”,type =“select”,low =“highland_terrain”,high =“mountain_terrain”,control =“terrain_type_cache”,threshold = 0.55,falloff = 0.2},
	{name =“highland_lowland_select”,type =“select”,low =“lowland_terrain”,high =“highland_mountain_select”,control =“terrain_type_cache”,threshold = 0.25,falloff = 0.15},
	{name =“highland_lowland_select_cache”,type =“cache”,source =“highland_lowland_select”},
	{name =“ground_select”,type =“select”,low = 0,high = 1,threshold = 0.5,control =“highland_lowland_select_cache”},
	
	{name =“cave_attenuate_bias”,type =“bias”,source =“highland_lowland_select_cache”,bias = 0.45},
	{name =“cave_shape1”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 1,frequency = 4},
	{name =“cave_shape2”,type =“fractal”,fractaltype = anl.RIDGEDMULTI,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 1,frequency = 4},
	{name =“cave_shape_attenuate”,type =“combiner”,operation = anl.MULT,source_0 =“cave_shape1”,source_1 =“cave_attenuate_bias”,source_2 =“cave_shape2”},             
	{name =“cave_perturb_fractal”,type =“fractal”,fractaltype = anl.FBM,basistype = anl.GRADIENT,interptype = anl.QUINTIC,octaves = 6,frequency = 3},
	{name =“cave_perturb_scale”,type =“scaleoffset”,source =“cave_perturb_fractal”,scale = 0.5,offset = 0},
	{name =“cave_perturb”,type =“translatedomain”,source =“cave_shape_attenuate”,tx =“cave_perturb_scale”},
	{name =“cave_select”,type =“select”,low = 1,high = 0,control =“cave_perturb”,threshold = 0.48,falloff = 0},
	
	{name =“ground_cave_multiply”,type =“combiner”,operation = anl.MULT,source_0 =“cave_select”,source_1 =“ground_select”}
}

一些结果:

现在,它看起来像一些参数需要一些调整。也许减少衰减,或加厚洞穴,减少地形分形中的八度音阶数,使地形更平滑等等......再次,这完全取决于你想要达到的目的。

posted @ 2018-07-12 17:48  三页菌  阅读(1010)  评论(3编辑  收藏  举报