0

そこで、私はパーリンとシンプレックス ノイズがどのように機能するかについて少し調査を行ってきました。通常のパーリン ノイズの主要な原理は理解できましたが、順列と勾配テーブルがどのように機能するかについて少し混乱しています。

私の理解では、シードされた乱数ジェネレーターよりも優れたパフォーマンスを提供します。これは、すばやくアクセスできるように適切にインデックス化された事前計算済みの値のテーブルであるためです。

私が完全に理解していないのは、それらが実際にどのように機能するかです。次のように、0 ~ 255 のシャッフルされた値の配列として実装された順列テーブルを見てきました。

permutation[] = { 151,160,137,91,90,15,
131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23,
190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,
88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166,
77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244,
102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196,
135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123,
5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,
223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9,
129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228,
251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107,
49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};

しかし、これの実際の目的が何であるかはわかりません。私が知りたいのは:

  • 格子点に関連して順列表はどのように使用されますか?
  • 勾配テーブルはどのように生成されますか?
  • 順列テーブルの値は勾配テーブルでどのように使用されますか? 順列値は勾配テーブルのインデックスに対応していますか?
4

1 に答える 1

1

libnoise と perlin のノイズ コードをしばらくの間、オフとオンに分解して、すべてがどのように機能するかを理解できるようにしました。理解できないコードで作業するのは嫌いです:)

Unity を使用していない場合は、 http://catlikecoding.com/unity/tutorials/noise/を参照すると役立つ場合がありますが、それに応じてコードを変換できる場合があります。それは私を大いに助けました。

ヒントやヒントが掲載されているさまざまなサイトが他にもあります。Google libnoise、procedural などは、参照できるいくつかの例を示しているはずです。

基本的に、整数配列と組み合わせてノイズで使用される勾配は、0,0,0 付近のポイントであり、設定された数値にパッドを追加するためのいくつかの追加があります。たとえば、次のように、x、y、z 座標 (ポイントの各辺を示す 0 と 1) に基づいて選択された整数の組み合わせを使用します。

// Separate the integer element
int ix0 = int(point.x);
int iy0 = int(point.y);
int iz0 = int(point.z);

// Grab the fractional parts for use later
float tx0 = point.x - ix0;
float ty0 = point.y - iy0;
float tz0 = point.z - iz0;
float tx1 = tx0 - 1f;
float ty1 = ty0 - 1f;
float tz1 = tz0 - 1f;

// Make sure that it is a value compatible with the integer array
ix0 &= hashMask;
iy0 &= hashMask;
iz0 &= hashMask;

// Get the other side of the point
int ix1 = ix0 + 1;
int iy1 = iy0 + 1;
int iz1 = iz0 + 1;

// Grab the integers found at the location in the array
int h0 = hash[ix0];
int h1 = hash[ix1];
int h00 = hash[h0 + iy0];
int h10 = hash[h1 + iy0];
int h01 = hash[h0 + iy1];
int h11 = hash[h1 + iy1];

// Gradient array
private static Vector3[] gradients3D = {
    new Vector3( 1f, 1f, 0f),
    new Vector3(-1f, 1f, 0f),
    new Vector3( 1f,-1f, 0f),
    new Vector3(-1f,-1f, 0f),
    new Vector3( 1f, 0f, 1f),
    new Vector3(-1f, 0f, 1f),
    new Vector3( 1f, 0f,-1f),
    new Vector3(-1f, 0f,-1f),
    new Vector3( 0f, 1f, 1f),
    new Vector3( 0f,-1f, 1f),
    new Vector3( 0f, 1f,-1f),
    new Vector3( 0f,-1f,-1f),

    new Vector3( 1f, 1f, 0f),
    new Vector3(-1f, 1f, 0f),
    new Vector3( 0f,-1f, 1f),
    new Vector3( 0f,-1f,-1f)
};

private const int gradientsMask3D = 15;

// Grab the gradient value at the requested point
Vector3 g000 = gradients3D[hash[h00 + iz0] & gradientsMask3D];
Vector3 g100 = gradients3D[hash[h10 + iz0] & gradientsMask3D];
Vector3 g010 = gradients3D[hash[h01 + iz0] & gradientsMask3D];
Vector3 g110 = gradients3D[hash[h11 + iz0] & gradientsMask3D];
Vector3 g001 = gradients3D[hash[h00 + iz1] & gradientsMask3D];
Vector3 g101 = gradients3D[hash[h10 + iz1] & gradientsMask3D];
Vector3 g011 = gradients3D[hash[h01 + iz1] & gradientsMask3D];
Vector3 g111 = gradients3D[hash[h11 + iz1] & gradientsMask3D];

// Calculate the dot product using the vector and respective fractions
float v000 = Dot(g000, tx0, ty0, tz0);
float v100 = Dot(g100, tx1, ty0, tz0);
float v010 = Dot(g010, tx0, ty1, tz0);
float v110 = Dot(g110, tx1, ty1, tz0);
float v001 = Dot(g001, tx0, ty0, tz1);
float v101 = Dot(g101, tx1, ty0, tz1);
float v011 = Dot(g011, tx0, ty1, tz1);
float v111 = Dot(g111, tx1, ty1, tz1);

// Interpolate between 2 dot results using the fractional numbers 
l0 = Lerp(v000, v100, tx);
l1 = Lerp(v010, v110, tx);
l2 = Lerp(l0,l1,ty);

l3 = Lerp(v001, v101, tx);
l4 = Lerp(v011, v111, tx);
l5 = Lerp(l3,l4,ty);

l6 = Lerp(l2,l5,tz);

これにより、同じ整数と勾配配列を使用して、空間内の単一の一意の点を表す単一の数値が得られます。シードを変更し、整数配列と勾配配列を再シャッフルするだけで、別の数値が生成され、アイテムに一意性を持たせることができますが、同じコードを使用してそれを生成できます。

整数配列が合計 512 要素の数値の繰り返しセットである理由は、上記のコードで追加された +1 値によって引き起こされる可能性のある 0 ~ 255 の制限をルックアップが誤って超えないようにするためです。

線 (1D x0 - x1 )、正方形 (2D x0,y0 - x1,y1 )、および立方体 (3D x0,y0,z0 - x1,y1,z1 ) を視覚化すると、コードが何をしているかがわかります。ほとんどの場合、コードは非常に似ています。

私は独自のバージョンのコードを作成しようとしましたが、何度か試みたにもかかわらず、なぜみんなのノイズ コードが非常に似ているのか理解できました。実際には、パーリンと同様にシンプレックス ノイズが機能する方法は 1 つしかありません。

したがって、私の目標は、少なくともパーリン ノイズとシェーダー プログラミングの両方の内外を理解できるように、この機能をシェーダーと同等のコードに組み込むことです。それは学習曲線ですが、同時に楽しいです。

うまくいけば、これですべての質問に答えました。Ken Perlin の改善された Perlin コードの理由と理由を知りたい場合は、以下をチェックしてください。

http://http.developer.nvidia.com/GPUGems/gpugems_ch05.html - キューブのビジュアル

于 2016-01-16T15:01:14.503 に答える