Worley Noise(二)

来源:互联网 发布:阿里云域名实名认证 编辑:程序博客网 时间:2024/06/05 12:40

  根据前文所述,Worley噪声代码实现比较简单,我们着重讲解一下2D噪声的算法,3D、4D实现基本一致,不作详细描述。

一、Worley Noise 2D实现

  还是与以前一样,我们分步来实现Worley噪声,根据前文步骤,Worley噪声实现共分六步:
  第一步、确定输入点所在的晶胞
这与Perlin噪声确定输入点所在晶格是一样的,代码如下:

            int evalCubeX = GetFloor(x);            int evalCubeY = GetFloor(y);

  第二步、对该晶胞产生可重复的随机数生成器
在我们的算法里,我们使用了FNV Hash算法,这样可以保证种子的唯一性,然后我们使用之前学习的线性同余算法(LCG random number generator)作为我们随机数生成器。在使用线性同余算法前使用的Hash共两个作用,第一是生成LCG算法的输入值,第二是为下步确定日胞内特征点数量作准备。代码如下:

             lastRandom = lcgRandom(hash((uint)(cubeX + seed), (uint)(cubeY)));             numberFeaturePoints = probLookup(lastRandom);

我们使用用晶胞左下角顶点坐标与一上种子作为Hash函数的输入,得到唯一确定的随机值,然后用这个值作为LCG的输入,保证同一个输入点得到同样的输出,然后我们还随机生成了[1,9]个特征点数量。
  第三步、确定在该晶胞内生成特征点的数量

         numberFeaturePoints = probLookup(lastRandom);

  第四步、随机的将3所生成的特征点放置到晶胞中
对每一个特征点,我们求出特征点的坐标值,某个特征点的坐标值就是(featurePointX,featurePointY),代码如下:

               lastRandom = lcgRandom(lastRandom);               randomDiffX = (float)lastRandom / 0x100000000;               lastRandom = lcgRandom(lastRandom);               randomDiffY = (float)lastRandom / 0x100000000;               featurePointX = randomDiffX + (double)cubeX;               featurePointY = randomDiffY + (double)cubeY;

  第五步、计算输入点到该晶胞内所有特征点的距离最小值
我们并没有保存特征点的坐标,而是直接求出特征点与输入点的距离,并将这个值与我们保存的最小值比较,如果小于之前的最小值,则更新最小值。

               tempDistance = EuclidianDistance(x, y, featurePointX, featurePointY);               if (tempDistance < closestDistance)                     closestDistance = tempDistance;

  第六步、查找并计算输入点到所在晶胞直接相邻晶胞内特征点的距离最小值,并与5中生成的最小值比较,返回最小值
在计算完同一晶胞内的所有特征点距离后,我们还要与输入点直接相邻的晶胞进行比较,这就是嵌套的两个for语句实现的。

 for (int i = -1; i < 2; ++i)     for (int j = -1; j < 2; ++j)     {......}

至此,我们就完成了Worley噪声的计算。这是C#实现的方案,在Cg中,我们更是直接一步利用hash函数得到输入点与特征点的距离差值,代码更简洁。生成的Worley噪声如下图所示:

这里写图片描述

上图的单形噪声
这里写图片描述

上图分形噪声

二、Worley NOise 3D实现

   3D Worley 噪声实现与2D基本一致,不同之处在于:一是3D生成的特征点有三个坐标分量面2D是二个,另一个3D生成需要检查周边的晶胞比2D多,因此for循环多了一个。代码如下:

            uint lastRandom, numberFeaturePoints;            Vector3 randomDiff = new Vector3();            Vector3 featurePoint;            int cubeX, cubeY,cubeZ;            double closestDistance = 1000000.0, tempDistance = 0.0;            int evalCubeX = GetFloor(x);            int evalCubeY = GetFloor(y);            int evalCubeZ = GetFloor(z);            for (int i = -1; i < 2; ++i)                for (int j = -1; j < 2; ++j)                {                    for (int k = -1; k < 2; ++k)                    {                        cubeX = evalCubeX + i;                        cubeY = evalCubeY + j;                        cubeZ = evalCubeZ + k;                        lastRandom = lcgRandom(hash((uint)(cubeX + seed), (uint)(cubeY),(uint)(cubeZ)));                        numberFeaturePoints = probLookup(lastRandom);                        for (uint l = 0; l < numberFeaturePoints; ++l)                        {                            lastRandom = lcgRandom(lastRandom);                            randomDiff.x = (double)lastRandom / 0x100000000;                            lastRandom = lcgRandom(lastRandom);                            randomDiff.y = (double)lastRandom / 0x100000000;                            lastRandom = lcgRandom(lastRandom);                            randomDiff.z = (double)lastRandom / 0x100000000;                            featurePoint = new Vector3(randomDiff.x + (double)cubeX, randomDiff.y + (double)cubeY,randomDiff.z + (double)cubeZ) ;                            tempDistance = EuclidianDistance(new Vector3(x,y,z), featurePoint);                            if (tempDistance < closestDistance)                                closestDistance = tempDistance;                        }                    }                }            return closestDistance;

