好久不发THREE.js的教程了,今天给大家带了一个小案例,介绍如何用柏林噪声在THREE.js中生成随机地形,先来看下效果。

ani

随机数和噪声

噪声实际上就是随机数生成器,普通噪声生成的随机数毫无规律可言。

在上世纪80年代,柏林噪声的发明者Ken Perlin尝试用普通噪声模拟随机效果,用于正在制作的经典迪士尼电影电子世界争霸战(TRON),但一直无法得到满意的效果,柏林噪声算法就这样应运而生。

2010年TRON重拍版本

柏林噪声

自然界中层叠的山峦,大理石的纹理,海面高低起伏的波浪,这些现象看似杂乱无章,却都有内在规律可循,普通的噪声无法模拟出这些自然效果,柏林噪声算法使这些成为可能。

用柏林噪声算法可以得到一组平滑插值的随机数,它们之间相互关联,可以用来生成接近自然的随机值。下图中,左边是普通噪声生成的随机纹理,右边是柏林噪声生成的纹理,可以看到后者生成的纹理更加自然平滑,随机值之间有明显过渡效果。

t1

我用canvas绘制了两组图像,分别是用两种算法生成一些随机点,然后将这些点连接起来,可以看到右侧柏林噪声得到的是一条相对平滑的曲线,而左边普通噪声的图形毫无规律可言。

普通噪声和柏林噪声随机点连线

生成随机地形

在THREE.js我们用一个平面来生成随机地形,平面是由一系列顶点构成的,我们只需要改变这些顶点的z轴坐标值,就能产生高低起伏的效果,我们先用随机噪声来试试。

let pos = this.groundGeo.attributes.position.array
for(let i = 0; i < pos.length; i+=3) {
  pos[i+2] = Math.random() * 2
}
this.groundGeo.attributes.position.needsUpdate = true
随机噪声产生的地形

可以看到随机噪声产生的地形太过于生硬,肯定不是我们想要的结果,再来试试柏林噪声。

let pos = this.groundGeo.attributes.position.array
for(let i = 0; i < pos.length; i+=3) {
  pos[i+2] = this.simplex.noise2D(tx, ty) * 2
}
this.groundGeo.attributes.position.needsUpdate = true


我们得到了更加自然平滑的地形效果,这里我们使用的Simplex Noise是柏林噪声的更高效,简介的实现,也是柏林噪声的作者提出的,这里我们不关心这些算法的实现细节。

柏林噪声产生的地形效果

移动地形

要实现地形向观察者移动,我们只需要改变柏林噪声的x坐标,让它随着时间递增。

let pos = this.groundGeo.attributes.position.array
this.offsetX += 0.01
for(let i = 0; i < pos.length; i+=3) {
  let tx = this.offsetX
  let ty = this.offsetY
  tx += row * 0.05
  ty += col * 0.05
  pos[i+2] = this.simplex.noise2D(tx, ty) * 5
}
this.groundGeo.attributes.position.needsUpdate = true

这里使用一个offsetX变量,它的值在每帧循环中递增。注意,我只贴了关键代码,具体细节可以点击阅读原文来获得本案例的源文件。

通过调整各项参数,我们还可以模拟出更多效果,例如:棱角分明的山峰,高低起伏的海浪,或者外形更加平滑的沙丘等等。

总结

Perlin Noise柏林噪声在游戏和图形领域被广泛的应用,利用它可以模拟出许多有趣的视觉效果,你学到了嘛!

关注