5

実際、トピックのタイトルに記載されている主題に関連するいくつかの質問があります。

私はすでにPerlin関数を使用してアプリケーションで稲妻を作成していますが、実装に完全に満足しているわけではありません。

以下の質問は、初期および改善されたパーリンノイズの実装に基づいています。

問題を単純化するために、1D Perlin関数を使用して、これらのノードでNノードで構成される水平線の高さを変調することによって単純な2D稲妻を作成していると仮定します。

  1. 私が理解している限り、Perlin関数に渡される後続の2つの値は、少なくとも1つ異なる必要があります。そうでない場合、結果の2つの値は同じになります。これは、単純なPerlin実装では、Random関数がint引数で機能し、改善された実装では、値が[0..255]にマップされ、値[0..255を含む配列へのインデックスとして使用されるためです。 ]ランダムな分布で。そうですか?

  2. Perlin関数によって返される最初と最後のオフセット値(つまり、ノード0とN-1の場合)が常に0(ゼロ)になるようにするにはどうすればよいですか?今、私はそれを達成するために私のPerlin関数で正弦関数(0 .. Pi)を変調していますが、それは本当に私が望んでいることではありません。それらをゼロに設定するだけでは、私が望むものではありません。なぜなら、その端にジャギーのない素敵な稲妻の道が欲しいからです。

  3. Perlin関数を変更するにはどうすればよいですか(稲妻のアニメーションの開始フレームと終了フレームとして使用できる2つの異なるパスを取得できるようにするため)?もちろん、各ノード値にパスごとの固定ランダムオフセット計算を追加したり、パーリンノイズを改善するために別の設定の順列テーブルを使用したりすることもできますが、より良いオプションはありますか?

4

2 に答える 2

2
  1. それはあなたがそれをどのように実装し、それからサンプリングするかに依存します。複数のオクターブを使用すると、整数にかなり対抗するのに役立ちます。

    オクターブとそれぞれに対して行われる追加の補間/サンプリングは、パーリンノイズのノイズの多くを提供します。理論的には、異なる整数位置を使用する必要はありません。いつでもサンプリングできるはずで、近くの値と似ています(ただし常に同一であるとは限りません)。

  2. 単純に加算するのではなく、乗数として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. 稲妻に追加するすべての次元のノイズに次元を追加します。稲妻を1次元(水平方向のギザギザ)で変更する場合は、1Dパーリンノイズが必要です。アニメートしたい場合は、2Dが必要です。2軸でギザギザにアニメーション化された稲妻が必要な場合は、3Dノイズなどが必要になります。
于 2011-09-01T23:37:24.513 に答える
1

peachykeenの答えを読み、インターネットでいくつかの(もっと)独自の調査を行った後、私は次の解決策が私のために働くことを発見しました。

  1. 私のパーリンノイズの実装では、稲妻パスノードに[0.0 .. 1.0]の値の範囲を使用すると、ノードMの値(double)M /(double)Nをパーリンノイズ関数に渡すのが最適です。

  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)を作成します。稲妻をアニメートするには、2つの稲妻パス(開始フレームと終了フレーム)を計算する必要があります。次に、各パスの頂点を開始位置と終了位置の間に配置する必要があります。終了フレームに達すると、終了フレームが開始フレームになり、新しい終了フレームが計算されます。3Dパスの場合、パスノードNごとに、ノードNおよび相互のパスに垂直な2つのオフセットベクトルを計算でき、2つの1Dパーリンノイズ値でスケーリングして、ノード位置を開始フレーム位置から終了フレーム位置までラープできます。 。これは、3Dパーリンノイズを実行するよりも安価であり、私のアプリケーションでは非常にうまく機能します。

これが参照としての標準1Dパーリンノイズの実装です(これを改善されたパーリンノイズのベースとして使用しているため、いくつかのものは仮想であり、戦略パターンアプリケーションで標準または改善されたパーリンノイズを使用できます。コードは次のようにいくらか簡略化されています。ここで公開するために、より簡潔にするために):

ヘッダーファイル:

#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 に答える