Написал некоторый тип шума perlin, он выглядит блочным

Предыдущий ответ на вопрос, похоже, не отвечает на мою проблему "Blocky" Перлинский шум

Я попытался упростить все, что мог, чтобы сделать мой код доступным и понятным.

Я не использую таблицу перестановок, вместо этого я использую генератор 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);
};

Вот результаты, я включил приведенный вектор градиентов (I умножил их на cellize/2).

result1

result2

Мой вопрос в том, почему есть белые артефакты, вы можете как-то увидеть квадраты...

PS: он был решен, я разместил фиксированный источник здесь http://pastebin.com/XHEpV2UP

Не делайте ошибку, применяя гладкую интерпретацию результата вместо коэффициента. Нормализация векторов или добавление смещения во избежание нулей, похоже, ничего не улучшают. Вот раскрашенный результат: result3

Ответ 1

Человеческий глаз чувствителен к разрыву в пространственной производной яркости (яркости). Линейной интерполяции, которую вы используете здесь, достаточно, чтобы сделать яркость непрерывной, но она не делает производную от яркости непрерывной.

Perlin рекомендует, используя упрощенную интерполяцию, чтобы получить более гладкие результаты. Вы можете использовать 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 квантуются до конкретных возможных значений. Вы можете сделать таблицу поиска для значений многочлена заблаговременно при этих значениях, заменив функцию "poly" в предоставленном фрагменте кода. Этот метод позволяет использовать еще более плавные функции (например, степень 5) при очень небольших дополнительных затратах.

Ответ 2

Хотя Джерри прав в своем предыдущем ответе (я бы просто прокомментировал выше, но я все еще довольно новичок в StackOverflow, и у меня недостаточно репутации для комментариев в данный момент)...

И его решение использования:

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

чтобы сгладить/кривая, интерполяционный фактор работает.


Немного лучшее решение состоит в том, чтобы упростить уравнение:

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

результирующая кривая практически идентична (есть небольшие различия, но они крошечные), и на каждую интерполяцию накладывается еще 2 умножения (и все еще только одно вычитание). Результатом является меньшее количество вычислительных усилий.


Это сокращение вычислений может действительно со временем складываться, особенно при использовании функции шумов. Например, если вы начинаете генерировать шум более чем в 2 измерениях.