如何用1D Noise实现带渐变效果的点位移?
使用1D渐变噪声实现点位移的问题修复
需求与问题
- 目标:基于覆盖整条线的1D渐变噪声驱动点的X/Y轴位移,噪声值作为-1~1的位移乘数,避免独立随机位移
- 当前问题:设置
frequency=1时,噪声出现多个波形,不符合“整条线单平滑渐变”的预期
问题根源
你使用的Perlin噪声实现中,frequency参数表示每单位输入坐标对应的噪声周期数。你的输入坐标是y轴的010区间,当`frequency=1`时,会覆盖10个完整噪声周期,因此出现多个波形。若要让整条线段对应一个平滑渐变,需要将输入坐标归一化到01区间,让整个线段仅覆盖一个噪声周期。
修复后的代码
import numpy as np import matplotlib.pyplot as plt import random import math from enum import Enum #PerlinNoise by alexandr-gnrk class Interp(Enum): LINEAR = 1 COSINE = 2 CUBIC = 3 class PerlinNoise(): def __init__(self, seed, amplitude=1, frequency=1, octaves=1, interp=Interp.COSINE, use_fade=False): self.seed = random.Random(seed).random() self.amplitude = amplitude self.frequency = frequency self.octaves = octaves self.interp = interp self.use_fade = use_fade self.mem_x = dict() def __noise(self, x): # 性能优化:缓存已生成的噪声值 if x not in self.mem_x: self.mem_x[x] = random.Random(self.seed + x).uniform(-1, 1) return self.mem_x[x] def __interpolated_noise(self, x): prev_x = int(x) # 前一个整数坐标 next_x = prev_x + 1 # 后一个整数坐标 frac_x = x - prev_x # 小数部分 if self.use_fade: frac_x = self.__fade(frac_x) # 插值计算 if self.interp is Interp.LINEAR: res = self.__linear_interp( self.__noise(prev_x), self.__noise(next_x), frac_x) elif self.interp is Interp.COSINE: res = self.__cosine_interp( self.__noise(prev_x), self.__noise(next_x), frac_x) else: res = self.__cubic_interp( self.__noise(prev_x - 1), self.__noise(prev_x), self.__noise(next_x), self.__noise(next_x + 1), frac_x) return res def get(self, x): frequency = self.frequency amplitude = self.amplitude result = 0 for _ in range(self.octaves): result += self.__interpolated_noise(x * frequency) * amplitude frequency *= 2 amplitude /= 2 return result def __linear_interp(self, a, b, x): return a + x * (b - a) def __cosine_interp(self, a, b, x): x2 = (1 - math.cos(x * math.pi)) / 2 return a * (1 - x2) + b * x2 def __cubic_interp(self, v0, v1, v2, v3, x): p = (v3 - v2) - (v0 - v1) q = (v0 - v1) - p r = v2 - v0 s = v1 return p * x**3 + q * x**2 + r * x + s def __fade(self, x): # 仅线性插值时生效的平滑过渡函数 return (6 * x**5) - (15 * x**4) + (10 * x**3) # 原始线段:x固定为10,y从0到10的20个点 x = np.linspace(10, 10, 20) y = np.linspace(0, 10, 20) seed = 10 # 初始化噪声生成器,参数不变 gen_x = PerlinNoise(seed=seed, amplitude=5, frequency=1, octaves=1, interp=Interp.CUBIC, use_fade=True) # 核心修改:将y坐标归一化到0-1区间,让整条线段对应一个噪声周期 normalized_y = y / np.max(y) noise_x = np.array([gen_x.get(pos) for pos in normalized_y]) # 绘制对比图 fig, ax = plt.subplots(1) ax.set_aspect("equal") ax.plot(x, y, linewidth=2, color="k") ax.scatter(x, y, s=20, zorder=4, color="k") ax.plot(x+noise_x, y, linewidth=2, color="blue") ax.scatter(x+noise_x, y, s=80, zorder=4, color="red") plt.show()
修复效果
修改后,整条线段的点会基于一个平滑的1D噪声渐变进行位移:
frequency=1对应整个线段覆盖一个完整的噪声周期- 噪声值在-1~1之间变化,最终位移量由
amplitude控制(示例中为5,即最大位移±5个单位) - 完全符合“基于覆盖整条线的渐变噪声驱动位移”的需求
内容的提问来源于stack exchange,提问作者Dr. Pontchartrain
相关产品推荐
相关产品推荐

