【问题标题】:Why doesn't this noise function handle negative arguments?为什么这个噪声函数不处理负参数?
【发布时间】:2019-03-11 03:47:18
【问题描述】:

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

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

我得到了奇怪的结果(大的和/或负的)。一些实验表明,当improved_noise 的参数为负时,就会发生这种情况。

为什么这个函数不能很好地处理负值,它可以很容易地调整,所以完整的数字行是一个有效的参数?

【问题讨论】:

  • 我似乎记得按位逻辑运算符是在表示方面为有符号整数定义的,但结果值是实现定义或未指定的。您应该检查 xiyizi 是否不是负数或使用无符号整数。
  • 选择一种语言,C++ 或 C,并删除另一种的标记。

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


【解决方案1】:

improved_noise 不是为处理负输入而设计的。

里面有一条评论说:

左边界是 (|_x_|,|_y_|,|_z_|)…

|…| 表示法表明绝对值是有意的。但是,代码计算:

int xi = (int)x & 255;

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

这有两个问题。首先,253 不是-3 的绝对值,所以这段代码与注释不符。其次,它采用包含点的单位立方体的“右”边界(具有较大值的边界),而 cmets 和正值的行为表明意图是设置 xiyi , 和 zi 到“左”边界(值较小的边界)。

从那里继续,代码集double xf = x - (int) x;。对于非负值,这会产生x 的小数部分。例如,如果 x 是 3.25,xf 将是 0.25。但是,对于负值和之前的 & 255 操作,这会误入歧途。对于x = -3.25,它计算 -3.25 - 253 = -256.25。但该代码可能仅用于在一个单位立方体内插值,用于从 0 到 1 的小数部分。用于执行插值的任何函数都可能不支持 -256.25。

本质上,这段代码从来没有被设计为支持负值,修复它需要从它应该如何操作的首要原则重新设计它。

original code you point to 更好:

int X = (int)Math.floor(x) & 255
…
x -= Math.floor(x);  

无论x是否为负,前者正确使用floor找到“左”边界。然后它将& 255 应用于那个。假设二进制补码,这将在周期性平铺中提供正确的坐标。 (假设二进制补码不是纯粹可移植的,应该记录或避免。)

然后它通过减去xfloor 而不是减去& 255 的结果来正确找到分数。例如,对于x = -3.25,这将产生整数坐标 -4 和小数 0.75。

修改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 标记了这个问题。在 C++ 中,最好使用 std::floor 而不是 floor,C++ 和 C 之间的差异可能还有其他问题。

【讨论】:

    猜你喜欢
    • 2020-05-06
    • 2012-12-27
    • 2018-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    相关资源
    最近更新 更多