Worley Noise(二)

16 篇文章 4 订阅
16 篇文章 9 订阅

  根据前文所述,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

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_DavidWang_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值