为什么这个一维柏林噪声发生器 Return 值 > 1?
Why does this 1-dimensional Perlin Noise Generator Return Values > 1?
出于教育目的,我想在 Kotlin 中实现一维 Perlin Noise
算法。我熟悉了算法 here and here.
我想我理解了基本概念,但是 我的实现可以 return 大于 1 的值。我希望调用 perlin(x)
的结果在 0 到 1 的范围内。我不知道我错在哪里,所以也许有人可以指出我正确的方向。为简单起见,我现在使用简单的线性插值代替 smoothstep 或其他高级技术。
class PerlinNoiseGenerator(seed: Int, private val boundary: Int = 10) {
private var random = Random(seed)
private val noise = DoubleArray(boundary) {
random.nextDouble()
}
fun perlin(x: Double, persistence: Double = 0.5, numberOfOctaves: Int = 8): Double {
var total = 0.0
for (i in 0 until numberOfOctaves) {
val amplitude = persistence.pow(i) // height of the crests
val frequency = 2.0.pow(i) // number of crests per unit distance
val octave = amplitude * noise(x * frequency)
total += octave
}
return total
}
private fun noise(t: Double): Double {
val x = t.toInt()
val x0 = x % boundary
val x1 = if (x0 == boundary - 1) 0 else x0 + 1
val between = t - x
val y0 = noise[x0]
val y1 = noise[x1]
return lerp(y0, y1, between)
}
private fun lerp(a: Double, b: Double, alpha: Double): Double {
return a + alpha * (b - a)
}
}
例如,如果您要使用这些随机生成的噪音
private val noise = doubleArrayOf(0.77, 0.02, 0.63, 0.74, 0.49, 0.22, 0.19, 0.76, 0.16, 0.08)
你最终会得到这样的图像:
其中绿线是 8
个八度的计算 Perlin Noise
,持久性为 0.5
。如您所见,x=0 处所有八度音阶的总和大于 1。(蓝线是第一个八度音阶 noise(x)
,橙色线是第二个八度音阶 0.5 * noise(2x)
)。
我做错了什么?
提前致谢。
注意:我知道 Simplex Noise
算法是 Perlin Noise
的继承者,但是出于教育目的我想实现 Perlin Noise
首先。我也知道我的边界应该设置为 256 的大小,但为了简单起见,我现在只使用 10。
我一直在挖掘并发现 this article 引入了一个值来规范化 Perlin(x)
返回的结果。本质上,将振幅相加,然后将总数除以该值。这似乎是有道理的,因为我们可能会“运气不好”并且在第一个八度音阶中的 y 值为 1.0,然后在下一个八度音阶中为 0.5,等等。所以除以振幅之和(在这种情况下为 1.5 2 个八度)将值保持在 0 - 1 范围内似乎是合理的。
但是,我不确定这是否是首选方式,因为其他资源的 none 使用了这种技术。
修改后的代码如下所示:
fun perlin(x: Double, persistence: Double = 0.5, numberOfOctaves: Int = 8): Double {
var total = 0.0
var amplitudeSum = 0.0 //used for normalizing results to 0.0 - 1.0
for (i in 0 until numberOfOctaves) {
val amplitude = persistence.pow(i) // height of the crests
val frequency = 2.0.pow(i) // frequency (number of crests per unit distance) doubles per octave
val octave = amplitude * noise(x * frequency)
total += octave
amplitudeSum += amplitude
}
return total / amplitudeSum
}
出于教育目的,我想在 Kotlin 中实现一维 Perlin Noise
算法。我熟悉了算法 here and here.
我想我理解了基本概念,但是 我的实现可以 return 大于 1 的值。我希望调用 perlin(x)
的结果在 0 到 1 的范围内。我不知道我错在哪里,所以也许有人可以指出我正确的方向。为简单起见,我现在使用简单的线性插值代替 smoothstep 或其他高级技术。
class PerlinNoiseGenerator(seed: Int, private val boundary: Int = 10) {
private var random = Random(seed)
private val noise = DoubleArray(boundary) {
random.nextDouble()
}
fun perlin(x: Double, persistence: Double = 0.5, numberOfOctaves: Int = 8): Double {
var total = 0.0
for (i in 0 until numberOfOctaves) {
val amplitude = persistence.pow(i) // height of the crests
val frequency = 2.0.pow(i) // number of crests per unit distance
val octave = amplitude * noise(x * frequency)
total += octave
}
return total
}
private fun noise(t: Double): Double {
val x = t.toInt()
val x0 = x % boundary
val x1 = if (x0 == boundary - 1) 0 else x0 + 1
val between = t - x
val y0 = noise[x0]
val y1 = noise[x1]
return lerp(y0, y1, between)
}
private fun lerp(a: Double, b: Double, alpha: Double): Double {
return a + alpha * (b - a)
}
}
例如,如果您要使用这些随机生成的噪音
private val noise = doubleArrayOf(0.77, 0.02, 0.63, 0.74, 0.49, 0.22, 0.19, 0.76, 0.16, 0.08)
你最终会得到这样的图像:
其中绿线是 8
个八度的计算 Perlin Noise
,持久性为 0.5
。如您所见,x=0 处所有八度音阶的总和大于 1。(蓝线是第一个八度音阶 noise(x)
,橙色线是第二个八度音阶 0.5 * noise(2x)
)。
我做错了什么?
提前致谢。
注意:我知道 Simplex Noise
算法是 Perlin Noise
的继承者,但是出于教育目的我想实现 Perlin Noise
首先。我也知道我的边界应该设置为 256 的大小,但为了简单起见,我现在只使用 10。
我一直在挖掘并发现 this article 引入了一个值来规范化 Perlin(x)
返回的结果。本质上,将振幅相加,然后将总数除以该值。这似乎是有道理的,因为我们可能会“运气不好”并且在第一个八度音阶中的 y 值为 1.0,然后在下一个八度音阶中为 0.5,等等。所以除以振幅之和(在这种情况下为 1.5 2 个八度)将值保持在 0 - 1 范围内似乎是合理的。
但是,我不确定这是否是首选方式,因为其他资源的 none 使用了这种技术。
修改后的代码如下所示:
fun perlin(x: Double, persistence: Double = 0.5, numberOfOctaves: Int = 8): Double {
var total = 0.0
var amplitudeSum = 0.0 //used for normalizing results to 0.0 - 1.0
for (i in 0 until numberOfOctaves) {
val amplitude = persistence.pow(i) // height of the crests
val frequency = 2.0.pow(i) // frequency (number of crests per unit distance) doubles per octave
val octave = amplitude * noise(x * frequency)
total += octave
amplitudeSum += amplitude
}
return total / amplitudeSum
}