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

Предыдущий ответ на вопрос не отвечает моей проблеме "Блочный" Шум Перлина

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

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

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

result1

result2

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

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

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

12

Решение

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

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

9

Другие решения

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

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

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

для сглаживания / изгиба работает коэффициент интерполяции.


Немного лучшим решением является упрощение уравнения для:

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

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


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

0

По вопросам рекламы [email protected]