下图是我们将第3维作为时间输入产生的2D平面动画:

这里写图片描述

基于CPU产生的动画
这里写图片描述

基于GPU产生的动画

三、Worley Noise 4D实现

   4D Worley 噪声实现与3D基本一致,不再赘述。代码如下:

       uint lastRandom, numberFeaturePoints;            Vector4 randomDiff = new Vector4();            Vector4 featurePoint;            int cubeX, cubeY, cubeZ,cubeW;            double closestDistance = 1000000.0, tempDistance = 0.0;            int evalCubeX = GetFloor(x);            int evalCubeY = GetFloor(y);            int evalCubeZ = GetFloor(z);            int evalCubeW = GetFloor(w);            for (int i = -1; i < 2; ++i)                for (int j = -1; j < 2; ++j)                {                    for (int k = -1; k < 2; ++k)                    {                        for (int n = -1; n < 2; ++n)                        {                            cubeX = evalCubeX + i;                            cubeY = evalCubeY + j;                            cubeZ = evalCubeZ + k;                            cubeW = evalCubeW + n;                            lastRandom = lcgRandom(hash((uint)(cubeX + seed), (uint)(cubeY), (uint)(cubeZ), (uint)(cubeW)));                            numberFeaturePoints = probLookup(lastRandom);                            for (uint l = 0; l < numberFeaturePoints; ++l)                            {                                lastRandom = lcgRandom(lastRandom);                                randomDiff.x = (double)lastRandom / 0x100000000;                                lastRandom = lcgRandom(lastRandom);                                randomDiff.y = (double)lastRandom / 0x100000000;                                lastRandom = lcgRandom(lastRandom);                                randomDiff.z = (double)lastRandom / 0x100000000;                                lastRandom = lcgRandom(lastRandom);                                randomDiff.w = (double)lastRandom / 0x100000000;                                featurePoint = new Vector4(randomDiff.x + (double)cubeX, randomDiff.y + (double)cubeY, randomDiff.z + (double)cubeZ,randomDiff.w + (double)cubeW);                                tempDistance = EuclidianDistance(new Vector4(x, y, z,w), featurePoint);                                if (tempDistance < closestDistance)                                    closestDistance = tempDistance;                            }                        }                    }                }            return closestDistance;

下图是GPU上的4D实现,将第4维作为时间输入产生的3D动画(在cpu上计算时间成本太高,没有做动画)。

这里写图片描述

四、CPU与GPU实现时的差异

  Worley噪声在CPU与GPU上实现时我们采用了不同的方法,CPU实现我们完全按照Worley论文使用的方法设计算法,在GPU上,出于以下两个方面原因,我们变更了原算法的实现:一是GPU不支持变量循环,在第三步中,worley原设计是产生[1,9]个随机特征点,在GPU中我们限定只产生1个特征点,二是在GPU中我们使用了更简单的hash函数,并直接得到输入点与特征点的距离值。虽然实现方式有些许不同,但实现的效果基本一致。

五、代码下载

VS2015平台下C#实现的WorleyNoise:
VS2015_C#_WorleyNoise

Unity2017.1.1f1平台下Cg实现的WorleyNoise
Unity2017.1.1f1_Cg_WorleyNoise

参考文献

1、An In Depth Cell Noise Tutorial
2、Cell (Worley) noise in Unity3D

原创粉丝点击