5

2D マップ上で時折点を輝かせるシェーダーを作成しています。(「きらめき」は単に明るい色のピクセルです。) きらめくブロックを (無限の) 平面上にランダムかつ均一に分散させて表示したいのですが、X 座標と Y 座標に基づいてきらめきを決定論的にしたいと考えています。座標からシードを作成し、そのシードから Java を作成しようとしましたRandomが、これまでのところ、認識可能なパターンが得られました。この関数は頻繁に (何百万回も) 呼び出されるため、パフォーマンスが重要です。

hashCode()最初に、素数乗数を使用して衝突を回避する実装を模倣しようとしました。これにより、一連のポイントが同じシードを共有するマップ全体に目に見える切り傷が生じました。

次に、次のように座標を連結してシードを作成しようとしました。

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

パターンは明らかではありませんが、これもパターン化されたデータになるようです。選択した座標が一列に表示され、均等に配置されていません。

パフォーマンスへの影響が懸念されるため、MD5 やその他の暗号化ハッシュ アルゴリズムの使用は避けています。

4

3 に答える 3

4

以下は、疑似乱数だが決定論的な方法でビットを混合するための非常に効率的な関数です。

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 とほぼ同じですが、はるかに高速です....

于 2012-01-13T05:13:29.063 に答える
2

で実装されている線形合同ジェネレーターjava.util.Randomには、選択した任意の に対して繰り返し可能であるという利点がありSEEDます。これらの宣言を考えると、

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);

次のようNに、長方形内のランダムに選択された点の (繰り返し可能な) リストを初期化できます。(0, 0, MAX_X, MAX_Y)

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();
    }
}
于 2012-01-13T05:02:34.250 に答える
0

これは私が行ったもので、機能します (目的の効果が得られます) が、完全ではありません。

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);

特に冗長であることは別として、プル コールは2 回Randomしか行わないため、 の使用はおそらく過剰です。nextInt()特定の範囲の値を生成するのに便利ですが、とにかくモジュロ演算でそれを行うことができるはずです。

MD5 はよく理解されたアルゴリズムであり、このアプリケーションでは暗号化セキュリティが重要ではない点が気に入っています。ただし、私は間違いなく、より高速な(そして乱雑でない)ものを望んでいます。

于 2012-01-13T16:56:59.530 に答える