2012-01-13 8 views
5

私はときどき2Dマップ上で輝きを出すシェーダーを書いています。 (「輝き」は単に明るい色のピクセルに過ぎません)私は、輝かしいブロックが(無限の)平面上にランダムかつ一様に分布しているようにしたいが、輝きはX座標とY座標に基づいて決定論的であることが望ましい。私は座標からシードを作成し、そのシードからJava Randomを作成しようとしましたが、これまでの試みで認識可能なパターンが生じました。この関数は頻繁に(何百万回も)呼び出されるため、パフォーマンスが重要です。X/Y座標から疑似乱数パターンを確定的に生成する方法はありますか?

最初に私のhashCode()の実装を模倣しようとしました。この実装では、衝突を避けるために素数乗数が使用されています。その結果、一連のポイントが同じシードを共有している地図全体に目立つガッシュが発生しました。

Iは、次いで、そうのような座標を連結することによって種子を作成しようとした:

long seed = ((long) x << 32) | (long) y; 
Random rand = new Random(seed); 

パターンとして明らかではないが、これは、同様にパターニングされたデータをもたらすように思われます。選択された座標は、均等に分布していないラインで表示されます。

MD5や他の暗号化ハッシュアルゴリズムを使用することは避けました。これは、パフォーマンスの影響を恐れているためです。

+1

1cmから数百万の擬似乱数を生成し、2次元正方形にプロットすると、強力なジェネレータを使用しない限り、認識可能なパターンがよく見えます。 kプレーンを検索する。あなたは、非線形合同擬似乱数ジェネレータを使いたいと思うでしょう。 –

答えて

2

java.util.Randomに実装されているlinear congruential generatorは、任意の選択された項目に対して繰り返し可能であるという利点があります。これらの宣言、

private static final int SEED = 42; 
private static final int N = 128; 
private static final int MAX_X = 1024; 
private static final int MAX_Y = 1024; 
private final Random rnd = new Random(SEED); 
private final List<SparklePoint> list = new ArrayList<SparklePoint>(N); 

次のように矩形(0, 0, MAX_X, MAX_Y)Nランダムに選択された点の(反復)リストを初期化することができ、所与:

public void init(int seed) { 
    for (int i = 0; i < N; i++) { 
     int x = rnd.nextInt(MAX_X); 
     int y = rnd.nextInt(MAX_Y); 
     list.add(new SparklePoint(x, y)); 
    } 
} 

各点にTimerその期間を与えるために便利です

private class SparklePoint implements ActionListener { 

    private static final int MAX_DELAY = 1000; 
    private final Point p; 
    private final Timer t; 
    private boolean bright; 

    public SparklePoint(int x, int y) { 
     p = new Point(x, y); 
     t = new Timer(rnd.nextInt(MAX_DELAY), this); 
     t.setRepeats(false); 
     t.start(); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     t.stop(); 
     if (bright) { 
      // darken p 
     } else { 
      // brighten p 
     } 
     bright = !bright; 
     t.setDelay(rnd.nextInt(MAX_DELAY)); 
     t.start(); 
    } 
} 
+0

挑戦は、最初にその種子を生成することです。私は一定のシードを使用することはできません。なぜなら、いつでも、私は地図の小さな部分だけを描いているからです。もし私が原点から遠く離れて描かれているなら、私が描画している座標に達するまで「ランダム」スピンをしたくないです。 –

+0

ああ、私は選択された点のセットが一定のままだと思った。現在の可視領域外の点を無視するようにビューを最適化することができます。 – trashgod

3

以下は、psでビットを混合するための非常に効率的な関数ですeudoランダムなく、決定論的なファッション:

public static final long xorShift64(long a) { 
    a ^= (a << 21); 
    a ^= (a >>> 35); 
    a ^= (a << 4); 
    return a; 
} 

あなたは、xとy座標から擬似ランダム長い結果を望むのであれば、あなたのような何かができる:

long mix = xorShift64(x) + Long.rotateLeft(xorShift64(y),32) + 0xCAFEBABE; 
    long result = xorShift64(mix); 

私は、このアプローチを使用しました前にグラフィックスで成功し、かなり良い結果を出しました!乱数の品質はjava.util.Randomとほぼ同じですが、それははるかに高速です。

+0

興味深いですね。あなたはそのような構造の耐衝突性を評価する記事を知っていますか? (小さな地図の目的のために、それはそれほど重要ではありませんが、私は不思議です。) –

0

これは私がやったことです(希望の効果を生む)が、間違いなく完璧ではありません。私は二回しかnextInt()を呼び出す引っ張るので、特に冗長であることから、別に

MessageDigest md5; 
try { 
    md5 = MessageDigest.getInstance("MD5"); 
} catch (NoSuchAlgorithmException e) { 
    e.printStackTrace(); 
    return null; 
} 
md5.update(new byte[] { 
    (byte)(x >>> 24), 
    (byte)(x >>> 16), 
    (byte)(x >>> 8), 
    (byte)x, 
    (byte)(z >>> 24), 
    (byte)(z >>> 16), 
    (byte)(z >>> 8), 
    (byte)z 
}, 0, 8); 
byte[] digest = md5.digest(); 
long seed = digest[0] + (digest[1] << 8) + (digest[2] << 16) + (digest[3] << 24) + (digest[4] << 32) + (digest[5] << 40) + (digest[6] << 48) + (digest[7] << 56); 
Random random = new Random(seed); 

は、Randomの使用は、おそらく過剰です。特定の範囲の値を生成するのに便利ですが、とにかくモジュロ演算でそれを行うことができるはずです。

私はMD5がよく理解されているアルゴリズムであり、このアプリケーションでは暗号のセキュリティは重要ではないと思います。私は間違いなく、より速い(そしてあまり目立たない)ものが好きです。

関連する問題