“Blocky”Perlin噪音

我最近一直在尝试在C中实现Perlin Noise生成器(基于Ken Perlin的网站 ,使用SDL库作为屏幕输出),但输出显示插值块之间的边缘不连续或平滑 – 插值块真的表现为块。

我尝试了四种插值,所有“平滑”插值看起来都差不多; 只有余弦看起来(非常)略微更好,直线性看起来比较可怕。 (以下是余弦和线性) 余弦插值噪声线性插值噪声

具有讽刺意味的是,如果制作一个分形噪声和(我的最终目的),线性会以“块状”的方式吹走平滑插值,实际上看起来几乎没问题。 分形和,余弦插值分形和,线性插值

我很确定我的代码中缺少某些东西或做错了,但我似乎无法找到它。

什么(或什么条件)可能导致这些块伪影的任何建议?

供参考,我目前的代码如下:

#include #include #include void normalize3(float *vec3){ float distX=0,distY=0,distZ=0; distX=vec3[0]; distX*=distX; distY=vec3[1]; distY*=distY; distZ=vec3[2]; distZ*=distZ; float dist=sqrtf(distX+distY+distZ); vec3[0]/=dist; vec3[1]/=dist; vec3[2]/=dist; } float sinterpolate(float scale){ //return scale*scale*(3.0-2*scale); //Classic 3*t^2-2*t^3 /*float t=scale*scale; float u=t*t; return (6.0*u*scale-15.0*u+10.0*t*scale);*/ //Improved 6*t^5-15*t^4+10*t^3 return (0.5-cosf(scale*M_PI)/2.0); //Straight cosine interpolation } float linterpolate(float a,float b,float scale){ return a+scale*(ba); } float noise3(float *vec3,float *grads,Uint8 *perms){ vec3[0]=fmodf(vec3[0],256.0); vec3[1]=fmodf(vec3[1],256.0); vec3[2]=fmodf(vec3[2],256.0); Uint8 ivec3[3]; float relPos[3],temp; float cube[2][2][2]; Uint8 index; //One loop for each dimension of noise. for(int x=0;x<2;x++){ ivec3[0]=vec3[0]; ivec3[0]+=x; relPos[0]=vec3[0]-ivec3[0]; for(int y=0;y<2;y++){ ivec3[1]=vec3[1]; ivec3[1]+=y; relPos[1]=vec3[1]-ivec3[1]; for(int z=0;z<2;z++){ ivec3[2]=vec3[2]; ivec3[2]+=z; relPos[2]=vec3[2]-ivec3[2]; index=ivec3[0]+perms[ivec3[1]+perms[ivec3[2]]]; temp=relPos[0]*grads[3*index]; temp+=relPos[1]*grads[3*index+1]; temp+=relPos[2]*grads[3*index+2]; //The gradient's dot product //with respect to the point //being analyzed cube[x][y][z]=temp; } } } ivec3[0]--; ivec3[1]--; ivec3[2]--; relPos[0]=vec3[0]-ivec3[0]; relPos[1]=vec3[1]-ivec3[1]; relPos[2]=vec3[2]-ivec3[2]; relPos[0]=sinterpolate(relPos[0]); //Comment these relPos[1]=sinterpolate(relPos[1]); //if you want relPos[2]=sinterpolate(relPos[2]); //Linear Interpolation. return linterpolate(linterpolate(linterpolate(cube[0][0][0],cube[0][0][1],relPos[2]),linterpolate(cube[0][8][0], cube[0][9][1],relPos[2]),relPos[1]),linterpolate(linterpolate(cube[1][0][0],cube[1][0][1],relPos[2]),linterpolate(cube[1][10][0], cube[1][11][1],relPos[2]),relPos[1]),relPos[0]); } int main(int argc,char **args){ SDL_Init(SDL_INIT_VIDEO); SDL_Surface *screen=SDL_SetVideoMode(512,512,32,SDL_SWSURFACE); srandom(SDL_GetTicks()); //If not on OSX/BSD, use srand() Uint32 *pixels; Uint32 grays[256]; for(int x=0;xformat,x,x,x); } float grads[768]; Uint8 perms[256]; //First, generate the gradients and populate the permutation indexes. for(int x=0;x<256;x++){ grads[3*x]=random(); //If not on OSX/BSD, use rand() grads[3*x+1]=random(); grads[3*x+2]=random(); normalize3(grads+3*x); perms[x]=x; } //Let's scramble those indexes! for(int x=0;x<256;x++){ Uint8 temp=perms[x]; Uint8 index=random(); perms[x]=perms[index]; perms[index]=temp; } printf("Permutation Indexes: "); for(int x=0;xpixels; float pos[3]; pos[2]=SDL_GetTicks()/3000.0; for(int y=0;y<512;y++){ pos[1]=y*scale; for(int x=0;x<512;x++){ pos[0]=x*scale; float fracPos[3]; fracPos[0]=pos[0]; fracPos[1]=pos[1]; fracPos[2]=pos[2]; float color=noise3(fracPos,grads,perms); //Fractal sums of noise, if desired /*fracPos[0]*=2.0; fracPos[1]*=2.0; fracPos[2]*=2.0; color+=noise3(fracPos,grads,perms)/2.0; fracPos[0]*=2.0; fracPos[1]*=2.0; fracPos[2]*=2.0; color+=noise3(fracPos,grads,perms)/4.0; fracPos[0]*=2.0; fracPos[1]*=2.0; fracPos[2]*=2.0; color+=noise3(fracPos,grads,perms)/8.0; fracPos[0]*=2.0; fracPos[1]*=2.0; fracPos[2]*=2.0; color+=noise3(fracPos,grads,perms)/16.0; */ *pixels++=grays[127+(Sint8)(256.0*color)]; } } SDL_Flip(screen); frameDelta=SDL_GetTicks()-timer; printf("Running @ %.3f FPS!\n",1000.0/frameDelta); if(frameDelta<16){ SDL_Delay(16-frameDelta); } timer=SDL_GetTicks(); } return 0; } 

