Генерация случайного движения для точки в трехмерном пространстве - PullRequest
0 голосов
/ 13 июня 2019

Я хочу смоделировать точку, которая движется со случайной вибрацией вокруг средней позиции (скажем, вокруг позиции [X, Y, Z] = [0,0,0]).Первое решение, которое я нашел, состоит в суммировании пары синусоид для каждой оси на основе следующего уравнения:

<a href="https://www.codecogs.com/eqnedit.php?latex=\sum_{i&space;=&space;1}^n&space;A_i&space;\sin(\omega_i&space;t&plus;\phi)" target="_blank"><img src="https://latex.codecogs.com/gif.latex?\sum_{i&space;=&space;1}^n&space;A_i&space;\sin(\omega_i&space;t&plus;\phi)" title="\sum_{i = 1}^n A_i \sin(\omega_i t+\phi)" /></a>

, где A_i - нормальная случайная амплитуда, а omega_i - нормальная случайная частота.Я еще не тестировал фазу, поэтому пока оставляю ее равной нулю.Я сгенерировал цифры ожидаемого нормального распределения и результатов уравнения со следующим подходом .Я пробовал несколько значений N, и я не уверен, что уравнение дает нормально распределенные результаты.Мой подход правильный?Есть ли лучший способ генерировать случайные вибрации?

1 Ответ

2 голосов
/ 23 июня 2019

Для такой задачи вы можете найти полезный шум Перлина или даже фрактальный броуновский шум шум. Смотрите эту реализацию в JavaScript:

class Utils {
    static Lerp(a, b, t) {
        return (1 - t) * a + t * b;
    }

    static Fade(t) {
        return t * t * t * (t * (t * 6 - 15) + 10);
    }   
}

class Noise {
    constructor() {
        this.p = [];
        this.permutationTable = [];
        this.grad3 = [[1, 1, 0], [-1, 1, 0], [1, -1, 0], 
        [-1, -1, 0], [1, 0, 1], [-1, 0, 1], 
        [1, 0, -1], [-1, 0, -1], [0, 1, 1], 
        [0, -1, 1], [0, 1, -1], [0, -1, -1]];

        for (let i = 0; i < 256; i++)
            this.p[i] = Math.floor(Math.random() * 256);

        for (let i = 0; i < 512; i++)
            this.permutationTable[i] = this.p[i & 255];
    }

    PerlinDot(g, x, y, z) {
        return g[0] * x + g[1] * y + g[2] * z;
    }             

    PerlinNoise(x, y, z) {
        let a = Math.floor(x);
        let b = Math.floor(y);
        let c = Math.floor(z);

        x = x - a;
        y = y - b;
        z = z - c;

        a &= 255;
        b &= 255;
        c &= 255;

        let gi000 = this.permutationTable[a + this.permutationTable[b + this.permutationTable[c]]] % 12;
        let gi001 = this.permutationTable[a + this.permutationTable[b + this.permutationTable[c + 1]]] % 12;
        let gi010 = this.permutationTable[a + this.permutationTable[b + 1 + this.permutationTable[c]]] % 12;
        let gi011 = this.permutationTable[a + this.permutationTable[b + 1 + this.permutationTable[c + 1]]] % 12;
        let gi100 = this.permutationTable[a + 1 + this.permutationTable[b + this.permutationTable[c]]] % 12;
        let gi101 = this.permutationTable[a + 1 + this.permutationTable[b + this.permutationTable[c + 1]]] % 12;
        let gi110 = this.permutationTable[a + 1 + this.permutationTable[b + 1 + this.permutationTable[c]]] % 12;
        let gi111 = this.permutationTable[a + 1 + this.permutationTable[b + 1 + this.permutationTable[c + 1]]] % 12;

        let n000 = this.PerlinDot(this.grad3[gi000], x, y, z);
        let n100 = this.PerlinDot(this.grad3[gi100], x - 1, y, z);
        let n010 = this.PerlinDot(this.grad3[gi010], x, y - 1, z);
        let n110 = this.PerlinDot(this.grad3[gi110], x - 1, y - 1, z);
        let n001 = this.PerlinDot(this.grad3[gi001], x, y, z - 1);
        let n101 = this.PerlinDot(this.grad3[gi101], x - 1, y, z - 1);
        let n011 = this.PerlinDot(this.grad3[gi011], x, y - 1, z - 1);
        let n111 = this.PerlinDot(this.grad3[gi111], x - 1, y - 1, z - 1);

        let u = Utils.Fade(x);
        let v = Utils.Fade(y);
        let w = Utils.Fade(z);

        let nx00 = Utils.Lerp(n000, n100, u);
        let nx01 = Utils.Lerp(n001, n101, u);
        let nx10 = Utils.Lerp(n010, n110, u);
        let nx11 = Utils.Lerp(n011, n111, u);

        let nxy0 = Utils.Lerp(nx00, nx10, v);
        let nxy1 = Utils.Lerp(nx01, nx11, v);

        return Utils.Lerp(nxy0, nxy1, w);
    }

    FractalBrownianMotion(x, y, z, octaves, persistence) {
        let total = 0;
        let frequency = 1;
        let amplitude = 1;
        let maxValue = 0;

        for(let i = 0; i < octaves; i++) {
            total = this.PerlinNoise(x * frequency, y * frequency, z * frequency) * amplitude;
            maxValue += amplitude;
            amplitude *= persistence;
            frequency *= 2;
        }

        return total / maxValue;
    }
} 

С помощью Fractal Brownian Motion может иметь огромный контроль над случайностью распределения. Вы можете установить масштаб , начальное смещение и его приращение для каждой оси, октавы и постоянство . Вы можете создать столько позиций, сколько захотите, увеличивая смещения, например:

const NUMBER_OF_POSITIONS = 1000;
const X_OFFSET = 0;
const Y_OFFSET = 0;
const Z_OFFSET = 0;
const X_SCALE = 0.01;
const Y_SCALE = 0.01;
const Z_SCALE = 0.01;
const OCTAVES = 8;
const PERSISTENCE = 2;
const T_INCREMENT = 0.1;
const U_INCREMENT = 0.01;
const V_INCREMENT = 1;

let noise = new Noise();
let positions = [];

let i = 0, t = 0, u = 0, v = 0;
while(i <= NUMBER_OF_POSITIONS) {
    let position = {x:0, y:0, z:0};

    position.x = noise.FractalBrownianMotion((X_OFFSET + t) * X_SCALE, (Y_OFFSET + t) * Y_SCALE, (Z_OFFSET + t) * Z_SCALE, OCTAVES, PERSISTENCE); 
    position.y = noise.FractalBrownianMotion((X_OFFSET + u) * X_SCALE, (Y_OFFSET + u) * Y_SCALE, (Z_OFFSET + u) * Z_SCALE, OCTAVES, PERSISTENCE); 
    position.z = noise.FractalBrownianMotion((X_OFFSET + v) * X_SCALE, (Y_OFFSET + v) * Y_SCALE, (Z_OFFSET + v) * Z_SCALE, OCTAVES, PERSISTENCE); 
    positions.push(position);

    t += T_INCREMENT;
    u += U_INCREMENT;
    v += V_INCREMENT;
    i++;
}

Позиции, которые вы получаете с этими опциями, будут выглядеть примерно так:

...
501: {x: 0.0037344935483775883, y: 0.1477509219864437, z: 0.2434570202517206}
502: {x: -0.008955635460317357, y: 0.14436114483299245, z: -0.20921147024725012}
503: {x: -0.06021806450587406, y: 0.14101769272762685, z: 0.17093922757597568}
504: {x: -0.05796055906294283, y: 0.13772732578136435, z: 0.0018755951606465138}
505: {x: 0.02243901814464688, y: 0.13448621540816477, z: 0.013341084536334057}
506: {x: 0.05074194554980439, y: 0.1312810723109357, z: 0.15821600463130164}
507: {x: 0.011075140752144507, y: 0.12809058766450473, z: 0.04006055269090941}
508: {x: -0.0000031848272303249632, y: 0.12488712875549206, z: -0.003957905411646261}
509: {x: -0.0029798194097060307, y: 0.12163862278870072, z: -0.1988934273517602}
510: {x: -0.008762098499026483, y: 0.11831055728747841, z: 0.02222898347134993}
511: {x: 0.01980289423585394, y: 0.11486802263767962, z: -0.0792283303765883}
512: {x: 0.0776034130079849, y: 0.11127772191732693, z: -0.14141576745502138}
513: {x: 0.08695806478169149, y: 0.10750987521108693, z: 0.049654228704645}
514: {x: 0.036915612100698, y: 0.10353995005320946, z: 0.00033977899920740567}
515: {x: 0.0025923223158845687, y: 0.09935015632822117, z: -0.00952549797548823}
516: {x: 0.0015456084571764527, y: 0.09493065267319889, z: 0.12609905321632175}
517: {x: 0.0582996941155056, y: 0.09028042189611517, z: -0.27532974820612816}
518: {x: 0.19186052966982514, y: 0.08540778482478142, z: -0.00035058098387404606}
519: {x: 0.27063961068049447, y: 0.08033053495775729, z: -0.07737309686568927}
520: {x: 0.20318957178662056, y: 0.07507568989311474, z: -0.14633819135757353}
...

Примечание: для эффективности рекомендуется генерировать все позиции только один раз в массив позиций, как в этом примере, а затем в некотором цикле анимации просто присваивать позиции своей точке из этого массива одну за другой.

Бонус: Здесь вы можете увидеть, как эти значения влияют на распределение нескольких точек, играя с панелью управления ответом в реальном времени: https://marianpekar.github.io/fbm-space/

Ссылка:

https://en.wikipedia.org/wiki/Fractional_Brownian_motion

https://en.wikipedia.org/wiki/Perlin_noise

...