精华内容
下载资源
问答
  • Perlin噪声
    2020-12-30 09:26:50

    最近在知乎上看了一篇关于用C++加速Python的短文,受益匪浅。同时也受到启发,撰写此文作为以后的参考。

    作为Python的用户经常碰到的一个问题就是速度太慢,一般来说速度下降的一个主要原因是来自多重的for循环。如何给现有的Python代码加速其实是Python用户的一门必修课。比较熟悉numpy的用户,可以熟练地写成矩阵化操作,这样可以大大加速运行的效率。这和MATLAB里多用矩阵操作而少用for循环是一个道理。但往往我们中的大多数并没有高超的numpy技巧。即便是有,Python代码的阅读性可能反而会下降。就算退一步讲,写成了比较好的numpy的代码也未必会比C/C++的代码快。在不放弃Python语言的前提下,怎么用C语言来提速呢?方法有很多,前面的所指的短文已经给出了一个答案。这里我再用一个例子做一个简单的说明,希望对大家有所帮助。首先代码都可以取到,https://github.com/yanfeit/PerlinNoise​github.com

    关于Perlin噪声,我就不详细介绍。简单地说来,Perlin噪声具有光滑性,自然性和随机性的特点。感兴趣的读者可以找到很多相关资料,在这里我推荐两个,pvigier的GitHub site和Adrian's 的博客。Pvigier的Perlin噪声是用numpy来实现的,读者如果对自己numpy的技巧深感自信,可以去阅读一下他写的代码。阅读他的代码之后,我们可以给自己两个问题:我是否可以写出这样高度矩阵化操作的numpy代码?

    是否我们遇到的所用问题都可以用numpy矩阵化的操作来解决?

    我想读者心中可能也会犯嘀咕,确实,高度矩阵化的操作需要程序员有高超的numpy技巧。反正我自愧不如,认为我很难写出那样漂亮的代码。我们再来看看Adrian的博客,这是篇博客文中的上乘之作, 是关于Perlin噪声的一个详细介绍,配合C#来实现。我想大多数读者可能和我一样,对写成for循环的形式感到极度舒适。而阅读这样的代码我想读者们也是驾轻就熟吧。所以我首先制作了一个Perlin噪声的C++代码(其实只用到了C的成分),之后我们会使用ctypes来调用动态链接库的代码。

    创建动态链接库

    $ mkdir build

    $ cd build

    $ cmake ..

    $ make

    $ mv ./lib/libperlinNoise.dylib ../python # Move library to the python folder.

    $ cd ../python

    $ python caltime.py # compare the time usage of numpy and C code.

    为了快速测试一下效果,读者可以尝试执行以上的代码。

    用了make之后我们会在/build/lib目录下得到一个libperlinNoise.dylib的动态链接库文件,在这个库里面我们可以调用两个函数。它们的接口如下所示,

    // 你可以在./lib/PerlinNoise.h的文件中找到相应代码。// 纯粹为了解释一下Float#ifndef PRECISION#define PRECISION 1#endif#if PRECISION==1typedef float Float;

    #elsetypedef double Float;

    #endif// .....省略很多代码......// lattice 是指生成梯度所在的格子的大小,x,y,z当然指的是方向// res 是指resolution,解析度(像素)。这里我与Pvigier的定义是不同的。Float* perlinNoise3D(int lattice_x, int lattice_y, int lattice_z,

    int res_x, int res_y, int res_z);

    Float* perlinNoise2D(int lattice_x, int lattice_y, int res_x, int res_y);

    两个函数返回的是指向Float的指针,我选用了单精度的浮点数也就是float。这里面有个需要注意的地方,函数切记不要返回指向一个超过二维数组的指针,其实根本就没有这样的定义,具体请看这个帖子。有了libperlinNoise.dylib这个动态链接库之后,剩下的任务就交给Python了。以下是我的代码的一部分(借鉴了Pvigier的代码,在./python/cppnoise.py中可以找到相应的代码),

    # 我们所采用加速的方法,ctypes是build-in package

    import ctypes

    # 因为我们要返回指针,我觉得调用numpy下的这个包是我找到的比较简单可行的方法

    from numpy.ctypeslib import ndpointer

    # ...省略...其余的包的调用....

    def octavePerlin2d(lattice, res, octaves = 1, persistence=0.5):

    # pass the dynamic library

    lib = ctypes.CDLL('./libperlinNoise.dylib')

    # get the 2d Perlin noise function

    perlinNoise2D = lib.perlinNoise2D

    # Need specify the types of the argument for function perlinNoise2D

    perlinNoise2D.argtypes = (ctypes.c_int, ctypes.c_int,

    ctypes.c_int, ctypes.c_int)

    # This note is extremely useful to understand how to return a 2d array!

    # https://stackoverflow.com/questions/43013870/

    # how-to-make-c-return-2d-array-to-python?noredirect=1&lq=1

    # We can never pass a 2d array, therefore return 1d array in a C function

    perlinNoise2D.restype = ndpointer(dtype=ctypes.c_float,

    shape = (res[0], res[1]))

    noise = np.zeros(res)

    frequency = 1

    amplitude = 1

    for _ in range(octaves):

    temp = perlinNoise2D(ctypes.c_int(frequency*lattice[1]),

    ctypes.c_int(frequency*lattice[0]),

    ctypes.c_int(res[1]),

    ctypes.c_int(res[0]) )

    noise += amplitude * temp

    frequency *= 2

    amplitude *= persistence

    return noise

    相比于木盏的函数,我这里相对来说复杂一点点。我们需要注意的是,我们得告诉函数传入的参数的类型和返回的类型和大小,这点至关重要。

    # 读取动态链接库,

    lib = ctypes.CDLL('./libperlinNoise.dylib')

    # 从句柄中拿到PerlinNoise2D的函数

    perlinNoise2D = lib.perlinNoise2D

    # 告诉函数传入参数的个数和类型

    perlinNoise2D.argtypes = (ctypes.c_int, ctypes.c_int,

    ctypes.c_int, ctypes.c_int)

    # 告诉函数返回的类型,是c_float。shape是optinal的。

    perlinNoise2D.restype = ndpointer(dtype=ctypes.c_float,

    shape = (res[0], res[1]))

    # 调用的方法非常简单。

    temp = perlinNoise2D(ctypes.c_int(frequency*lattice[1]),

    ctypes.c_int(frequency*lattice[0]),

    ctypes.c_int(res[1]),

    ctypes.c_int(res[0]) )

    在Python中调用动态链接库后得到的加速效果(当然我在C++用了单精度的float),读者可以自行修改成双精度去测试一下。

    $ python caltime.py

    2D noise, numpy time consuming: 0.08261830806732177

    3D noise, numpy time consuming: 4.525643992424011

    2D noise, cpp time consuming: 0.007184123992919922

    3D noise, cpp time consuming: 0.1645211935043335

    我们可以发现C语言的代码可以说快了将近10倍以上。

    最后上个图,以飨读者。

    更多相关内容
  • pyerlin:在python中玩perlin噪声
  • 柏林噪声 JAVA中Perlin噪声算法的实现。 输出 在项目文件夹中生成一个带有噪波纹理的 .png 文件。 图片
  • 一个简单的Perlin噪声发生器。 您需要的主要文件是Perlin.h和Perlin.cpp。 实例化一个Perlin对象后,您可以生成一个平滑的Perlin噪声值,如下所示: Perlin p; p.noise(x,y,z) 其中x,y,z是表示3D空间中位置的...
  • Rust中简单的Perlin噪声实现例子 extern crate perlin_noise as perlin;use perlin :: PerlinNoisefn main () { let perlin = PerlinNoise :: new (); println! ( "{}" ,perlin. get ( 132.2 )); println! ( "{}" ,...
  • 程序开发中总会用到随机方法,一般的随机方法虽然通用,但是产生的随机数又因为过于"随机",不适合用来生成平滑连续的随机数据(譬如自然地形的高度),这个时候我们便需要使用特殊的随机方法了, Perlin 噪声便是一种能够...

    程序开发中总会用到随机方法,一般的随机方法虽然通用,但是产生的随机数又因为过于"随机",不适合用来生成平滑连续的随机数据(譬如自然地形的高度),这个时候我们便需要使用特殊的随机方法了, Perlin 噪声便是一种能够产生平滑(随机)数值的随机方法.

    Perlin 噪声

    理解了二维的 Value 噪声,我们就可以进一步来看 二维的 Perlin 噪声了.

    二维 Perlin 噪声的生成方式和 二维 Value 噪声的生成方式大体相同,二维 Perlin 噪声也是根据给定的坐标选取对应的正方形,并将该正方形的四个顶点作为插值端点,但是在 Perlin 噪声中,端点并不是直接对应一个随机值,而是对应一个二维(梯度)向量,另外我们再取端点到给定坐标的方向(距离)向量,这两个向量的点积才是我们用来插值的随机值,说的有些抽象,我们可以看看下面的示意图(蓝色向量为梯度向量,红色向量为距离向量):

    perlin_7

    除此之外, 二维 Perlin 噪声的生成过程就和 二维 Value 噪声的生成过程就没有什么不同了( a r , b r , c r , d r ar, br, cr, dr ar,br,cr,dr 为四个端点对应的梯度向量, a v , b v , c v , d v av, bv, cv, dv av,bv,cv,dv 为四个端点与给定坐标形成的距离向量, u u u x x x 轴原始的线性插值系数, v v v y y y 轴原始的线性插值系数(实际上,通过 u u u, v v v 我们就能得到 a v , b v , c v , d v av, bv, cv, dv av,bv,cv,dv) ):

    u ′ = 6 u 5 − 15 u 4 + 10 u 3 v ′ = 6 v 5 − 15 v 4 + 10 v 3 a = d o t ( a r , a v ) b = d o t ( b r , b v ) c = d o t ( c r , c v ) d = d o t ( d r , d v ) e = ( 1 − u ′ ) a + u ′ b f = ( 1 − u ′ ) c + u ′ d r = ( 1 − v ′ ) e + v ′ f u' = 6u^5 - 15u^4 + 10u^3 \\ v' = 6v^5 - 15v^4 + 10v^3 \\ a = dot(ar, av) \\ b = dot(br, bv) \\ c = dot(cr, cv) \\ d = dot(dr, dv) \\ e = (1 - u')a + u'b \\ f = (1 - u')c + u'd \\ r = (1 - v')e + v'f u=6u515u4+10u3v=6v515v4+10v3a=dot(ar,av)b=dot(br,bv)c=dot(cr,cv)d=dot(dr,dv)e=(1u)a+ubf=(1u)c+udr=(1v)e+vf

    下面是二维 Perlin 噪声的示意图(其中各个坐标点的明暗程度代表了相应的随机值):

    perlin_8

    (注:上图展示的是实际生成的二维 Perlin 噪声数据,显示上没有做额外的插值处理,所以看起来会有明显的边界)

    Simplex 噪声

    Simplex 噪声是 Perlin 噪声的改进版,(二维)Perlin 噪声通过选取对应的正方形(方形)来获取插值端点,(二维)Simplex 噪声则是选取对应的三角形(单形)来获取插值端点.这样做的好处是单形的顶点数是随着维度线性增长的,而方形的顶点数是随着维度指数增长的,基于此,Simplex 噪声的计算复杂度要比 Perlin 函数低不少,但另一方面,在 Perlin 噪声中,从给定坐标获取对应的方形非常简单,只需要对坐标取底(floor)即可,但在 Simplex 噪声中,从给定坐标获取对应的单形则比较复杂.

    perlin_9
    (二维单形示例)

    Simplex 噪声中随机值的生成也和 Perlin 噪声有所不同,有兴趣进一步了解的朋友可以从这里看起~

    多维 Perlin 噪声

    从 二维 Perlin 噪声扩展到 多维 Perlin 噪声还是比较简单的,譬如 三维 Perlin 噪声,使用的是立方体(三维中的方形)的 8 个顶点作为插值端点,更高维度的话,则是使用 超立方体 的各个端点作为插值端点,端点个数与维度( D D D)呈指数关系( 2 D 2^D 2D)

    一维 Perlin 噪声

    说了多维 Perlin 噪声,那 一维 Perlin 噪声如何生成呢(毕竟一维坐标下并没有向量的概念)? 实际上,我们还是可以在一维坐标上"定义"二维向量,只需要将该二维向量的 y y y 轴数值设置为 0 0 0 即可,同样的,我们也可以依此计算出距离向量, 这样我们就可以沿用 二维 Perlin 噪声 的生成方法来生成 一维 Perlin 噪声了.

    这里有一份相关的代码实现,有兴趣的朋友可以看看~

    分形噪声

    很多讲解 Perlin 噪声的文章也会提到 分形噪声,不过分形噪声本质上并不是某种特定类型的噪声(自然也不是 Perlin 噪声),而更应该说是一种噪声的叠加方法,他是将很多个不同频率,不同振幅的基础噪声(譬如 Value噪声, Perlin噪声 等等)相互叠加,最后形成的一种噪声(统称为分形噪声).

    更多细节可以从这里开始了解~

    更多资料

    展开全文
  • 描述使用numpy的快速简单的Perlin噪声生成器。安装您可以通过以下方式安装此软件包: pip3 install git+https://github.com/pvigier/perlin-numpy用法 from perlin_numpy import ( generate_fractal_noise_2d , ...
  • 简单聊聊 Perlin 噪声(上篇)

    千次阅读 2020-08-27 10:38:05
    程序开发中总会用到随机方法,一般的随机方法虽然通用,但是产生的随机数又因为过于"随机",不适合用来生成平滑连续的随机数据(譬如自然地形的高度),这个时候我们便需要使用特殊的随机方法了, Perlin 噪声便是一种能够...

    程序开发中总会用到随机方法,一般的随机方法虽然通用,但是产生的随机数又因为过于"随机",不适合用来生成平滑连续的随机数据(譬如自然地形的高度),这个时候我们便需要使用特殊的随机方法了, Perlin 噪声便是一种能够产生平滑(随机)数值的随机方法.

    Value 噪声

    为了更容易的理解 Perlin 噪声,我们先从较简单的 Value 噪声看起:

    首先我们考虑 一维 情况(即通过一维坐标来获取随机值),如果我们仅使用一般随机方法的话,得到的随机数值是这样的:

    perlin_1_2

    可以看到数据杂乱无章,远不能说是平滑连续,有什么办法可以改进呢?

    一种朴素的想法就是在整数坐标处仍然使用一般随机方法来生成随机值,但是对于处在(相邻)两个整数坐标之间的点(即浮点坐标点),则使用线性插值的方式(在这两个整数坐标点对应的随机值之间线性插值)来生成随机值,最后得到的随机数值是这样的:

    perlin_2

    可以看到数据比起之前已经平滑连续了不少,但是在整数坐标处仍然不够平滑连续(整数坐标处不可导(自然也不连续)),而这是由于我们之前采用了线性插值的方式来生成随机值造成的,改善的方式也比较明晰,就是改用非线性插值的方式来生成整数坐标间的随机值.

    当然,也不是随便一种非线性插值都可以满足我们的要求(我们需要该非线性插值在(相邻)端点(整数坐标)处可导并且导数相同(连续)),使用下面的非线性插值方法可以达到我们的目标(公式中的 t t t 是原始的线性插值系数, a a a b b b 则是两个端点处的随机值( a a a 对应左端点, b b b 对应右端点), r r r 则是最终的噪声值):

    t ′ = 3 t 2 − 2 t 3 r = ( 1 − t ′ ) a + t ′ b t' = 3t^2 - 2t^3 \\ r = (1 - t')a + t'b t=3t22t3r=(1t)a+tb

    使用上述的方法,我们得到的随机数值是这样的:

    perlin_3

    上述的非线性插值公式还可以进一步改进,基本思想就是使(相邻)端点(整数坐标)处更加"平滑连续"(即在(相邻)端点(整数坐标)处二阶导数相同(连续)):

    t ′ = 6 t 5 − 15 t 4 + 10 t 3 r = ( 1 − t ′ ) a + t ′ b t' = 6t^5 - 15t^4 + 10t^3 \\ r = (1 - t')a + t'b t=6t515t4+10t3r=(1t)a+tb

    基于改进的公式,我们得到的随机数值是这样的:

    perlin_4

    至此,我们便得到了 一维 的 Value 噪声.

    那么 二维(即通过二维坐标来获取随机值) Value 噪声又如何生成呢 ?

    其实思路上 二维 Value 噪声 和一维 Value 噪声也是一致的,仍然是首先在端点坐标处生成随机值,之后基于给定的坐标进行非线性插值.

    当然,由于坐标是二维的关系,端点已经不是一维情况下坐标轴上的整数坐标了,而是变成了正方形的四个顶点(我们使用正方形平铺二维平面,这样任意一个二维坐标都可对应到单个正方形,而这个正方形的四个顶点即是我们用来非线性插值的端点)

    perlin_5

    当然,由于二维坐标的关系,插值的时候我们需要先在 x x x 轴上分别做两次插值,再在 y y y 轴上做一次插值( a , b , c , d a, b, c, d a,b,c,d 为四个端点的随机值, u u u x x x 轴原始的线性插值系数, v v v y y y 轴原始的线性插值系数):

    u ′ = 6 u 5 − 15 u 4 + 10 u 3 v ′ = 6 v 5 − 15 v 4 + 10 v 3 e = ( 1 − u ′ ) a + u ′ b f = ( 1 − u ′ ) c + u ′ d r = ( 1 − v ′ ) e + v ′ f u' = 6u^5 - 15u^4 + 10u^3 \\ v' = 6v^5 - 15v^4 + 10v^3 \\ e = (1 - u')a + u'b \\ f = (1 - u')c + u'd \\ r = (1 - v')e + v'f u=6u515u4+10u3v=6v515v4+10v3e=(1u)a+ubf=(1u)c+udr=(1v)e+vf

    下面是二维 Value 噪声的示意图(其中各个坐标点的明暗程度代表了相应的随机值):

    perlin_6

    (注:上图展示的是实际生成的二维 Value 噪声数据,显示上没有做额外的插值处理,所以看起来会有明显的边界)

    至此,我们也明白了 二维 Value 噪声的生成原理.

    未完待续

    展开全文
  • 当前,它以1D,2D和3D版本实现Perlin噪声功能(噪声也实现了单纯噪声)。为什么? 之所以启动vnoise,是因为不再支持原始的噪声库,并且无法使用Python的最新版本的二进制文件,这使得非技术用户难以安装。 因为...
  • perlin噪声算法是比较有名的一种噪声算法,现在很多噪声算法几乎都是对原始perlin噪声进行改进的结果,而perlin噪声在游戏开发中也有许多用处,比如地形的生成,纹理材质的生成等等… perlin噪声的算法过程如下,...

    Perlin噪声算法

    perlin噪声算法是比较有名的一种噪声算法,现在很多噪声算法几乎都是对原始perlin噪声进行改进的结果,而perlin噪声在游戏开发中也有许多用处,比如地形的生成,纹理材质的生成等等…

    当然。。unity内置了perlin噪声的实现,即mathf.perlinnoise函数。这个函数是一个c++写的本地函数。但是不能知其然不知其所以然,本文就讲解了perlin噪声具体的实现过程。

    perlin噪声的算法过程如下,我们以2D perlin噪声为例。
    首先需要创建一个2D’网格’,‘网格’由’格子’构成,这里的’格子’并不是对应单个像素的,而是一个’格子’里包含有许多个像素。
    (如图所示的一个格子,一个格子由9个像素组成,而’网格’由无数个这种’格子’组成,一个格子由多少像素组成对最后的效果有很大影响)
    在这里插入图片描述
    对每一个像素,我们需要计算它的灰度值,计算过程如下。
    1.对每个’格子’的四个顶点,分别关联一个随机的单位向量(也可以不预先计算而是在用到时用一个函数得到,但是这个函数必须对每个种子返回确定的’随机值’)
    2.判断该像素属于哪一个’格子’
    3.计算该像素到四个顶点的偏移向量,不用单位化
    4.对每个顶点,依次用到该顶点的偏移向量点积之前关联在该顶点上的随机向量,对每个顶点得到一个值
    5.进行正方形插值,获得该像素的灰度值

    如果用数学的语言描述,其实就是在格子的四个顶点关联一个随机的梯度,对于每个格子里的像素,计算该像素到四个顶点的向量,然后使用这个向量点积对应顶点的关联梯度(学过高数的朋友应该听出来了,其实就是算四个顶点上的方向导数)然后进行正方形插值
    听起来可能有些莫名其妙的,看一下代码可能就清楚了,代码还是比较简短的。

    代码实现

    我们要写的函数形式是这样的,即给定一个浮点坐标值,返回一个灰度值。
    当然像素坐标是没有浮点值的,这里需要用到我们用到的’格子’概念,如果一个格子包含四个像素,在传入像素坐标值的时候就要除以4。

    public static float perlin(float x, float y)
        {
        }
    

    接下来声明5个点,分明是坐标点,以及该坐标所属的格子的四个顶点。具体的计算通过向下取整来进行。

    		//声明二维坐标
            Vector2 pos = new Vector2(x, y);
            //声明该点所处的'格子'的四个顶点坐标
            Vector2 rightUp = new Vector2((int) x + 1, (int) y + 1);
            Vector2 rightDown = new Vector2((int) x + 1, (int) y);
            Vector2 leftUp = new Vector2((int) x, (int) y + 1);
            Vector2 leftDown = new Vector2((int) x, (int) y);
    

    接下来就是计算格子顶点对应的值的函数,我们这里传入两个坐标值。
    首先用坐标值对应一个随机的单位向量。
    接着向量相减计算出偏移向量。
    最后进行点积,点积的值[-1,1]还需要映射到灰度值取值[0,1]中

        static float dotGridGradient(Vector2 p1, Vector2 p2)
        {
            Vector2 gradient = randomVector2(p1);
            Vector2 offset = p2 - p1;
            return Vector2.Dot(gradient, offset) / 2 + 0.5f;
        }
    

    我们这里每个顶点对应的随机向量不是预先计算的,如果预先计算的话也很方便,用random类就可以了,生成之后记得保存起来。
    但是直接用一种映射的方式生成随机向量的话,就可以省掉这个存储空间了。
    我们使用的传入一个坐标返回一个随机单位向量的函数如下。
    看不懂这个函数的话很正常。。因为并不能证明这个函数确实具有统计分布上的随机性,这里的系数也是随便取值的。
    但是这个函数确实可以作为简易的随机函数来使用,而且这种基于sin函数的简易随机数函数在很多地方都有使用,只要想象一下这个函数的图像就明白了。
    而且可以保证对相同的参数返回值是相同的。

        static Vector2 randomVector2(Vector2 p)
        {
            float random = Mathf.Sin(666+p.x*5678 + p.y*1234 )*4321;
            return new Vector2(Mathf.Sin(random), Mathf.Cos(random));
        }
    

    对每个像素计算出四个值之后就可以进行正方形插值了,正方形插值就是线性插值的二维版本。首先对上面两个顶点进行插值,接着对下面两个顶点进行插值。最后对这两个值再进行插值。

      //计算x上的插值
            float v1 = dotGridGradient(leftDown, pos);
            float v2 = dotGridGradient(rightDown, pos);
            float interpolation1 = interpolate(v1, v2, x - (int) x);
    
            //计算y上的插值
            float v3 = dotGridGradient(leftUp, pos);
            float v4 = dotGridGradient(rightUp, pos);
            float interpolation2 = interpolate(v3, v4, x - (int) x);
    
            float value = interpolate(interpolation1, interpolation2, y - (int) y);
            return value;
    

    线性插值函数

      static float interpolate(float a0, float a1, float w)
        {
            //线性插值
            return (a1 - a0) * w + a0;
        }
    

    这样perlin噪声的代码就写完了。
    接着我们再创建一个c#脚本基于这个函数来生成一张纹理。
    创建一个Texture2D然后在一个二重循环里用SetPixel函数来设置对应像素点的颜色,因为我们要创建一个灰度图,所以rgb分量全都一样。
    需要注意我们传入参数的时候对坐标值除以了64,这就代表一个’格子’里有64个像素。
    最后还写了一个函数把创建好的纹理保存成图片。
    把这个类挂载在一个物体上就可以生成一张perlin噪声图并贴在物体上,同时保存该图在Assets文件夹下。

     public class CreatePerlinNoiseTex : MonoBehaviour
        {
            void Start()
            {
                Texture2D texture = new Texture2D(1024, 1024);
    
                this.GetComponent<Renderer>().material.mainTexture = texture;
    
                for (int y = 0; y < texture.height; y++)
                {
                    for (int x = 0; x < texture.width; x++)
                    {
                        float grayscale = PerlinNoise.perlin(x / 64f, y / 64f);
                        texture.SetPixel(x, y, new Color(grayscale,grayscale,grayscale));
                    }
                }
    
                texture.Apply();
                saveTexture2D(texture, "tex");
            }
    
    
            void saveTexture2D(Texture2D texture, string fileName)
            {
                var bytes = texture.EncodeToPNG();
                var file = File.Create(Application.dataPath + "/" + fileName + ".png");
                var binary = new BinaryWriter(file);
                binary.Write(bytes);
                file.Close();
                UnityEditor.AssetDatabase.Refresh();
            }
        }
    

    效果演示

    效果如下
    在这里插入图片描述

    生成的图片如下。
    可以看到,仔细看的话隐隐约约能注意到格子的边界。这主要是因为线性插值导致的,在格子的边界处亮度的变化率会产生突变,导致了这种效果。
    在这里插入图片描述
    我们这里更改线性插值代码为hermite曲线插值,hermite曲线插值保证在线段端点的导数为0,也就是保证了一阶参数连续性,因此在边界处的过渡会更平滑。(注:我们这里不手动实现hermite曲线插值,而是使用SmoothStep函数,该函数在内部就是使用了hermite曲线插值)
    如果想知道hermite曲线插值的实现细节可以看我这篇博客
    hermite曲线的绘制

        static float interpolate(float a0, float a1, float w)
        {
            //线性插值
            //return (a1 - a0) * w + a0;
            
            //hermite插值
            return Mathf.SmoothStep(a0, a1, w);
        }
    

    更改插值方法后输出图片如下。几乎意识不到格子的存在了。
    在这里插入图片描述

    (注意避免一个格子里只有一个像素和所有像素都在一个格子里的情况,这种情况下几乎看不出效果,另外一个格子里含有的像素大小最好是2的幂次,否则在边缘处的插值会有一些问题,计算上也会变慢)

    一个格子16个像素
    在这里插入图片描述
    一个格子4个像素
    在这里插入图片描述

    地形生成

    我们这里稍微演示一下噪声图的作用,用噪声图来生成一个简单的地形。一般来说地形生成需要直接用Mesh类来生成顶点,但是为了简单起见我们就用许多cube来合成一个地形,这种地形也是类似Minecraft里的地形生成。
    再写一个简单的类来调用perlin噪声函数就可以了,只是我们这里需要把perlin噪声的返回值映射到cube的坐标。

    using Algorithm;
    using UnityEngine;
    
    public class CreateTerrain : MonoBehaviour
    {
        
        void Start()
        {
            for (int z = 0; z < 128; z++)
            {
                for (int x = 0; x < 128; x++)
                {
                    float grayscale = PerlinNoise.noise(x / (float) 16, z / (float) 16);
                    var cube=GameObject.CreatePrimitive(PrimitiveType.Cube);
                    int cubeY = (int) (grayscale * 20);
                    cube.transform.position = new Vector3(x, cubeY, z);
                    cube.GetComponent<Renderer>().material.color=Color.green;
                }
            }
        }
    }
    

    把这个脚本挂载到任意场景中的物体上,就可以得到如下效果。是不是能让人想到minecraft里的地形呢~
    其实minecraft也是使用这种地形生成的算法,当然肯定比这里复杂的多,当然核心还是用到的是各种噪声算法。
    在这里插入图片描述

    完整代码

    using UnityEngine;
    
    public static class PerlinNoise
    {
        static float interpolate(float a0, float a1, float w)
        {
            //线性插值
            //return (a1 - a0) * w + a0;
            
            //hermite插值
            return Mathf.SmoothStep(a0, a1, w);
        }
    
    
        static Vector2 randomVector2(Vector2 p)
        {
            float random = Mathf.Sin(666+p.x*5678 + p.y*1234 )*4321;
            return new Vector2(Mathf.Sin(random), Mathf.Cos(random));
        }
    
    
        static float dotGridGradient(Vector2 p1, Vector2 p2)
        {
            Vector2 gradient = randomVector2(p1);
            Vector2 offset = p2 - p1;
            return Vector2.Dot(gradient, offset) / 2 + 0.5f;
        }
    
    
        public static float perlin(float x, float y)
        {
            //声明二维坐标
            Vector2 pos = new Vector2(x, y);
            //声明该点所处的'格子'的四个顶点坐标
            Vector2 rightUp = new Vector2((int) x + 1, (int) y + 1);
            Vector2 rightDown = new Vector2((int) x + 1, (int) y);
            Vector2 leftUp = new Vector2((int) x, (int) y + 1);
            Vector2 leftDown = new Vector2((int) x, (int) y);
    
            //计算x上的插值
            float v1 = dotGridGradient(leftDown, pos);
            float v2 = dotGridGradient(rightDown, pos);
            float interpolation1 = interpolate(v1, v2, x - (int) x);
    
            //计算y上的插值
            float v3 = dotGridGradient(leftUp, pos);
            float v4 = dotGridGradient(rightUp, pos);
            float interpolation2 = interpolate(v3, v4, x - (int) x);
    
            float value = interpolate(interpolation1, interpolation2, y - (int) y);
            return value;
        }
    }
    

    生成图片的类

    using System.IO;
    using UnityEngine;
    
    namespace _01PerlinNoise
    {
        public class CreatePerlinNoiseTex : MonoBehaviour
        {
            void Start()
            {
                Texture2D texture = new Texture2D(1024, 1024);
    
                this.GetComponent<Renderer>().material.mainTexture = texture;
    
                for (int y = 0; y < texture.height; y++)
                {
                    for (int x = 0; x < texture.width; x++)
                    {
                        float grayscale = PerlinNoise.perlin(x /16f, y / 16f);
                        texture.SetPixel(x, y, new Color(grayscale,grayscale,grayscale));
                    }
                }
    
                texture.Apply();
                saveTexture2D(texture, "tex");
            }
    
    
            void saveTexture2D(Texture2D texture, string fileName)
            {
                var bytes = texture.EncodeToPNG();
                var file = File.Create(Application.dataPath + "/" + fileName + ".png");
                var binary = new BinaryWriter(file);
                binary.Write(bytes);
                file.Close();
                UnityEditor.AssetDatabase.Refresh();
            }
        }
    }
    

    另外代码也传到github仓库里了,大家也可以关注一下哦~
    我的github

    展开全文
  • Perlin 噪声生成器的 Javascript 版本 示例用法 // image width & height var width = 200 ; var height = 100 ; // read parameters var density = document . getElementById ( "density" ) . value ; ; var red...
  • perlin噪声

    2018-10-21 22:10:00
    手贱去点了图形学里面的噪声课程,然后一个周末就交代在这上面了,还是有些云里雾里。...下面就是perlin噪声生成个灰度图。  没啥意思是吧,那么看下面这个: 现在说说最有名的噪声算法:perli...
  • 实时体积云渲染(地平线):二.Perlin噪声和Worley噪声
  • 针对Vega Prime自带海洋模块中海浪随机性不强和海面网格划分简单的不足,以OpenGL为基础将基于Perlin噪声的海面仿真应用到Vega Prime中。Perlin噪声的生成采用计算机运算最快的位运算方法,减小海面高度场的计算时间...
  • Perlin噪声变形

    2017-09-24 11:11:33
    基于Unity2017.1.1f1_Cg实现的Perlin噪声变形,实现了脉流及方向脉流。可作为学习研究之用。资源描述要长。
  • 佩林噪音 简单的一维Perlin噪声发生器 安装 npm install @mohayonao/perlin-noise 原料药 佩林噪声 constructor(rand: function = Math.random) 实例方法 noise(x: number): number 执照 麻省理工学院
  • 它还可以生成Perlin噪声,脊形多重分形噪声和其他类型的相干噪声。先决条件Java 8从源头建造为了构建数学,您只需要运行./gradlew build命令。 您可以在./build/libs找到已编译的JAR文件, ./build/libs标签类似于...
  • Unity自带的一个API叫Mathf.PerlinNoise,使用它能很方便的制作出噪声图。使用方法为,在Tools中找到该脚本对应按钮,点击即可进行噪声图制作
  • Unity自带的一个API叫Mathf.PerlinNoise,使用它能很方便的制作出噪声图。使用方法为,在Tools中找到该脚本对应按钮,点击即可进行噪声图制作
  • SoftNoise-GDScript- GDScript函数集会产生噪声(值噪声,Perlin噪声,opensimplex(2d,3d和4d)...)。使用方法示例: other_script.gd extends Nodevar preScript = preload("res://scripts/softnoise.gd")var ...
  • 程序图生成 托管在 尝试通过各种算法以程序方式生成地形。 其中包含一些实际的地图生成器,以及未完成和失败的算法... map-gen-perlin-具有可调整参数的基于Perlin噪声的工作岛生成器 hackathon-生成一组程序岛的位图
  • 深度卷积神经网络对Perlin噪声的敏感性研究.pdf
  • 世界种子 一款关于发现、征服、创造和分享新世界的游戏。
  • mapgen-viewer:基于Voronoi图和Perlin噪声的地图生成器
  • 佩林菲尔德 创建对象网格,并通过Perlin噪声上下移动它们。
  • Perlin噪声1D&2D代码

    2017-08-15 21:05:11
    自己写的Perlin噪声1D&2D代码,采用VS2015+C#。
  • 色情 用于Java的可移植,开放源代码,相干噪声生成库可以生成Perlin噪声,脊形多重分形噪声以及其他类型的相干噪声。
  • Ue4MarchingCubes:“针对虚幻引擎4使用Marching cubes算法的3D Perlin噪声
  • Perlin噪声1D&2D&3D&D代码

    2017-09-13 20:46:43
    完全自己写的Perlin噪声1D&2D&3D&D代码,按照原理未作任何优化。只作原理学习演示之用。开发平台为VS2015 + c#

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 978
精华内容 391
关键字:

Perlin噪声