Kugelblitz

いつ何時誰の挑戦でも受ける!

Processingで地形の自動生成

地形イメージの自動生成を行うプログラムを作りました。こういったイメージを作成する目的には、Processingが楽でいいです。
結果は以下のようになりました。

地形の凸凹はパーリンノイズで決定して、上下左右の端に近い部分は無理やり高度を下げて海にしています。

クリックするたびに新しい地形がバンバン生成されるのですが、ぼんやり眺めていると、なんかこういろんな妄想が浮かんできますな。

プログラムソースは以下です。

int IMG_WIDTH = 2048;
int IMG_HEIGHT = 1024;
int imageViewX = 0;
int imageViewY = 0;
int imageViewWidth = IMG_WIDTH;
int imageViewHeight = IMG_HEIGHT;
// 数字が大きいほど、地形がのっぺりする
float SCALE = 100f;

void setup() {
  fullScreen();
  colorMode(RGB, 256, 256, 256);
  noLoop();

  // イメージ描画位置を決定
  if (width / IMG_WIDTH < height / IMG_HEIGHT) {
    imageViewWidth = width;
    imageViewHeight = IMG_HEIGHT * width / IMG_WIDTH;
    imageViewY = (height - imageViewHeight) / 2;
  } else {
    imageViewWidth = IMG_WIDTH * width / IMG_HEIGHT;
    imageViewHeight = height;
    imageViewX = (width - imageViewWidth) / 2;
  }
}

void draw() {
  long millis = millis();
  noiseSeed((int)millis);
  background(0, 0, 0);
  PImage img = createImage(IMG_WIDTH, IMG_HEIGHT, ARGB);
  img.loadPixels();
  for (float y = 0; y < img.height; y++) {
    for (float x = 0; x < img.width; x++) {
      float tmp = lerp(-200, 500, noise(x / SCALE, y / SCALE ));
      // 海抜に近いところはなだらかに。高度が高くなるほど急峻にする。
      if (tmp > 0) {
        float ratio = lerp(0f, 1f, pow(tmp, 0.1 ) / pow(500, 0.1));
        tmp *= ratio;
      }

      // 端に近いところは海にする
      float offsetW = pow(lerp(0, 100, abs(x - IMG_WIDTH / 2) / IMG_WIDTH), 1.5); 
      tmp -= offsetW;
      float offsetH = pow(lerp(0, 100, abs(y - IMG_HEIGHT / 2) / IMG_HEIGHT), 1.5); 
      tmp -= offsetH;

      tmp = constrain(tmp, -500, 500);
      img.pixels[(int)(x + (y * img.width))] = createColor(tmp);
    }
  }

  // 枠線描画
  for (float y = 32; y < img.height; y += 32) {
    for (float x = 0; x < img.width; x += 4) {
      img.pixels[(int)(x + (y * img.width))] =  color(30, 30, 30);
    }
  }
  for (float y = 0; y < img.height; y+= 4) {
    for (float x = 32; x < img.width; x += 32) {
      img.pixels[(int)(x + (y * img.width))] =  color(30, 30, 30)  ;
    }
  }

  for (float x = 0; x < img.width; x++) {
    int y = 0;
    img.pixels[(int)(x + (y * img.width))] = color(200, 200, 200);
    y = img.height - 1;
    img.pixels[(int)(x + (y * img.width))] = color(200, 200, 200);
  }

  for (float y = 0; y < img.height; y++) {
    int x = 0; 
    img.pixels[(int)(x + (y * img.width))] = color(200, 200, 200);
    x = img.width - 1;
    img.pixels[(int)(x + (y * img.width))] = color(200, 200, 200);
  }

  img.updatePixels();

  // 画像を表示
  image(img, imageViewX, imageViewY, imageViewWidth, imageViewHeight);  
   
  img.save("island-" + millis + ".png");
}

color createColor(float val) {
  color col;
  if (val < -50) {
    col = lerpColor(color(0, 0, 50), color(0, 0, 205), (val + 300) / 250f);
  } else if (val < 0) {
    col = lerpColor(color(0, 0, 205), color(65, 105, 237), (val + 50) / 50f);
    // ここまで海
  } else if (val < 250) {
    col = lerpColor(color(0, 101, 68), color(158, 154, 46), (val - 0) / 249f);
  } else {
    col = lerpColor(color(158, 154, 46), color(154, 108, 0), (val - 250) / 249f);
  }
  return col;
}

void mousePressed() {

  redraw();
}

Pocket

他の記事