Question

So I'm trying to generate perlin noise and save it to an image file. I've got the image being saved correctly but the noise doesn't really look like perlin noise.

Here's my code:

package com.egs.survivalsim.util;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import com.egs.survivalsim.MainComponent;

public class Noise {

    MainComponent main;

    public double noise(int x,int y){
        x = x + y * 57;
        x = ((x << 13) ^ x);
        double t = (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff;
        return 1 - t * 0.000000000931322574615478515625;
    }

    public double sNoise(int x, int y){
        double corners = (noise(x - 1, y - 1) + noise(x + 1, y - 1) + noise(x - 1, y + 1) + noise(x + 1, y + 1)) * 0.0625;
        double sides = (noise(x - 1, y)  + noise(x + 1, y) + noise(x, y - 1) + noise(x, y + 1))  * 0.125;
        double center = noise(x, y) * 0.25;
        return corners + sides + center;        
    }

    public double lInterpoleLin(double a, double b, double x){
        return a * (1 - x) + b * x;     
    }

    public double lInterpoleCos(double a, double b, double x){
        double ft = x * 3.1415927;
        double f = (1 - Math.cos(ft)) * 0.001;
        return a * (1 - f) + b * f;
    }

    public double iNoise(double x, double y){
        int iX = (int) x;
        int iY = (int) y;
        double dX = x - iX;
        double dY = y - iY;
        double p1 = sNoise(iX, iY);
        double p2 = sNoise(iX + 1, iY);
        double p3 = sNoise(iX, iY + 1);
        double p4 = sNoise(iX + 1, iY + 1);
        double i1 = lInterpoleCos(p1 ,p2 ,dX);
        double i2 = lInterpoleCos(p3, p4, dX);
        return lInterpoleCos(i1, i2, dY);   
    }   

    public double pNoise(double x, double y, double persistence, int octave){
        double result;
        double amplitude = 1;
        int frequence = 1;
        result = 0;
        for(int n = 0; n < octave; n++){
            frequence <<= 1;
            amplitude *= persistence;
            result += iNoise(x * frequence, y * frequence) * amplitude;
        }
        return result;  
    }

    public void startNoise(MainComponent main){
        System.out.println("Generating noise map");
        this.main = main;
        System.out.println("Width: " + main.worldWidth);
        System.out.println("Height: " + main.worldHeight);
        BufferedImage image = new BufferedImage(main.worldWidth, main.worldHeight, BufferedImage.TYPE_INT_RGB);
        for(int y = 0; y < main.worldHeight; y++){
            for(int x = 0; x < main.worldWidth; x++){
                double c = pNoise((double) x - main.worldWidth, (double) y - main.worldHeight, 0.2, 2);

                c *= 128.0;
                c += 127.0;
                if(c > 255.0){
                    c = 255.0;
                }
                if(c < 0.0){
                    c = 0.0;
                }

                int r = (int) c;
                int g = (int) c;
                int b = (int) c;

                if(c>128)
                     r>>=1;
                    if(c>128)
                     b>>=1;


                int color = new Color(r, g, b).getRGB();

                image.setRGB(x, y, color);
                main.noiseArray[x][y] = (int) c;
            }
        }

        File fileImage = new File("noise.png");
        try {
            ImageIO.write(image, "png", fileImage);
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("Noise map generated");
    }
}
Was it helpful?

Solution

No, indeed this looks a bit strange. First, there seems to be an issue with the creation of your noise. Second, I don't really know why you are creating a color image. Quickest possible solution is to have a look at the latest publication of Ken Perlin and check out his reference implementation for it.

With a simple copy&paste and a few lines of extra java-code, you get a nice looking image.

enter image description here

Please note that I really just hacked it down, meaning I scaled the sampling-range manually so that it looks good and since I had no idea in which range the values of the Perlin-noise is, I simply re-scaled the array to fit into the range [0, 255].

In the code below, only the main is important. The rest is copied from Perlin.

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class Noise {

  static final int p[] = new int[512], permutation[] = {151, 160, 137, 91, 90, 15,
    131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
    190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
    88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166,
    77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244,
    102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196,
    135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123,
    5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
    223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
    129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228,
    251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107,
    49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
    138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
  };

  static {
    for (int i = 0; i < 256; i++) p[256 + i] = p[i] = permutation[i];
  }

  static public double noise(double x, double y, double z) {
    int X = (int) Math.floor(x) & 255,                  // FIND UNIT CUBE THAT
      Y = (int) Math.floor(y) & 255,                  // CONTAINS POINT.
      Z = (int) Math.floor(z) & 255;
    x -= Math.floor(x);                                // FIND RELATIVE X,Y,Z
    y -= Math.floor(y);                                // OF POINT IN CUBE.
    z -= Math.floor(z);
    double u = fade(x),                                // COMPUTE FADE CURVES
      v = fade(y),                                // FOR EACH OF X,Y,Z.
      w = fade(z);
    int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z,      // HASH COORDINATES OF
      B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;      // THE 8 CUBE CORNERS,

    return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z),  // AND ADD
      grad(p[BA], x - 1, y, z)), // BLENDED
      lerp(u, grad(p[AB], x, y - 1, z),  // RESULTS
        grad(p[BB], x - 1, y - 1, z))),// FROM  8
      lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1),  // CORNERS
        grad(p[BA + 1], x - 1, y, z - 1)), // OF CUBE
        lerp(u, grad(p[AB + 1], x, y - 1, z - 1),
          grad(p[BB + 1], x - 1, y - 1, z - 1))));
  }

  static double fade(double t) {
    return t * t * t * (t * (t * 6 - 15) + 10);
  }

  static double lerp(double t, double a, double b) {
    return a + t * (b - a);
  }

  static double grad(int hash, double x, double y, double z) {
    int h = hash & 15;                      // CONVERT LO 4 BITS OF HASH CODE
    double u = h < 8 ? x : y,                 // INTO 12 GRADIENT DIRECTIONS.
      v = h < 4 ? y : h == 12 || h == 14 ? x : z;
    return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
  }

  public static void main(String[] args) {
    final int WIDTH = 2 * 256, HEIGHT = 256;

    double[] data = new double[WIDTH * HEIGHT];
    int count = 0;

    for (int y = 0; y < HEIGHT; y++) {
      for (int x = 0; x < WIDTH; x++) {
        data[count++] = PerlinNoise.noise(20.0 * x / WIDTH, 10.0 * y / HEIGHT, 0);
      }
    }

    double minValue = data[0], maxValue = data[0];
    for (int i = 0; i < data.length; i++) {
      minValue = Math.min(data[i], minValue);
      maxValue = Math.max(data[i], maxValue);
    }

    int[] pixelData = new int[WIDTH * HEIGHT];
    for (int i = 0; i < data.length; i++) {
      pixelData[i] = (int) (255 * (data[i] - minValue) / (maxValue - minValue));
    }

    BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_BYTE_GRAY);
    img.getRaster().setPixels(0, 0, WIDTH, HEIGHT, pixelData);

    File output = new File("image.jpg");
    try {
      ImageIO.write(img, "jpg", output);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top