フラクタル地形

先日THREE.jsでのポリゴンとテクスチャの使い方を覚えたので、なんか面白い使い道ないかなと検索して「フラクタル地形」というのを見つけました。

Wikipediaによると

http://upload.wikimedia.org/wikipedia/commons/6/6d/Animated_fractal_mountain.gif

正方形を4つの同じ大きさの小さい正方形に分割し、その中心の点を無作為な値で垂直方向に変位させる。この過程を4つに分けられた各正方形についても繰り返し、再帰的に実施して必要な詳細レベルに到達するまで行う。

ということらしく、このたった2文とアニメーションGIFでだいたい理解できる程度には簡単そうだったのでやってみました(説明とアニメーションGIFが合ってないけどまぁ言いたいとは分かる・・・)。

おお、たしかに山っぽい。

もちろんリロードするとまた別の地形が生成されます。

ソース見てないけどTHREE.jsについてくるこれとかもたぶん同じ事やってるんじゃないかな。

・・・

今回特にTHREE.js的な見所はほとんどないんですが、ざっと解説?すると

  var hights = [];
  var size = 256;
  for (var y = 0; y <= size; y++) {
    var line = [];
    for (var x = 0; x <= size; x++) {
      line.push(0);
    }
    hights.push(line);
  }
  hights[0][0] = rand(size);
  hights[0][size] = rand(size);
  hights[size][0] = rand(size) + size / 3;
  hights[size][size] = rand(size) + size / 3;

  var u = size;
  do {
    u /= 2;
    for (var y = 0; y <= size; y += u * 2) {
      for (var x = u; x <= size; x += u * 2) {
        hights[y][x] = (hights[y][x-u] + hights[y][x+u]) / 2 + rand(u);
      }
    }
    for (var y = u; y <= size; y += u * 2) {
      for (var x = 0; x <= size; x += u * 2) {
        hights[y][x] = (hights[y-u][x] + hights[y+u][x]) / 2 + rand(u);
      }
    }
    for (var y = u; y <= size; y += u * 2) {
      for (var x = u; x <= size; x += u * 2) {
        hights[y][x] = (hights[y][x-u] + hights[y][x+u] + hights[y-u][x] + 
          hights[y+u][x]) / 4 + rand(u);
      }
    }
  } while (1 < u);

256x256の2次元配列を用意して、Wikipediaを参考に高さを設定して

  function initTerrain() {
    var geometry = new THREE.Geometry();
    for (var y = 0; y < hights.length; y++) {
      for (var x = 0; x < hights[y].length; x++) {
        geometry.vertices.push(new THREE.Vertex(new THREE.Vector3(y, hights[y][x], x)));
      }
    }

    for (var y = 0; y < hights.length - 1; y++) {
      for (var x = 0; x < hights[y].length - 1; x++) {
        geometry.faces.push(new THREE.Face3(
          x + y * hights.length, 
          x + 1 + y * hights.length, 
          x + (y + 1) * hights.length
        ));
        geometry.faceVertexUvs[0].push([
          new THREE.UV(x/size, y/size),
          new THREE.UV((x+1)/size, y/size),
          new THREE.UV(x/size, (y+1)/size)
        ]);
      }
    }
    for (var y = 1; y < hights.length; y++) {
      for (var x = 1; x < hights[y].length; x++) {
        geometry.faces.push(new THREE.Face3(
          x + y * hights.length, 
          x - 1 + y * hights.length, 
          x + (y - 1) * hights.length
        ));
        geometry.faceVertexUvs[0].push([
          new THREE.UV(x/size, y/size),
          new THREE.UV((x-1)/size, y/size),
          new THREE.UV(x/size, (y-1)/size)
        ]);
      }
    }
    geometry.computeFaceNormals();

    var texture = THREE.ImageUtils.loadTexture('Grass0133_9_S.jpg');
    var material = new THREE.MeshLambertMaterial({map:texture});
    var mesh = new THREE.Mesh(geometry, material);
    scene.add(mesh);
  }

ポリゴン作ってテクスチャ貼って表示。こないだと同じ。

二つ新しい、というか勉強になったのは

scene.fog = new THREE.FogExp2(0xccccff, 0.003);

シーンの遠景の色をぼかすことができるFogの存在を知ったことと

    geometry.computeFaceNormals();

この法線ベクトル計算してくれるメソッドの存在。前になんか作ったときはそんなん知らなかったから自分でやっちゃったよ・・・。

ということで、あまり綺麗じゃないですが、ソースは公開しているのでなにか参考にできることでもあればご自由にどうぞ。

https://github.com/technohippy/threejs-toys/tree/master/fractal-landscape