在阅读了peachykeen的答案并在互联网上进行了一些(更多)自己的研究之后,我发现以下解决方案对我有用。
在我实现 Perlin 噪声时,对闪电路径节点使用 [0.0 .. 1.0] 的值范围效果最好,将节点 M 的值 (double) M / (double) N 传递给 Perlin 噪声函数。
要让噪声函数 F' 为节点 0 和节点 N-1 返回相同的值,可以应用以下公式:F'(M) = ((M - N) * F(N) + N * F (N - M)) / M。为了使闪电路径偏移以 0 开始和结束,您只需在计算路径后从所有闪电路径偏移中减去 F'(0)。
为了使闪电路径随机化,在计算每个路径节点的偏移量之前,可以计算一个随机偏移量 R 并将其添加到传递给噪声函数的值中,从而使节点的偏移量 O = F'(N+R)。要为闪电设置动画,需要计算两条闪电路径(开始和结束帧),然后每个路径顶点必须在其开始和结束位置之间进行调整。一旦到达结束帧,结束帧将成为开始帧并计算新的结束帧。对于 3D 路径,对于每个路径节点 N,可以计算两个偏移向量,它们与节点 N 处的路径相互垂直,并且可以用两个 1D Perlin 噪声值进行缩放,以使节点位置从帧位置开始到结束位置. 这可能比做 3D Perlin 噪声便宜,并且在我的应用程序中运行良好。
这是我作为参考的标准 1D Perlin 噪声的实现(有些东西是虚拟的,因为我使用它作为改进 Perlin 噪声的基础,允许在策略模式应用程序中使用标准或改进的 Perlin 噪声。代码已被简化为以及使它在这里发布更简洁):
头文件:
#ifndef __PERLIN_H
#define __PERLIN_H
class CPerlin {
private:
int m_randomize;
protected:
double m_amplitude;
double m_persistence;
int m_octaves;
public:
virtual void Setup (double amplitude, double persistence, int octaves, int randomize = -1);
double ComputeNoise (double x);
protected:
double LinearInterpolate (double a, double b, double x);
double CosineInterpolate (double a, double b, double x);
double CubicInterpolate (double v0, double v1, double v2, double v3, double x);
double Noise (int v);
double SmoothedNoise (int x);
virtual double InterpolatedNoise (double x);
};
#endif //__PERLIN_H
执行:
#include <math.h>
#include <stdlib.h>
#include "perlin.h"
#define INTERPOLATION_METHOD 1
#ifndef Pi
# define Pi 3.141592653589793240
#endif
inline double CPerlin::Noise (int n) {
n = (n << 13) ^ n;
return 1.0 - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0;
}
double CPerlin::LinearInterpolate (double a, double b, double x) {
return a * (1.0 - x) + b * x;
}
double CPerlin::CosineInterpolate (double a, double b, double x) {
double f = (1.0 - cos (x * Pi)) * 0.5;
return a * (1.0 - f) + b * f;
}
double CPerlin::CubicInterpolate (double v0, double v1, double v2, double v3, double x) {
double p = (v3 - v2) - (v0 - v1);
double x2 = x * x;
return v1 + (v2 - v0) * x + (v0 - v1 - p) * x2 + p * x2 * x;
}
double CPerlin::SmoothedNoise (int v) {
return Noise (v) / 2 + Noise (v-1) / 4 + Noise (v+1) / 4;
}
int FastFloor (double v) { return (int) ((v < 0) ? v - 1 : v; }
double CPerlin::InterpolatedNoise (double v) {
int i = FastFloor (v);
double v1 = SmoothedNoise (i);
double v2 = SmoothedNoise (i + 1);
#if INTERPOLATION_METHOD == 2
double v0 = SmoothedNoise (i - 1);
double v3 = SmoothedNoise (i + 2);
return CubicInterpolate (v0, v1, v2, v3, v - i);
#elif INTERPOLATION_METHOD == 1
return CosineInterpolate (v1, v2, v - i);
#else
return LinearInterpolate (v1, v2, v - i);
#endif
}
double CPerlin::ComputeNoise (double v) {
double total = 0, amplitude = m_amplitude, frequency = 1.0;
v += m_randomize;
for (int i = 0; i < m_octaves; i++) {
total += InterpolatedNoise (v * frequency) * amplitude;
frequency *= 2.0;
amplitude *= m_persistence;
}
return total;
}
void CPerlin::Setup (double amplitude, double persistence, int octaves, int randomize) {
m_amplitude = (amplitude > 0.0) ? amplitude : 1.0;
m_persistence = (persistence > 0.0) ? persistence : 2.0 / 3.0;
m_octaves = (octaves > 0) ? octaves : 6;
m_randomize = (randomize < 0) ? (rand () * rand ()) & 0xFFFF : randomize;
}