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
- Worley Noise(二)
- Worley Noise(一)
- Worley Noise (三)
- 图形学——Cell/Worley Noise
- Perlin noise(二)
- Value Noise(二)
- Simplex Noise(二)
- Perlin noise(一)
- Perlin noise(三)
- Simplex Noise(一)
- Simplex Noise (三)
- Value Noise(一)
- noise
- noise
- (POJ1953)World Cup Noise
- 白噪声(White noise)
- (POJ1953)World Cup Noise(dp)
- poj - 1953 - World Cup Noise(dp)
- Mongodb--Python使用mongodb
- 黑魔法师之门(codevs1995)
- Linux实验2
- Git Bash的初学经验以及基本指令
- java常用类库---对象克隆技术
- Worley Noise(二)
- 我的C语言矩阵库_2
- cookie和sessionStorage,localStorage区别
- Win10 64位+VS2015+Opencv3.3.0安装配置
- 浅谈Redis
- 欧拉-伯努利梁横向振动2
- 为什么更喜欢和朋友一起玩游戏
- 欧拉定理与函数
- 课表