繁体   English   中英

使用Perlin噪音来制造闪电?

[英]Using Perlin noise to create lightning?

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

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

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

为了简化这个问题,让我们假设我通过使用1D Perlin函数调制由这些节点上的N个节点组成的水平线的高度来创建简单的2D闪电。

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

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

  3. 如何改变Perlin功能(这样我可以使用两条不同的路径作为闪电的动画起点和终帧)? 我当然可以为每个节点值添加固定的随机偏移量,或者使用不同的设置置换表来改善Perlin噪声,但是有更好的选择吗?

  1. 这取决于您如何实现它并从中进行采样。 使用多个八度音程可以帮助计算整数。

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

  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. 为添加到闪电的每个维度添加一个维度。 如果您在一维中修改闪电(水平锯齿状),则需要1D perlin噪声。 如果你想为它设置动画,你需要2D。 如果你想要在两轴和动画上锯齿状的闪电,你需要3D噪音,依此类推。

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

  1. 通过实现Perlin噪声,对闪电路径节点使用[0.0 .. 1.0]的值范围最佳,将节点M的值(双)M /(双)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;
  }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM