为 3D 空间中的点生成随机运动

Posted

技术标签:

【中文标题】为 3D 空间中的点生成随机运动【英文标题】:Generate random movement for a point in 3D space 【发布时间】:2019-10-28 07:21:31 【问题描述】:

我想模拟一个在平均位置周围随机振动的点(比如说在位置 [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+\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+\phi)" title="\sum_i = 1^n A_i \sin(\omega_i t+\phi)" /></a>

其中A_i 是正常的随机幅度,omega_i 是正常的随机频率。我还没有测试相位,所以我暂时将其设置为零。我使用以下approach 生成了预期正态分布和方程结果的数字。我尝试了N 的多个值,但我不确定该等式是否给出了正态分布的结果。我的方法正确吗?有没有更好的方法来产生随机振动?

【问题讨论】:

为什么需要正弦曲线?只需为每个振动在每个方向添加一个随机偏移量。而且,如果您使用正态分布的随机数,则结果直方图将呈正态分布并具有预期的高斯“钟形”曲线 @EliahuAaron 我希望该点运动顺畅。 随机运动不是“平稳”,而是“抖动”。也许您想在某个“平滑”路径中移动该点并为其添加一个小的随机振动。如果是这样,您可以在每个方向上选择一条正弦曲线路径,并为其添加一些小的随机噪声 @EliahuAaron 如果用正弦(或其他)的总和可以进行高斯近似,我可以创建平滑的运动。 @EliahuAaron,一个简单的例子是海浪的高度。我猜高度会相对平滑(即船上没有抖动),并且可以通过一些随机分布来近似。 【参考方案1】:

对于这样的任务,您可能会发现有用的 Perlin Noise 甚至 Fractal Brownian Motion 噪声。在 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;
    
 

使用分形布朗运动可以对分布的随机性进行极大的控制。您可以为每个轴设置比例、初始偏移及其增量八度音程持久性。您可以通过增加偏移量来生成任意数量的位置,如下所示:

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

【讨论】:

感谢您的详细回答,我将尝试实现此功能,看看是否可以实现。

以上是关于为 3D 空间中的点生成随机运动的主要内容,如果未能解决你的问题,请参考以下文章

unity3d中随机出现图片。

多维空间中的随机单位向量

将纬度和经度转换为 3D 空间中的点

为基于文本的 RPG 创建更一致、随机生成的世界空间

Unity3d在屏幕范围内随机生成一个圆

Unity3d 随机地图生成