【问题标题】:Wrote some perlin noise kind of code, it looks blocky写了一些 perlin 噪声类型的代码,它看起来很块状
【发布时间】:2014-08-17 13:47:14
【问题描述】:

之前回答的问题似乎没有回答我的问题"Blocky" Perlin noise

我尽量简化以使我的代码易于阅读和理解。

我不使用置换表,而是使用 mt19937 生成器。

我使用 SFML

using namespace std;
using namespace sf;
typedef Vector2f Vec2;
Sprite spr;
Texture tx;
// dot product
float        prod(Vec2 a, Vec2 b)       { return a.x*b.x + a.y*b.y; }
// linear interpolation
float         interp(float start,float end,float coef){return coef*(end-start)+start;}
// get the noise of a certain pixel, giving its relative value vector in the square with [0.0 1.0] values
float getnoise(Vec2&A, Vec2&B, Vec2&C, Vec2&D, Vec2 rel){
    float
    dot_a=prod(A ,Vec2(rel.x   ,rel.y)),
    dot_b=prod(B ,Vec2(rel.x-1 ,rel.y)),
    dot_c=prod(C ,Vec2(rel.x   ,rel.y-1)),
    dot_d=prod(D ,Vec2(rel.x-1 ,rel.y-1));
    return interp
    (interp(dot_a,dot_b,rel.x),interp(dot_c,dot_d,rel.x),rel.y);
//    return interp
//    (interp(da,db,rel.x),interp(dc,dd,rel.x),rel.y);
}
// calculate the [0.0 1.0] relative value of a pixel
Vec2 getrel(int i, int j, float cellsize){
    return Vec2
    (float
     (i // which pixel
      -(i/int(cellsize))//which cell
      *cellsize)// floor() equivalent
      /cellsize,// [0,1] range
     float(j-(j/int(cellsize))*cellsize)/cellsize
     );
}
// generates an array of random float values
vector<float> seeded_rand_float(unsigned int seed, int many){
    vector<float> ret;
    std::mt19937 rr;
    std::uniform_real_distribution<float> dist(0, 1.0);

    rr.seed(seed);

    for(int j = 0 ; j < many; ++j)
        ret.push_back(dist(rr));
    return ret;
}
// use above function to generate an array of random vectors with [0.0 1.0] values
vector<Vec2>seeded_rand_vec2(unsigned int seed, int many){
    auto coeffs1 = seeded_rand_float(seed, many*2);
//    auto coeffs2 = seeded_rand_float(seed+1, many); //bad choice !
    vector<Vec2> pushere;
    for(int i = 0; i < many; ++i)
        pushere.push_back(Vec2(coeffs1[2*i],coeffs1[2*i+1]));
//    pushere.push_back(Vec2(coeffs1[i],coeffs2[i]));
    return pushere;
}
// here we make the perlin noise
void make_perlin()
{
    int seed = 43;
    int pixels = 400; // how many pixels
    int divisions = 10; // cell squares
    float cellsize = float(pixels)/divisions; // size of a cell

    auto randv = seeded_rand_vec2(seed,(divisions+1)*(divisions+1));
    // makes the vectors be in [-1.0 1.0] range
    for(auto&a:randv)
        a = a*2.0f-Vec2(1.f,1.f);
    Image img;
    img.create(pixels,pixels,Color(0,0,0));

    for(int j=0;j<=pixels;++j)
    {
        for(int i=0;i<=pixels;++i)
        {
            int ii = int(i/cellsize); // cell index
            int jj = int(j/cellsize);
            // those are the nearest gradient vectors for the current pixel
            Vec2
            A = randv[divisions*jj      +ii],
            B = randv[divisions*jj      +ii+1],
            C = randv[divisions*(jj+1)  +ii],
            D = randv[divisions*(jj+1)  +ii+1];

            float val = getnoise(A,B,C,D,getrel(i,j,cellsize));
            val = 255.f*(.5f * val + .7f);

            img.setPixel(i,j,Color(val,val,val));
        }
    }
    tx.loadFromImage(img);
    spr.setPosition(Vec2(10,10));
    spr.setTexture(tx);
};

这是结果,我包括了结果梯度向量(我将它们乘以 cellsize/2)。

我的问题是为什么会有白色伪影,你可以以某种方式看到方块...

PS:已经解决了,我这里贴出固定源码http://pastebin.com/XHEpV2UP

不要错误地对结果应用平滑插值而不是系数。规范化向量或添加偏移量以避免零似乎并没有改善任何东西。这是彩色的结果:

【问题讨论】:

    标签: c++ sfml noise perlin-noise procedural-generation


    【解决方案1】:

    人眼对亮度(亮度)的空间导数的不连续性很敏感。您在此处使用的线性插值足以使亮度连续,但不能使亮度的导数连续。

    Perlin recommends 使用简化插值来获得更平滑的结果。您可以在插值函数中使用 3*t^2 - 2*t^3 (如链接演示中的建议)。这应该可以解决眼前的问题。

    看起来像

    // interpolation
    float        linear(float start,float end,float coef){return coef*(end-start)+start;}
    float        poly(float coef){return 3*coef*coef - 2*coef*coef*coef;}
    float        interp(float start,float end,float coef){return linear(start, end, poly(coef));}
    

    但请注意,为每个插值计算多项式是不必要的昂贵。通常(包括这里)这种噪声是在像素网格上评估的,正方形是一些整数(或有理数)像素大;这意味着 rel.x、rel.y、rel.x-1 和 rel.y-1 被量化为特定的可能值。您可以在这些值处提前为多项式的值创建一个查找表,替换提供的代码 sn-p 中的“poly”函数。这种技术可以让您以极少的额外成本使用更平滑(例如 5 级)的缓动函数。

    【讨论】:

    • 它是 rel.x-1 和 rel.y-1。感谢插值函数,我将这些多项式应用于结果而不是系数。
    • 其他错别字:你在 3*coeff+coeff 中有一个 + 而不是 *,它是 3t^2 - 2t^3(减去不加)。我还提供了 Vec2 interp,但在这里我使用了 float interp。无论如何,我没有看到我的代码有任何修复...
    • 针对错别字和类型签名进行了编辑(抱歉,这里有点睡眠不足)。我提供的 interp 函数可以用作您的 interp 函数的直接替代品,它应该可以修复块状外观。
    • @JerryFederspiel 还有一个错字:它是3*t^2 - 2*t^3 而不是2*t^3 - 3*t^2。这改变了很多输出。 ;-)
    • 感谢您的功能确实有帮助,我编辑了我的问题以发布结果
    【解决方案2】:

    尽管 Jerry 在他的上述回答中是正确的(我会在上面简单地评论,但我对 StackOverflow 还是很陌生,目前我没有足够的声誉来评论)...

    以及他的使用解决方案:

    (3*coef*coef) - (2*coef*coef*coef)
    

    平滑/弯曲插值因子起作用。


    稍微好一点的解决方案是将方程简化为:

    (3 - (2*coef)) * coef*coef
    

    生成的曲线几乎相同(存在细微差别,但很小),并且每次插值需要进行的乘法运算次数减少 2 次(并且仍然只有一次减法运算)。从而减少计算工作量。


    随着时间的推移,计算量的减少确实会增加,尤其是在大量使用噪声函数时。例如,如果您开始产生超过 2 维的噪声。

    【讨论】:

      猜你喜欢
      • 2017-09-04
      • 2014-05-21
      • 2018-01-23
      • 1970-01-01
      • 2014-10-06
      • 2015-03-20
      • 2014-02-15
      • 2020-06-06
      • 2011-09-20
      相关资源
      最近更新 更多