199751
本帖最后由 199751 于 2023-3-18 16:33 编辑

我们当打开一个一个存档或者是创建一个存档过程中

一般就是经典的存档名称

世界类型还有是否允许作弊的选项

但是在这背后有没有想过一些问题

世界是如何被生成的呢

我们今天就从底层逻辑方面去探讨这些东西

文章具体内容多数引用知乎wiki


第一

我们现在探讨一些关于噪声函数的方面内容

柏林噪声函数,又称噪声函数

在我的世界生成地形方面起到了十分重要的作用

就比如说是在材质纹理的渲染等方面

一些我的世界比较著名的bug也是出自这个之手

比如“边境之地”

我们先来解释一下柏林噪声函数的定义

柏林噪声 (Perlin noise )指由Ken Perlin发明的自然噪声生成算法 。一个噪声函数基本上是一个种子随机发生器。它需要一个整数作为参数,然后根据这个参数返回一个随机数。如果两次都传同一个参数进来,它就会产生两次相同的数。这条规律非常重要,否则柏林函数只是生成一堆垃圾

我们常说的地图种子就是指柏林函数里的种子输入,“如果两次都传同一个参数进来,就会生成两次相同的数”

可以说为什么我们每次输入相同种子都会出现相同地形的原因

从本质上来说,柏林噪声函数

就是个随机生成器,我的世界里的地形就是这个函数随即生成

我们众所周知的都是知道我的世界的地形是一块一块的区块拼接起来的

具体的叙述

噪声(Noise)实际上就是一个随机数生成器,当然,这是一种伪随机(现实世界中的真随机在计算机中不存在)。我们所看到的那些黑白噪声图,实际上是随机数映射到0和1之间产生的灰度图。随机本身就是不同,那为什么还需要不同的随机?
普通噪声(随机数生成器)的问题在于,它实在太过于随机,毫无规律可言(如图1所示)。在上个世纪80年代,柏林噪声的发明者 Ken Perlin 当时正在参与迪士尼的经典电影电子世界争霸战(TRON)的制作。他想用噪声来模拟逼真的随机效果,但对最终产生的不自然的效果很不满意。在现实世界中,尽管很多自然现象(如山脉起伏、大理石纹理等)是随机的,但随机之中蕴含着规律,普通噪声很难体现出这种乱中有序的效果。(引知乎用户阿虎)


选取随机点,给定随机值
给定点位的梯度(用插值函数标识)
根据随机点的值与梯度计算出为赋值位置的数值
在这一过程中Perlin Noise使用一些特殊的处理策略保证生成的数据伪随机并且连续

很简单的数学函数原理

说明白点,就是在设定的是一个函数

这个函数的表达式就是特殊的处理方式

随机点代表定义域

反馈的值在进行运算生成新的数值

从而达到生成地形的目的

一下为函数生成原理

public double perlin(double,x,double,y,double,z,):

这是最基本的柏林函数式子

想当然我们看到这个

可能会莫名想起在游戏里面看到的f3

没错,xyz说白了就是你生成世界的媒介,每一块都有具体坐标就是这样来的

但是有没有想过一个问题

double函数虽然说是极为大的数字

可以说是天文数字,我们可以看看具体在java中的数值

[-1.79769313486231570e+308,-4.94065645841246544e-324] ∪  [4.94065645841246544e-324,1.79769313486231570e+308]

说具体点就是2的正负1024次方

但是但是,前提是这个数字还是有最终临界的

我们知道我的世界里面是有tp指令的

当然,麻将肯定是想到了这个问题,其实在如今版本基本不会看到这个情况了

但是在远古版本还是会有这样情况出现的

notch在原先版本没有设置tp指令的情况下还是知道到达这个数字的情况是怎么样的

所以说我们可以将这个天文数字称之为xyz坐标无限

这是很具体的比喻

因为的确就是坐标无限


我们可以知道

其实我的世界地形的确就像是天文数字一样,才会出现崩溃的现象



我们可以在这个视频里面看到,x坐标是无限

所以说其实notch早在远古其实就想到了这种情况

在本质上来说,或者是在早期游戏里面我们可以看到的是

我的世界其实在原本的单人游戏就只是个内置服务器

在1.7.10版本如果游戏突然出现崩溃的情况就会出现关闭内置服务器的字样

其实客户端只是单纯的进行渲染和输出,真正的内容展现在服务端

文章不去设计关于底层代码层面的内容

当然去具体的描述世界生成的流程

具体的就像是不断地采集点

引用柏林函数的内容

这段代码以二维柏林噪声为例,描述了确定一个点梯度值的过程。梯度表中的值都是随机生成的,通过对点的


坐标分别进行计算,可以得到点的梯度(即一个二维向量)。确定一点的梯度值并不止这一种方法,另一种可行方法之后会提到。

在一维柏林噪声中,各个整点的梯度就是斜率,所以我们可以不依赖梯度表,将排列表中所取到值直接作为梯度即可。因为排列表中的值是乱序的,所以有良好的随机性,且对于同样的输入,它的输出都相同,伪随机性也得以保留。在Ken Perlin的经典噪声算法中,使用的排列表是由数字0到255散列组成的。

static int[] perm = {151,160,137,91,90,15,

131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,

190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,

88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,

77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,

102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,

135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,

5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,

223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,

129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,

251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,

49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,

138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180};





可以看到在柏林函数里边的采点是要又255组组成

具体的采样原理在不具体展开

感兴趣的可以去知乎了解

说到底就是不断地采值去计算

最简单粗暴的理解



在这张早期反编译的图片里面我们可以清楚的看到地形生成的复杂程度

这仅仅只是群系方面的代码

而且在柏林函数中也包含具体的灰度值等内容,由于生成机制过于复杂

就好比立体几何中平面向量的应用



柏林噪声函数生成的晶体格

有没有很二维码

当你把噪声函数叠加的时候,你可能想了解每次具体使用了什么振幅和频率。上面一维的例子对于每个连续叠加的噪声函数使用了两倍的频率和二分之一倍的振幅。这个太普通了,事实上太普通,以至于很多人甚至从来都没有考虑过使用其他什么。尽管如此,你可以通过在每步使用其他的频率和振幅来创建不同特征的柏林噪声函数。例如,为了创建一个平滑滚动的丘陵,你可以使用大的振幅和小的频率的柏林噪声函数,同时小的振幅和高的频率,你可以创建一个平地,另外要创建非常颠簸的平面,应该选择小的振幅和低的频率。

在渲染中的应用:

1.在凸凹贴图它能很好地模拟火焰、云彩、奇形怪状的岩石,以及树木和大理石表面等;

2.做特效地模拟火焰、云彩等。

比如说立体的柏林噪声函数

等等内容都是生成我的世界最底层的原理、

64位或者是32位数据溢出也是大概由于bouble函数

引入文章内容



第一页 上一页 下一页 最后一页