概要
中点変位法で線を描くでは1次元の線を作成しましたが、今回は2次元に拡張してハイトマップを生成してみましょう。
アルゴリズム
ハイトマップをBitmap(Data)で作成するとします。
四隅のピクセルに適当な値を入れておきます。
var midPoint:Number = (tl + tr + bl + br) / 4 + getRandomHeight(size);
四隅から見て中央にあるピクセルに、平均値 + 変位させるランダムな値を入れます。
var top:Number = (tl + tr) / 2; // 上 = 左上と右上の平均値
var bottom:Number = (bl + br) / 2; // 下 = 左下と右下の平均値
var left:Number = (tl + bl) / 2; // 左 = 左上と左下の平均値
var right:Number = (tr + br) / 2; // 右 = 右上と右下の平均値
四隅の間のピクセル(中央の上下左右)に各平均値を入れます。
size /= 2;
generateHeightmap(x, y, size, tl, top, left, midPoint);
generateHeightmap(x + size, y, size, top, tr, midPoint, right);
generateHeightmap(x, y + size, size, left, midPoint, bl, bottom);
generateHeightmap(x + size, y + size, size, midPoint, right, bottom, br);
後は矩形を4分割(2*2)して再帰的に値を求めていきます。
public function getRandomHeight(value:Number):Number
{
// -0.5~0.5 -> -0.25~0.25 -> -0.125~0.125
return (Math.random() - 0.5) * value / size;
}
一次元の場合と同じように、変位させる値の範囲は分割するたび(サイズが小さくなるたび)に小さくさせます。
プログラム
コメント有り
|
+ | | ... |
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import frocessing.color.ColorHSV;
public class Main extends Sprite
{
private var bd:BitmapData; // ハイトマップの値を入れるBitmapData
private var max:Number = 1.0; // 値の最大値
private var min:Number = 0.0; // 値の最小値
private var size:int = 256; // ハイトマップのサイズ
private var minSize:int = 2; // minSize未満のサイズになったら分割を終了させる
private var color:ColorHSV = new ColorHSV();
public function Main()
{
bd = new BitmapData(size, size, false);
addChild(new Bitmap(bd));
stage.addEventListener(MouseEvent.CLICK, onMouseClick);
onMouseClick();
}
private function onMouseClick(event:MouseEvent = null):void
{
generateHeightmap(0, 0, size, Math.random(), Math.random(), Math.random(), Math.random());
}
// x, y = 左上座標
// size = 現在のサイズ(幅, 高さ)
// tl = 左上の値, tr = 右上の値, bl = 左下の値, br = 右下の値
public function generateHeightmap(x:int, y:int, size:int, tl:Number, tr:Number, bl:Number, br:Number):void
{
// minSize未満のサイズになったら分割を終了させる
if (size < minSize)
{
// 平均値を出す
var value:Number = (tl + tr + bl + br) / 4;
// RGB(value, value, value)にしてグレイ色に
color.gray(value * 256);
// (x, y)~(x + minSize, y + minSize)分だけ塗る
bd.fillRect(new Rectangle(x, y, x + minSize, y + minSize), color.value);
}
else
{
// 四隅から見て中央にあるピクセルに平均値 + 変位させるランダムな値を入れる
var midPoint:Number = (tl + tr + bl + br) / 4 + getRandomHeight(size);
// min~max(0.0~1.0)までの値に収まるように調整
if (midPoint < min) midPoint = min;
if (max < midPoint) midPoint = max;
// 中央から見て上下左右にあるピクセルにも平均値を入れる
var top:Number = (tl + tr) / 2; // 上 = 左上と右上の平均値
var bottom:Number = (bl + br) / 2; // 下 = 左下と右下の平均値
var left:Number = (tl + bl) / 2; // 左 = 左上と左下の平均値
var right:Number = (tr + br) / 2; // 右 = 右上と右下の平均値
// 2*2に分割するのでサイズ(幅, 高さ)を半分にする
size /= 2;
// 左上
generateHeightmap(x, y, size, tl, top, left, midPoint);
// 右上
generateHeightmap(x + size, y, size, top, tr, midPoint, right);
// 左下
generateHeightmap(x, y + size, size, left, midPoint, bl, bottom);
// 右下
generateHeightmap(x + size, y + size, size, midPoint, right, bottom, br);
}
}
public function getRandomHeight(value:Number):Number
{
// -0.5~0.5 -> -0.25~0.25 -> -0.125~0.125
return (Math.random() - 0.5) * value / size;
}
}
}
|
コメント無し
|
+ | | ... |
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import frocessing.color.ColorHSV;
public class Main extends Sprite
{
private var bd:BitmapData;
private var max:Number = 1.0;
private var min:Number = 0.0;
private var size:int = 256;
private var minSize:int = 2;
private var color:ColorHSV = new ColorHSV();
public function Main()
{
bd = new BitmapData(size, size, false);
addChild(new Bitmap(bd));
stage.addEventListener(MouseEvent.CLICK, onMouseClick);
onMouseClick();
}
private function onMouseClick(event:MouseEvent = null):void
{
generateHeightmap(0, 0, size, Math.random(), Math.random(), Math.random(), Math.random());
}
public function generateHeightmap(x:int, y:int, size:int, tl:Number, tr:Number, bl:Number, br:Number):void
{
if (size < minSize)
{
var value:Number = (tl + tr + bl + br) / 4;
color.gray(value * 256);
bd.fillRect(new Rectangle(x, y, x + minSize, y + minSize), color.value);
}
else
{
var midPoint:Number = (tl + tr + bl + br) / 4 + getRandomHeight(size);
if (midPoint < min) midPoint = min;
if (max < midPoint) midPoint = max;
var top:Number = (tl + tr) / 2;
var bottom:Number = (bl + br) / 2;
var left:Number = (tl + bl) / 2;
var right:Number = (tr + br) / 2;
size /= 2;
generateHeightmap(x, y, size, tl, top, left, midPoint);
generateHeightmap(x + size, y, size, top, tr, midPoint, right);
generateHeightmap(x, y + size, size, left, midPoint, bl, bottom);
generateHeightmap(x + size, y + size, size, midPoint, right, bottom, br);
}
}
public function getRandomHeight(value:Number):Number
{
return (Math.random() - 0.5) * value / size;
}
}
}
|
実行結果
flash on 2011-10-2 - wonderfl build flash online
画面をクリックするたびにハイトマップを生成します。