Что вызывает эти артефакты в моем 2D-шуме Перлина? - PullRequest
0 голосов
/ 28 ноября 2018

Я реализовал улучшенный алгоритм шума Перлина .Код, предусмотренный для 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;
}

Я создаю 2D-изображение, фиксируя z=0.Это частота 10, так что x, y находятся в [0..10]:

enter image description here

Моя 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 голосов
/ 29 ноября 2018

Вероятно, ошибка в grad (приоритет + выше, чем ?:), что приводит к резкому изменению (во всяком случае, неверному) результата для конкретного xf / yf / hashзначения.

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