Написал некоторый тип шума 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 измерениях.