当前位置: 首页 > 知识库问答 >
问题:

“块状”柏林噪声

夏侯涵映
2023-03-14

我最近一直在尝试用C实现一个Perlin噪声发生器(基于Ken Perlin的网站,使用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;
}

用法:跑步时,按住上下键可放大或缩小噪声网格。

共有2个答案

于嘉许
2023-03-14

这是柏林噪声最初实现的问题。

他这儿有篇论文

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

解决此问题的一种方法是将坐标空间从0变为1,而不是从0变为512。

另一种方法是按照论文中的描述实现修复。

或者最后,不要使用最初的柏林噪声,而是换成他也开发的单纯形噪声,这里有纸,这里有解释。

羊舌承
2023-03-14

我终于找到了问题所在:梯度生成器。

我假设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,导致分形和不再工作,并且生成的图像具有相同的块瑕疵。

 类似资料:
  • 我使用柏林噪声生成2D高度图。起初,我手动尝试了一些参数,并为我的工作找到了振幅、持久性...的良好组合。 现在我正在开发该程序,我为用户添加了更改贴图参数并为自己制作新贴图的功能,但现在我发现某些参数(主要是倍频程和频率)的值不在我以前看到的范围内。我认为,如果设定的振幅=20,我从中得到的值(高度)将在[0,20]或[-10,10]或[-20,20]范围内,但现在我看到振幅不是控制输出范围的唯

  • 我想让3D Perlin噪波算法适应较低的维度,但我在梯度函数方面遇到了问题,因为我不完全理解推理。 原始的Perlin梯度函数采用四个参数:一个和一个三维坐标。函数的结果基于的值返回,如下所示。 : : : : : : : : : : : : : : : : 从0到11的返回值构成了一种模式,因为每个组合只表示一次。然而,最后四个是重复的。为什么选择它们来拟合最后四个返回值?有两个维度(x,y)

  • 我正在研究一些相干噪声的各种实现(我知道有库,但这主要是为了我自己的启发和好奇心)以及如何使用它,我对最初的Perlin噪声有一个问题。 根据这个经常链接的数学常见问题,输出范围将介于-1和1之间,但我不明白该值是如何在该范围内的。 据我所知,算法基本上是这样的:每个网格点都有一个相关的长度为1的随机梯度向量。然后,对于每个点,对于所有四个周围的网格点,计算随机梯度和从该网格点出发的向量的点积。然

  • 我有一个柏林噪声函数,我想用它来为我的游戏挑选生物群落地图。问题是,生物群落是由两个因素决定的——平均降雨量和平均温度。所以,我想,我只需要做两个柏林噪声函数,然后重叠它们。 现在的问题是,生物群落并不包括所有可能的降水温度组合。例如,没有高降雨量和低温的生物群落,如图所示。 我怎么能仍然使用柏林噪声,但永远无法到达生物群落未覆盖的区域?

  • 问题内容: 我正在docker容器中的纱线模式下使用本教程的火花群集在纱线模式下的火花群集中启动齐柏林飞艇。但是我陷入了第4步。我在Docker容器中找不到conf / zeppelin- env.sh来进行进一步的配置。我尝试将这些齐柏林飞艇的conf文件夹放入,但是现在成功了。除了那个齐柏林飞艇笔记本电脑也没有运行在本地主机上:9001。 我对分布式系统非常陌生,如果有人可以帮助我以纱线模式在

  • 已尝试执行 投掷: hiveDriver class java.lang.ClassNotFoundException java.net.urlClassLoader.FindClass(urlClassLoader.java:381)java.lang.ClassLoader.LoadClass(classLoader.java:424)sun.misc.launcher$appClassLoa