为什么此噪声函数不处理否定参数?

时间:2018-10-05 15:29:05

标签: c++ c numbers integer perlin-noise

我的代码改编自"Improved" Perlin noise

const js_string = 'while(1);{"menu":{"id":"file","value":"File","popup":{"menuitem":[{"value":"New","onclick":"CreateNewDoc()"},{"value":"Open","onclick":"OpenDoc()"},{"value":"Close","onclick":"CloseDoc()"}]}}}';
//while(9); has 9 characters so remove it and parse it to json object
console.log(JSON.parse(js_string.slice(9)));

我想要一个方向的周期性噪声,所以我将该方向包裹在一个额外维度的圆上,这样称呼它

double improved_noise (double x, double y, double z)
{
    // Calculate the "unit cube" that the point asked will be located in
    // The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that
    // plus 1.  Next we calculate the location (from 0.0 to 1.0) in that
    // cube. We also fade the location to smooth the result.

    int xi = (int)x & 255;
    int yi = (int)y & 255;
    int zi = (int)z & 255;

    double xf = x - (int) x;
    double yf = y - (int) y;
    double zf = z - (int) z;

    double u = fade (xf);
    double v = fade (yf);
    double w = fade (zf);

    int aaa, aba, aab, abb, baa, bba, bab, bbb;
    auto & p = permutation;

    aaa = p[p[p[    xi ] +     yi ] +     zi ];
    aba = p[p[p[    xi ] + inc(yi)] +     zi ];
    aab = p[p[p[    xi ] +     yi ] + inc(zi)];
    abb = p[p[p[    xi ] + inc(yi)] + inc(zi)];
    baa = p[p[p[inc(xi)] +     yi ] +     zi ];
    bba = p[p[p[inc(xi)] + inc(yi)] +     zi ];
    bab = p[p[p[inc(xi)] +     yi ] + inc(zi)];
    bbb = p[p[p[inc(xi)] + inc(yi)] + inc(zi)];

    double x1, x2, y1, y2;

    // The gradient function calculates the dot product between a
    // pseudorandom gradient vector and the vector from the input
    // coordinate to the 8 surrounding points in its unit cube.

    // This is all then lerped together as a sort of weighted average
    // based on the faded (u,v,w) values we made earlier.

    x1 = lerp (
        grad (aaa, xf  , yf  , zf),
        grad (baa, xf-1, yf  , zf),
        u);

    x2 = lerp (
        grad (aba, xf  , yf-1, zf),
        grad (bba, xf-1, yf-1, zf),
        u);

    y1 = lerp (x1, x2, v);

    x1 = lerp (
        grad (aab, xf  , yf  , zf-1),
        grad (bab, xf-1, yf  , zf-1),
        u);

    x2 = lerp (
        grad (abb, xf  , yf-1, zf-1),
        grad (bbb, xf-1, yf-1, zf-1),
        u);

    y2 = lerp (x1, x2, v);

    return (lerp (y1, y2, w) + 1) / 2;
}

我得到了奇怪的结果(大和/或否定)。一些实验表明,当 improved_noise (sin(x*2*M_PI), cos(x*2*M_PI), y)) 的参数为负数时会发生这种情况。

为什么此函数不能很好地处理负值,并且可以轻松地进行调整,以使整数行成为有效参数?

1 个答案:

答案 0 :(得分:3)

improved_noise不能处理负输入。

其中的一条评论说:

The left bound is ( |_x_|,|_y_|,|_z_| )…

|…|表示建议使用绝对值。但是,代码将计算:

int xi = (int)x & 255;

在常见的C实现中(使用二进制补码),这有效地计算了x模数256的整数部分的残差。例如,如果x为-3.25,则其整数部分为-3,并且这会将xi设置为253(即-3 + 256)。

这有两个问题。首先,253不是-3的绝对值,因此此代码与注释不匹配。其次,它采用包含该点的单位立方体的“右”边界(值较大的边界),而注释以及正值的行为表明,其意图是设置xi,{ {1}}和yi到“左”边界(值较小的边界)。

从那里继续,代码设置zi。对于非负值,这将生成double xf = x - (int) x;的小数部分。例如,如果x为3.25,则x为.25。但是,如果使用负值和先前的xf操作,则会误入歧途。对于& 255 = -3.25,它计算得出-3.25-253 = -256.25。但是该代码可能仅打算在0到1的小数部分内在一个单位立方体内进行插值。无论使用什么函数执行插值,都可能不支持-256.25。

从本质上讲,此代码从未设计为支持负值,而要对其进行修复,则需要从应该如何操作的第一原则进行重新设计。

original code you point to更好:

x

无论int X = (int)Math.floor(x) & 255 … x -= Math.floor(x); 是否为负,前者正确使用floor来找到“左”边界。然后将x应用于此。假设为两个补码,这将在定期平铺中提供正确的坐标。 (假设两个补码不是纯粹可移植的,应该记录或避免。)

然后,它通过减去& 255的{​​{1}}而不是减去floor的结果来正确地找到分数。例如,对于x = −3.25,这将产生整数坐标−4和分数.75。

& 255修改为类似功能可能会有所帮助。您可以尝试:

x

您同时用C ++和C标记了这个问题。在C ++中,最好使用improved_noise而不是int xi = (int) floor(x) & 255; int yi = (int) floor(y) & 255; int zi = (int) floor(z) & 255; double xf = x - floor(x); double yf = y - floor(y); double zf = z - floor(z); ,并且C ++和C之间可能存在其他差异。