2011-09-01 76 views
5

其实我有几个问题与主题标题中给出的主题有关。使用佩林噪音创造闪电?

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

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

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

  1. 据我已经明白,传递到柏林函数两个后续值必须由至少一个不同,或所得到的两个值将是相同的。这是因为通过简单的Perlin实现,Random函数使用int参数,并且在改进后的实现中,值映射为[0..255],然后用作包含值[0..255 ]随机分布。是对的吗?

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

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

+1

这个问题很好。 – sharptooth

+0

http://www.noisemachine.com/talk1/23.html –

回答

2
  1. 这取决于你如何从中实现它和样品。使用多个八度有助于计数整数相当多。

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

  2. 我会建议使用perlin作为乘数而不是简单的加法,并在闪电过程中使用曲线。例如,perlin的范围是[-1.5,1.5],闪电正常曲线(两端0,中心1),lightning + (perlin * curve)将使您的端点保持不变。根据您如何实现您的柏林噪声发生器,您可能需要类似:

    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噪音,等等。
+0

您对(1.)和(3.)的回复非常有帮助。 (2.)正是我所做的,但不想这样做。不过,非常感谢,这让我进一步。 – karx11erx

+0

我不确定有2更好的方法;当然,没有一个我可以想出,没有变得非常复杂。如果这是一个问题,您可以使用您使用的曲线来获得更好的结果。 – ssube

+0

如何使用2D噪声为1D闪电路径制作动画?我无法完全理解这一点。 y是经过的时间(以帧计,从0到<动画帧数> -1)? – karx11erx

1

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

  1. 我的执行Perlin杂的,使用的值的范围[0.0 .. 1.0]雷电路径节点工作最好,使数值(双)M /(双)N为节点M到柏林噪声函数。为了使噪声函数F'返回节点0和节点N-1的相同值,可以应用下面的公式:F'(M)=((M-N)* F(N)+ N * F(N - M))/ M。为了使闪电路径偏移以0开始和结束,您只需在计算路径后从所有闪电路径偏移中减去F'(0)。在计算每个路径节点的偏移量之前,可以计算随机偏移量R,并将其与传递给噪声函数的值相加,以便节点的偏移量O = F'(N + 1) R)。为了给闪电设置动画,需要计算两条闪电路径(开始帧和结束帧),然后每个路径顶点必须在其开始和结束位置之间放置。一旦到达结束帧,结束帧就成为开始帧,并计算新的结束帧。对于3D路径,对于每个路径节点N,可以计算两个垂直于节点N处的路径和彼此的偏移矢量,并且可以利用两个1D柏林噪声值来缩放,以从节点位置开始到结束帧位置。这可能比3D 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; 
    }