是什么在我的2D Perlin杂波中造成这些伪影?

时间:2018-11-28 16:34:17

标签: c++ perlin-noise

我实现了Scipy documentation。为3D噪声提供的代码可以正常工作。

我以明显的方式调整了算法以制作2D版本。几乎可以用,但是会产生伪像,如下图所示。

这是正确的3D版本:

unsigned inc (unsigned number)
{
    return (number + 1) & 255;
}

double fade (double t)
{
    // Fade function as defined by Ken Perlin.
    // This eases coordinate values
    // so that they will "ease" towards integral values.
    // This ends up smoothing the final output.

    // 6t^5 - 15t^4 + 10t^3

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

double lerp (double a, double b, double x)
{
    return a + x * (b - a);
}

double grad (unsigned hash, double x, double y, double z)
{
    // Take the hashed value and take the first 4 bits of it
    // (15 == 0b1111)

    unsigned h = hash & 15;

    // If the most significant bit (MSB) of the hash is 0
    // then set u = x.  Otherwise y.

    double u = h < 8 /* 0b1000 */ ? x : y;

    double v;

    if (h < 4 /* 0b0100 */)
        // If the first and second significant bits
        // are 0, set v = y
        v = y;
    else if (h == 12 /* 0b1100 */ || h == 14 /* 0b1110*/)
        // If the first and second significant bits
        // are 1, set v = x
        v = x;
    else
        // If the first and second significant bits are not
        // equal (0/1, 1/0) set v = z
        v = z;

    // Use the last 2 bits to decide if u and v are positive
    // or negative. Then return their addition.

    return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}


double
ImprovedNoise :: 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);

    auto result = (lerp (y1, y2, w) + 1) / 2;

    assert (0 <= result);
    assert (result <= 1);
    assert (false == std :: isnan (result));

    return result;
}

我通过固定z=0来生成2D图像。这是10的频率,所以x,y在[0..10]中:

improved Perlin noise algorithm

我的2D版本:

double grad (unsigned hash, double x, double y)
{
    double u = (hash & 1) ? x : y;

    double v = (hash & 2) ? x : y;

    return ((hash & 4) ? u : -u) + (hash & 8) ? v : -v;
}

double
ImprovedNoise :: noise (double x, double y)
{
    int xi = (int)x & 255;
    int yi = (int)y & 255;

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

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

    int aaa, aba,baa, bba;

    auto & p = permutation;

    aaa = p[p[    xi ] +     yi ];
    aba = p[p[    xi ] + inc(yi)];
    baa = p[p[inc(xi)] +     yi ];
    bba = p[p[inc(xi)] + inc(yi)];

    double x1, x2;

    // 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),
        grad (baa, xf-1, yf),
        u);

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

    double result = (lerp (x1, x2, v) + 1) / 2;

    assert (0 <= result);
    assert (result <= 1);
    assert (false == std :: isnan (result));

    return result;
}

这是它生成的图像。

enter image description here

它是使用以下方法生成的:

int size=400;
int freq=10;

create_widget (size, size, [&] (int x, int y)
{
    return noise (x*freq / float (size), y*freq / float (size));
});

是什么导致这些水平线和垂直线?我以为这可能是整数边界问题,但这可以预测整个图像中的freq个伪像,因此我想是另外一回事了。

您知道错误在哪里吗?

1 个答案:

答案 0 :(得分:0)

grad中可能有一个错误(+的优先级高于?:),这会导致特定xf上(无论如何不正确)结果的突然变化。 / yf / hash值。

return ((hash & 4) ? u : -u) + (hash & 8) ? v : -v;
                              (                   )