c++ - 是什么导致我的 2D Perlin 噪声中出现这些伪像?

标签 c++ perlin-noise

我实现了 improved Perlin noise algorithm .为 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 生成二维图像。这是 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 伪影,所以我猜这是另外一回事。

你能看出错误是什么吗?

最佳答案

grad可能有错误(+的优先级高于?:),导致(反正不正确)特定 xf/yf/hash 值的结果。

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

关于c++ - 是什么导致我的 2D Perlin 噪声中出现这些伪像?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53524107/

相关文章:

c++ - 为什么语句是: "undefined behavior means the compiler can do anything it wants" true?

algorithm - Perlin/Simplex Noise 算法的随机性质量如何?

matlab - 在 matlab 中生成程序\perlin 噪声

algorithm - 如何跟踪/颜色分离使用 perlin 噪声生成的形状?

c++ - 场景更改时的 QGraphicsRectItem 和 QGraphicsScene 问题

c++ - 客户端未检测到服务器断开连接

c++ - 痛饮 : How to typemap two fields of a struct?

c++ - 是否可以在 gcc 中关闭对 "and"/"or" boolean 运算符使用的支持?

python - Perlin噪音问题: clearly visible lines in result

c++ - 使用 Perlin 噪声生成 2d 地形(基于平铺)