C &引用;“块状”;柏林噪声
我最近一直在尝试用C实现一个柏林噪声发生器(基于SDL库,使用SDL库作为屏幕输出),但是输出显示插值块之间的边不是连续的或平滑的-插值块确实表现为块 我试过四种插值,所有的“平滑”插值看起来都差不多;相比之下,只有余弦看起来(非常)稍微好一点,直线看起来很可怕。(以下是余弦和线性) 具有讽刺意味的是,如果对噪声进行分形求和(这是我的最终目的),那么线性插值会以“块状”的形式消除平滑插值,实际上看起来几乎没有问题。 我很确定我的代码中缺少了一些东西,或者做错了,但我似乎找不到 关于可能导致这些块瑕疵的原因(或条件)有何建议? 作为参考,我的当前代码如下:C &引用;“块状”;柏林噪声,c,perlin-noise,C,Perlin Noise,我最近一直在尝试用C实现一个柏林噪声发生器(基于SDL库,使用SDL库作为屏幕输出),但是输出显示插值块之间的边不是连续的或平滑的-插值块确实表现为块 我试过四种插值,所有的“平滑”插值看起来都差不多;相比之下,只有余弦看起来(非常)稍微好一点,直线看起来很可怕。(以下是余弦和线性) 具有讽刺意味的是,如果对噪声进行分形求和(这是我的最终目的),那么线性插值会以“块状”的形式消除平滑插值,实际上看起来几乎没有问题。 我很确定我的代码中缺少了一些东西,或者做错了,但我似乎找不到 关于可能导致这
#include<stdio.h>
#include<math.h>
#include<SDL/SDL.h>
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*(b-a);
}
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;x<256;x++){
grays[x]=SDL_MapRGB(screen->format,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;x<256;x++){
printf("%hhu, ",perms[x]);
}
putchar('\n');
Uint32 timer=SDL_GetTicks(),frameDelta;
SDL_Event eventos;
float zoom=-5.0;
eventos.type=SDL_NOEVENT;
while(eventos.type!=SDL_QUIT){
SDL_PollEvent(&eventos);
if(SDL_GetKeyState(NULL)[SDLK_UP]){
zoom-=0.001*frameDelta;
}
else if(SDL_GetKeyState(NULL)[SDLK_DOWN]){
zoom+=0.001*frameDelta;
}
float scale=expf(zoom);
pixels=screen->pixels;
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;
}
#包括
#包括
#包括
void normalize3(浮点*vec3){
浮动距离X=0,距离Y=0,距离Z=0;
distX=vec3[0];
distX*=distX;
distY=vec3[1];
distY*=distY;
distZ=vec3[2];
distZ*=distZ;
浮动距离=sqrtf(distX+distY+distZ);
vec3[0]/=dist;
vec3[1]/=dist;
vec3[2]/=dist;
}
浮法烧结(浮法氧化){
//返回刻度*刻度*(3.0-2*刻度);//经典3*t^2-2*t^3
/*浮动t=刻度*刻度;
浮点数u=t*t;
返回(6.0*u*scale-15.0*u+10.0*t*scale);*///改进的6*t^5-15*t^4+10*t^3
return(0.5-cosf(scale*M_PI)/2.0);//直线余弦插值
}
浮子过滤纸(浮子a、浮子b、浮子刻度){
返回a+刻度*(b-a);
}
浮动噪音3(浮动*矢量3、浮动*梯度、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];
浮动位置[3],温度;
浮立方[2][2][2];
Uint8指数;
//噪声的每个维度都有一个循环。
对于(intx=0;x来说,柏林噪声的原始实现存在问题
他有一张纸在上面
在计算整数坐标处的梯度时,使用的一个或多个向量将为0,因此总体梯度将为0。因此,您将在整数坐标处获得线网格
解决此问题的一种方法是使坐标空间从0变为1,而不是从0变为512
另一种方法是按照his中的描述实现修复
或者最后,不要使用原始的柏林噪声,而是换成他也开发的单纯形噪声、论文和解释。我终于找到了问题:梯度发生器
我假设random()函数将其二进制值传递给grads[]数组,以这种方式覆盖整个浮点数范围。不幸的是,情况并非如此:它的返回值首先被转换为浮点数,然后存储在数组中。我最大的问题是所有生成的向量都有正成员值
这证明了块瑕疵的合理性:有许多“山丘”(高值)彼此相邻生成,但没有“山谷”(低值),两个相邻的山丘最终会碰撞并沿整数值生成线条
意识到这一点后,我尝试进行一些指针杂耍,并将值直接存储在Uint32格式中,但渐变中的值变得古怪(infs、NaNs、1.0和0.0一直如此),因此我返回到原始路径,并对代码本身中的数字求反
这架7-liner解决了整个问题:
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;
只要把它放在规格化函数之前或之后,它就完成了
现在看起来像柏林噪音:
分形和看起来也更好一些:
@DiJuMx:我看到了“改善噪音”以前的论文,但没有意识到梯度会对噪声外观产生多大的影响。另外,尝试将坐标空间从0~256更改为0~1会导致分形和不再工作,生成的图像具有相同的块瑕疵。我建议您更清楚地提出这个问题,可能使用“级别2”标题##问题
就在前面。您可能应该包括到Ken Perlin网站的链接(最好是您正在使用的特定页面)。当然,这可能不难找到,但如果人们不必进入他们选择的搜索引擎,它会改进您的问题。编辑并链接。我非常感谢您对改进问题的建议。您是否有理由自己实现噪音发生器,而不是使用现有的噪音发生器,如Excellent模块化?只是为了学习。我以后一定会检查这个库的源代码。我知道原始噪声有一些限制,比如不完全各向同性,但它不应该像我的例子中的边缘块那样明显。甚至在他的网站上(noisemachine.com)噪声图像并不明显,这是经典柏林噪声的一个常见缺陷。至少,他关于单纯形噪声的论文中包含了一个Java参考实现。我将在将来尝试重新编写它。感谢后续工作。我成功地犯了同样的错误(仅使用正向量分量生成梯度)现在我觉得有点傻。