Question

I've created a Perlin noise function. My issue is when I generate a terrain map from the noise it ends up blocky, nothing like the cloudlike images I've seen. I'm curious on what I'm doing wrong (if anything). here's my code:

main.cpp

#include "PerlinNoise.h"
#include <stdio.h>
#include <SDL/SDL.h>

void DrawPixel(SDL_Surface *screen, int x, int y, Uint8 R, Uint8 G, Uint8 B);
SDL_Surface *Render_Noise(int w, int h, int r, int g, int b);
PerlinNoise noiseGen(2,.5,25);

int main(int argc, char **argv)
{
    SDL_Init(SDL_INIT_EVERYTHING);
    SDL_Surface *screen = SDL_SetVideoMode(500,500,32,SDL_SWSURFACE);
    SDL_Surface *noise = Render_Noise(500,500,255,255,255);

    SDL_SaveBMP(noise, "noise.bmp");

    bool quit = false;

    while(!quit)
    {
        SDL_Event e;

        SDL_WaitEvent(&e);

        switch(e.type)
        {
        case SDL_QUIT:
            quit = true;
        }

        SDL_BlitSurface(noise,NULL,screen,NULL);
        SDL_Flip(screen);
        SDL_Delay(2000);
    }

    SDL_FreeSurface(noise);
    SDL_Quit();

    return 0;
}

void DrawPixel(SDL_Surface *screen, int x, int y, Uint8 R, Uint8 G, Uint8 B)
{
    Uint32 color = SDL_MapRGB(screen->format, R, G, B);

    if(SDL_MUSTLOCK(screen))
    {
        if(SDL_LockSurface(screen) < 0)
        {
            return;
        }
    }

    switch(screen->format->BytesPerPixel)
    {
    case 1:
        {
        Uint8 *bufp;

        bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;
        *bufp = color;
        }
        break;

    case 2:
        {
        Uint16 *bufp;
        bufp = (Uint16 *)screen->pixels + y*screen->pitch/2 + x;
        *bufp = color;
        }
        break;

    case 3:
        {
        Uint8 *bufp;

        bufp = (Uint8 *)screen->pixels + y*screen->pitch + x;
        *(bufp+screen->format->Rshift/8) = R;
        *(bufp+screen->format->Bshift/8) = B;
        *(bufp+screen->format->Gshift/8) = G;
        }
        break;

    case 4:
        {
        Uint32 *bufp;

        bufp = (Uint32 *)screen->pixels + y*screen->pitch/4 + x;
        *bufp = color;
        }
        break;
    }

    if(SDL_MUSTLOCK(screen))
    {
        SDL_UnlockSurface(screen);
    }
    SDL_UpdateRect(screen, x, y, 1, 1);
}

SDL_Surface *Render_Noise(int w, int h, int r, int g, int b)
{
    SDL_Surface *ret = SDL_CreateRGBSurface(SDL_SWSURFACE,w,h,32,0,0,0,0);          //create an empty image

    for(int y = 0; y < h; y++)
    {
        for(int x = 0; x < w; x++)
        {
            double getnoise = 0;
            for(int a = 0; a < noiseGen.n; a++)
            {
                getnoise += noiseGen.generateNoise(x,y);
                noiseGen.z = rand() % 100;
            }

            getnoise / noiseGen.n;

            int color = (int)((getnoise * 128.0) + 128.0);      //convert noise to 0-256 value

            if (color > 255)
                color = 255;
            if (color < 0)
                color = 0;

            DrawPixel( ret, x, y, (int)((r/255.0) * (double)color), (int)((g/255.0) * (double)color), (int)((b/255.0) * (double)color) );
        }
    }
    return ret;
}

perlinnoise.cpp

#include "PerlinNoise.h"

PerlinNoise::PerlinNoise(int octaves, double persistence, int zoom)
{
    p = persistence;
    n = octaves - 1;
    z = zoom;
}

PerlinNoise::~PerlinNoise()
{

}

///<summary>Gets a random number using x and y as seeds </summary>
///<param name = x> A double value </param>
///<param name = y> A double value </param>
///<returns> A random number between -1.0 and 1.0 </returns>
inline double PerlinNoise::noise(double x, double y)
{
    int n = x + y * 57; 
    n = (n << 13) ^ n;

    return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}

///<summary> Smooths out noise values </summary>
///<param name = x> a double value </param>
///<param name = y> a double value </param>
///<returns> a smoothed value between -1.0 and 1.0 </returns>
double PerlinNoise::smoothNoise(double x, double y)
{
    double corners = (noise(x+1, y+1) + noise(x+1,y-1) + noise(x-1,y+1) + noise(x-1,y-1)) / 16;
    double sides = (noise(x,y+1) + noise(x,y-1) + noise(x+1,y) + noise(x-1,y)) / 8;
    double center = noise(x,y) / 4;

    return corners + sides + center;
}

///<summary> Cosine Interpolation </summary>
///<param name = a> The low value to be interpolated </param>
///<param name = b> The high value to be interpolated </param>
///<param name = x> A value between -1.0 and 1.0 </param>
///<returns> Interpolated value between a and b </returns>
double PerlinNoise::cosineInterpolation(double a, double b, double x)
{
    double ft = x * 3.1415927;
    double f = (1.0 - cos(ft)) * .5;

    return a * (1.0 - f) + b * f;
}

///<summary> Gets smoothed noise values and interpolates them </summary>
///<param name = x> a double value </param>
///<param name = y> a double value </param>
///<returns> a value between -1 and 1 that's been smoothed and interpolated </returns>
double PerlinNoise::interpolatedNoise(double x, double y)
{
    double integer_x = (int)x;
    double fractional_x = x - integer_x;

    double integer_y = (int)y;
    double fractional_y = y - integer_y;

    double v1 = smoothNoise(integer_x, integer_y);
    double v2 = smoothNoise(integer_x + 1, integer_y);
    double v3 = smoothNoise(integer_x, integer_y + 1);
    double v4 = smoothNoise(integer_x + 1, integer_y + 1);

    double inter1 = cosineInterpolation(v1, v2, fractional_x);
    double inter2 = cosineInterpolation(v3, v4, fractional_x);

    return cosineInterpolation(inter1, inter2, fractional_y);
}

double PerlinNoise::generateNoise(double x, double y)
{
    double total = 0;

    for(int i = 0; i < n; i++)
    {
        frequency = pow(2.0,i);
        amplitude = pow(p,i);

        total = total + interpolatedNoise(x * frequency / z, y * frequency / z) * amplitude;
    }

    return total;
}
Était-ce utile?

La solution

Found the problem, my persistence was much lower than it should be, upping that I got the cloudlike image I expected

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top