2011-09-01 79 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; 
    }