之前回答的问题似乎没有回答我的问题"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
不要错误地在结果上应用平滑的interp而不是系数。归一化向量或添加偏移量以避免零点似乎没有改善任何东西。这是彩色结果:
答案 0 :(得分:9)
人眼对亮度(亮度)的空间导数中的不连续性敏感。您在此处使用的线性插值足以使亮度连续,但不会使亮度的导数连续。
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被量化为特定的可能值。您可以提前在这些值上为多项式的值创建一个查找表,替换&#34; poly&#34;函数在提供的代码片段中。这种技术让您可以以极低的额外成本使用更平滑(例如5级)的缓动功能。
答案 1 :(得分:0)
虽然Jerry在上面的回答中是正确的(我上面会简单地评论一下,但我仍然是StackOverflow的新手,而且我目前没有足够的声誉发表评论)......
他使用的解决方案:
(3*coef*coef) - (2*coef*coef*coef)
使插值因子平滑/曲线起作用。
稍微好一点的解决方案是将等式简化为:
(3 - (2*coef)) * coef*coef
得到的曲线几乎相同(存在细微差别,但它们很小),并且每次插值的乘法次数减少2次(并且仍然只有一次减法)。从而减少了计算量。
这种计算量的减少可能会随着时间的推移而增加,尤其是在使用噪声功能时。例如,如果您开始生成超过2维的噪音。