Heightmap Generation

Noise Generation

Language: C++

This code sample demonstrate perlin noise generation. The final result is the sum of several coherent noises of varying frequencies and scale.

Code sample

Header

namespace noiselib{
 
    /**
     * Return a value in a discrete 2D white noise given by seed at a particular coordinate.
     * Consequently, the value is fully determined by x,y and seed.
     */
    double randNoiseDouble(int x, int y, int seed);
 
    /**
     * Return a value in a continuous 2D noise given by seed at a particular coordinate.
     * Consequently, the value is fully determined by x,y and seed.
     * The function use a gradient method to generate noise which is spatially 
     * pseudo-uniform and coherent.
     */
    double randGradientNoiseDouble(double x, double y, int seed);
 
    /**
     * Return a value in a 2D Perlin noise given by seed at a particular coordinate.
     * Consequently, the value is fully determined by x,y and seed.
     * The function basically sum several gradient noise at different frequencies.
     */
    double randPerlinNoise(double x, double y, int seed, int nbrOctave, double persistence);
}

Implementation

/**
 * These constants are used by the randNoiseDouble function
 */
const int NOISE_P_X = 1619;
const int NOISE_P_Y = 31337;
const int NOISE_P_SEED = 1013;
 
/**
 * A simple cos lookup table
 */ 
double cosValue[16] = {1.0,0.9238,0.7071,0.3826,0,-0.3826,-0.7071,-0.9238,
                      -1.0,-0.9238,-0.7071,-0.3826,0,0.3826,0.7071,0.9238};
 
/*
 * The dot product of two vector
 */
double dotProduct(double vx, double vy, double wx, double wy){
    return vx*wx+vy*wy;
}
 
/**
 * A weigth function for polynomial interpolation
 */ 
double easeCurve(double t){
    return 6*pow(t,5)-15*pow(t,4)+10*pow(t,3);
}
 
/**
 * Linear interpolation between two value
 */
double linearInterpolation(double x0, double x1, double t){
    return x0+(x1-x0)*t;
}
 
/**
 * Interpolate between four values, using ease curves.
 */ 
double biLinearInterpolation(double x0y0, double x1y0, double x0y1, double x1y1, double x, double y){
    double tx = easeCurve(x);
    double ty = easeCurve(y);
    double u = linearInterpolation(x0y0,x1y0,tx);
    double v = linearInterpolation(x0y1,x1y1,tx);
    return linearInterpolation(u,v,ty);
}
 
/**
 * Return a value in a discrete 2D white noise given by seed at a particular coordinate.
 * Consequently, the value is fully determined by x,y and seed.
 */
double noiselib::randNoiseDouble(int x, int y, int seed){
  int n = (
      NOISE_P_X    * x
    + NOISE_P_Y    * y
    + NOISE_P_SEED * seed)
    & 0x7fffffff;
  n = (n >> 13) ^ n;
  n = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
  return 1.0 - (double)n/1073741824;
}
 
/**
 * Return a value in a continuous 2D noise given by seed at a particular coordinate.
 * Consequently, the value is fully determined by x,y and seed.
 * The function use a gradient method to generate noise which is spatially 
 * pseudo-uniform and coherent.
 */
double noiselib::randGradientNoiseDouble(double x, double y, int seed){
    //Get the top left border coordinate
    int x0 = (x > 0.0? (int)x: (int)x - 1);
    int y0 = (y > 0.0? (int)y: (int)y - 1);
 
    //Local coordinate
    double xl = x-x0;
    double yl = y-y0;
 
    //We associate a vector with each corner, by computing a random angle.
    //We use an integer value in order to use the cos lookup table, cosValue.
    //The vector are dependant of x and y. This is essential in order to obtain a coherent noise.
    int n00 = (int)((randNoiseDouble(x0, y0, seed)+1)*8);
    int n10 = (int)((randNoiseDouble(x0+1, y0, seed)+1)*8);
    int n01 = (int)((randNoiseDouble(x0, y0+1, seed)+1)*8);
    int n11 = (int)((randNoiseDouble(x0+1, y0+1, seed)+1)*8);
 
    //Compute a value for each corner using dot product between the corner vectors and the local coordinates
    double s = dotProduct(cosValue[n00], cosValue[(n00-4)%16], xl, yl);
    double u = dotProduct(-cosValue[n10], cosValue[(n10-4)%16], 1.0-xl, yl);
    double v = dotProduct(cosValue[n01], -cosValue[(n01-4)%16], xl, 1.0-yl);
    double w = dotProduct(-cosValue[n11], -cosValue[(n11-4)%16], 1.0-xl, 1.0-yl);
 
    //Interpolate the value
    return biLinearInterpolation(s,u,v,w,xl,yl);    
}
 
/**
 * Return a value in a 2D Perlin noise given by seed at a particular coordinate.
 * Consequently, the value is fully determined by x,y and seed.
 * The function sum several gradient noise at different frequencies.
 */
double noiselib::randPerlinNoise(double x, double y, int seed, int nbrOctave, double persistence){
    //We start from a 0 value.
    double px = 0.0;
 
    //Then add the noises
    for(int oct = 0; oct<nbrOctave; oct++){
        double xr = x * pow(2.0,oct);
        double yr = y * pow(2.0,oct);
        px = px + pow(persistence,oct) * randGradientNoiseDouble(xr,yr,seed+oct);
    }
    return px;
}
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License