WebGLで地形編集ツールを作る


UnityのTrrainのように、グレースケールを高さに置き換える方法を使えば、
2Dの地形図も簡単に作れるはず。

これをWebGLとシェーダーで作ってみた。
[DEMO] http://tashinoso.xyz/mapgen/

つまり……

これを、

こうして……

こうじゃ!

シェーダーの記述

#define R_LUMINANCE 0.298912
#define G_LUMINANCE 0.586611
#define B_LUMINANCE 0.114478

precision mediump float;
varying vec2 vTextureCoord;
varying vec2 vFilterCoord;
uniform sampler2D uSampler;
uniform sampler2D uTex;

const vec3 monochromeScale = vec3(R_LUMINANCE, G_LUMINANCE, B_LUMINANCE);

void main(void) {
  vec4 color = texture2D(uSampler, vTextureCoord);
  float grayColor = dot(color.rgb, monochromeScale);

  vec4 color2 = texture2D(uTex, vec2(grayColor, 0.5));
  gl_FragColor = color2;
}

ほぼテンプレ通りのグレースケール変換シェーダー。

いじったのはこの部分だけ。

  vec4 color2 = texture2D(uTex, vec2(grayColor, 0.5));
  gl_FragColor = color2;

グレースケール値 = X軸の値として、
高度カラー用のテクスチャ画像から、出力カラーを抽出している。

狙い通り、左の画像にこのシェーダーでフィルタをかけると、
地図(地形図)状に描画された。

編集ツール化への道

シェーダー部分はあっさり済んだものの、これを編集ツール化する過程でいくつか悩んだ。

canvasを2つ重ねている

ベースになるグレースケール画像を編集する方法は、
黒と白のブラシ画像を用意して、ベースに重ねて描画していけば簡単に済む。

ただ、この編集中のcanvasにシェーダーを反映する方法が分からなくて、結局、
canvasAで編集 → canbasBにテクスチャとして読込むという方法を取っている。

今のcanvasが2つある状態は、いかにも鈍臭いと思っているのだが、さてどうしたものか。

ブラシの反映が遅い

canvasAで編集、canvasBにテクスチャ読込、という流れになっているため、
ブラシドラッグ時の連続更新が、canvasBに反映されるのが遅い。
遅いので、今はドラッグ終了時にcanvasBに反映されるように制限している。
canvasAを表示してみると、なめらかにブラシが描画されているのだが。

ベースとなるノイズ画像の自動生成を諦めた

地図用に欲しいのは、Photoshopの「雲模様」で作る画像。

パーリンノイズを使えば、こういうノイズ画像を作れるらしいのだが、
求めている画像を生成できなかったので、ひとまず諦めた。
(ヘビがのたくったような画像になる)

代わりにPhotoshopのバッチ処理で30枚ほど画像を用意した。

Three.js を諦めて Pixi.js へ

シェーダーを作る際に、高度カラー用のテクスチャ画像を詠込む箇所が上手くいかず、
やむなく Pixi.js に乗り換えた。
なんでだったんだろ?

ソースコード

ソースコード一式、画像も含めて置いております。
https://github.com/tashinoso/another-map

MITライセンスということにしていますので、好きに使ってください。

たぶん、今回一番難しかったのは、コードを書くことよりも、
高さ用のカラーとか、ちょうどいいブラシ画像を作るのに試行錯誤することだったと思うので。