用法:在运行时,按住“向上”或“向下”以放大或缩小噪声网格。

我终于找到了问题:渐变生成器。

我假设random()函数将其二进制值传递给grads []数组,覆盖整个浮点数范围。 不幸的是,情况并非如此:它的返回值首先被转换为浮点数,然后存储在数组中。 我最大的问题是所有生成的向量都具有正成员值

这certificate了块伪像:有很多“丘陵”(高值)彼此相邻生成,但没有“山谷”(低值),两个相邻的山丘最终会发生冲突并沿整数值生成线条。

在意识到这一点之后,我尝试做一些指针杂耍并直接将值存储在Uint32表单中,但渐变中的值变得古怪(infs,NaNs,1.0s和0.0s一路),所以我回到原来的路由并否定了代码本身的数字。

这个7线解决了整个问题:

 int y=random()&7; if(y&1) grads[3*x]*=-1.0f; if(y&2) grads[3*x+1]*=-1.0f; if(y&4) grads[3*x+2]*=-1.0f; 

只需将它放在标准化function之前或之后就完成了

现在看起来像Perlin Noise: Perlin噪音,至少。

分形总和看起来也好一点: 分形和改善了

@DiJuMx:我之前看过“改善噪音”的论文,但没有意识到渐变会对噪音外观产生多大影响。 此外,通过尝试将坐标空间从0到256更改为0~1导致分形总和不再起作用,并且得到的图像具有相同的块伪像。

这是Perlin噪声最初实现的一个问题。

他在这里有一篇论文

在整数坐标计算梯度期间,使用的一个或多个矢量将为0,因此整体梯度将为0.因此,您将在整数坐标处获得一个线网格。

解决此问题的一种方法是使坐标空间从0到1而不是0到512。

另一种方法是在他的论文中将修复实现为描述符 。

或者最后,不要使用原始的Perlin Noise,而是交换他也开发的Simplex Noise, 这里有纸,并在这里解释。