5

实际上,我有几个与主题标题中给出的主题相关的问题。

我已经在我的应用程序中使用 Perlin 函数来创建闪电,但我对我的实现并不完全满意。

以下问题基于初始和改进的 Perlin 噪声实现。

为了简化问题,假设我正在创建一个简单的 2D 闪电,方法是使用 1D Perlin 函数在这些节点处调制由 N 个节点组成的水平线的高度。

  1. 据我所知,传递给 Perlin 函数的两个后续值必须至少相差一个,否则生成的两个值将相同。这是因为对于简单的 Perlin 实现,Random 函数使用 int 参数,在改进的实现中,值被映射到 [0..255],然后用作包含值 [0..255] 的数组的索引] 随机分布。那正确吗?

  2. 如何实现 Perlin 函数返回的第一个和最后一个偏移值(即节点 0 和 N-1)始终为 0(零)?现在我正在用我的 Perlin 函数调制一个正弦函数(0 .. Pi)来实现这一点,但这并不是我真正想要的。只是将它们设置为零不是我想要的,因为我想要一条没有锯齿的漂亮闪电路径。

  3. 如何改变 Perlin 函数(以便获得两条不同的路径,可用作闪电的动画开始帧和结束帧)?我当然可以将每个路径计算的固定随机偏移量添加到每个节点值,或者使用不同的设置排列表来改善 Perlin 噪声,但是有更好的选择吗?

4

2 回答 2

2
  1. 这取决于您如何实现它并从中采样。使用多个八度音程有助于相当多地计算整数。

    为每个八度音程和额外的插值/采样提供了柏林噪声中的大部分噪声。理论上,您不应该需要使用不同的整数位置;您应该能够在任何时候进行采样,并且它将与附近的值相似(但并不总是相同)。

  2. 我建议使用 perlin 作为乘数而不是简单的加法,并在闪电过程中使用曲线。例如,perlin 在 [-1.5, 1.5] 范围内和闪电上方的法线曲线(两端为 0,中心为 1),lightning + (perlin * curve)将使您的端点保持静止。根据您实现 perlin 噪声生成器的方式,您可能需要以下内容:

    lightning.x += ((perlin(lightning.y, octaves) * 2.0) - 0.5) * curve(lightning.y);

    如果perlin返回 [0,1] 或

    lightning.x += (perlin(lightning.y, octaves) / 128.0) * curve(lightning.y);

    如果它返回 [0, 255]。假设lightning.x从一个给定的值开始,可能是 0,这将给出一条仍然符合原始起点和终点的有点锯齿状的线。

  3. 为添加到闪电的每个维度添加一个维度。如果您要修改一维闪电(水平锯齿状),则需要一维柏林噪声。如果要对其进行动画处理,则需要 2D。如果您想要在两个轴上呈锯齿状并具有动画效果的闪电,则需要 3D 噪波等等。
于 2011-09-01T23:37:24.513 回答
1

在阅读了peachykeen的答案并在互联网上进行了一些(更多)自己的研究之后,我发现以下解决方案对我有用。

  1. 在我实现 Perlin 噪声时,对闪电路径节点使用 [0.0 .. 1.0] 的值范围效果最好,将节点 M 的值 (double) M / (double) N 传递给 Perlin 噪声函数。

  2. 要让噪声函数 F' 为节点 0 和节点 N-1 返回相同的值,可以应用以下公式:F'(M) = ((M - N) * F(N) + N * F (N - M)) / M。为了使闪电路径偏移以 0 开始和结束,您只需在计算路径后从所有闪电路径偏移中减去 F'(0)。

  3. 为了使闪电路径随机化,在计算每个路径节点的偏移量之前,可以计算一个随机偏移量 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;
  }
于 2011-09-02T10:03:08.407 回答