DiaplacementMapFilter の計算を実感してみた
/**
* Copyright Aquioux ( http://wonderfl.net/user/Aquioux )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/hKSI
*/
package {
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.Bitmap;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "#000000")]
/**
* DiaplacementMapFilter の計算を実感してみた
* @see http://aquioux.net/blog/?p=1802
* 写真:写真素材 足成【フリーフォト、無料写真素材サイト】 http://www.ashinari.com/
* @author YOSHIDA, Akio (Aquioux)
*/
public class Main extends Sprite {
// マウス座標
private var mousePosition_:MousePosition;
// perlinNoise
private var perlinNoiseHelper_:PerlinNoiseHelper;
// 描画計算
private var calculator_:Calculator;
// 表示
private var viewer_:Viewer;
// テキスト表示(displacement map 状態)
private var tf_:TextField;
// コンストラクタ
public function Main() {
var url:String = "http://assets.wonderfl.net/images/related_images/6/63/631e/631e61b36c4a8c296a4291be5487a837d05d8910";
var loader:Loader = new Loader();
loader.load(new URLRequest(url), new LoaderContext(true));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
}
private function completeHandler(event:Event):void {
setup(event.target.loader);
addEventListener(Event.ENTER_FRAME, update);
stage.addEventListener(MouseEvent.CLICK, clickHandler);
}
// セットアップ
private function setup(loader:Loader):void {
// 変数定義
var sw:int = stage.stageWidth;
var sh:int = stage.stageHeight;
var imageWidth:int = loader.width;
var imageHeight:int = loader.height;
var intervalWidth:int = 3;
var intervalHeight:int = 3;
// イメージ取得
var imageBmd:BitmapData = new BitmapData(imageWidth, imageHeight);
imageBmd.draw(loader);
addChild(new Bitmap(imageBmd));
// マウス座標
mousePosition_ = new MousePosition(stage);
mousePosition_.range = 1.0;
// perlinNoise
perlinNoiseHelper_ = new PerlinNoiseHelper(imageWidth, imageHeight);
var mapBm:Bitmap = new Bitmap(perlinNoiseHelper_.bitmapdata);
addChild(mapBm);
mapBm.x = sw - mapBm.width;
// 描画計算
calculator_ = new Calculator();
calculator_.map = perlinNoiseHelper_.bitmapdata;
calculator_.image = imageBmd;
calculator_.setup(sw, sh, intervalWidth, intervalHeight);
// Viewer
viewer_ = new Viewer(sw, sh);
addChild(viewer_);
// テキスト表示
var size:int = 12;
tf_ = new TextField();
tf_.autoSize = TextFieldAutoSize.LEFT;
tf_.defaultTextFormat = new TextFormat("_typewriter", size, 0xFFFFFF);
tf_.x = size * 0.5;
tf_.y = sw - (size * 2);
addChild(tf_);
clickHandler(null);
}
// アップデート
private function update(e:Event):void {
// perlinNoise 更新
perlinNoiseHelper_.update(mousePosition_.positionX, mousePosition_.positionY);
// 描画計算更新
calculator_.update();
// 表示更新
viewer_.update(calculator_.data);
}
// マウスイベント
private function clickHandler(e:MouseEvent):void {
calculator_.flg = !calculator_.flg;
tf_.text = calculator_.state;
}
}
}
//package {
import flash.display.BitmapData;
import flash.geom.Point;
/**
* perlinNoise Helper
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class PerlinNoiseHelper {
// perlinNoise 対象 BitmapData
public function get bitmapdata():BitmapData { return bmd_; }
private var bmd_:BitmapData;
// 上記 bitmapData(bmd_)のサイズ
private var width_:int;
private var height_:int;
// perlinNoise で使う変数
private var offsets_:Array = [new Point(), new Point()];
private const OCTAVES:uint = offsets_.length;
/**
* コンストラクタ
* @param width perlinNoise を発生させる BitmapData の幅
* @param height 同高さ
*/
public function PerlinNoiseHelper(width:int, height:int):void {
width_ = width;
height_ = height;
bmd_ = new BitmapData(width_, height_, false, 0x000000);
}
/**
* 更新
* perlinNoise の更新
*/
public function update(offsetX:Number, offsetY:Number):void {
// offsets_ の更新
offsets_[0].x += offsetX;
offsets_[0].y += offsetY;
offsets_[1].x -= offsetX;
offsets_[1].y -= offsetY;
// perlinNoise の更新
bmd_.perlinNoise(width_, height_, OCTAVES, 0xFF, true, true, 1, true, offsets_);
}
}
//}
//package {
import flash.display.BitmapData;
/**
* 描画座標等を計算
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class Calculator {
// 現在のフラグに応じた displacementMap の状態
public function get state():String { return state_; }
private var state_:String = MISTAKE;
private static const CORRECT:String = "Correct displacementMap Logic";
private static const MISTAKE:String = "Mistake displacementMap Logic";
// displacementMap 計算切り替えフラグ
public function get flg():Boolean { return flg_; }
public function set flg(value:Boolean):void {
flg_ = value;
state_ = flg_ ? CORRECT : MISTAKE;
}
private var flg_:Boolean = false;
// 計算結果データ
public function get data():Vector.<int> { return data_; }
private var data_:Vector.<int>;
// displacementMap の map(perlinNoise)
public function set map(value:BitmapData):void { map_ = value; }
private var map_:BitmapData;
// 適用対象イメージ
public function set image(value:BitmapData):void { image_ = value; }
private var image_:BitmapData;
// displacementMap の強さ
private const DEGREE:int = 50;
// map サイズ
private var mapWidth_:int;
private var mapHeight_:int;
// interval サイズ
private var intervalWidth_:Number;
private var intervalHeight_:Number;
// 開始座標
private var offsetX_:Number;
private var offsetY_:Number;
/**
* 初期化
* @param drawWidth 描画領域の幅
* @param drawHeight 同高さ
* @param intervalWidth 水平方向のピクセル間距離
* @param intervalHeight 垂直方向の同
*/
public function setup(drawWidth:int, drawHeight:int, intervalWidth:int, intervalHeight:int):void {
if (!map_) new Error("読み取り対象の BitmapData が未指定です。");
intervalWidth_ = intervalWidth;
intervalHeight_ = intervalHeight;
mapWidth_ = map_.width;
mapHeight_ = map_.height;
offsetX_ = (drawWidth - (mapWidth_ - 1) * intervalWidth_) / 2;
offsetY_ = (drawHeight - (mapHeight_ - 1) * intervalHeight_) / 2;
data_ = new Vector.<int>(mapWidth_ * mapHeight_ * 3, true);
}
/**
* 更新
*/
public function update():void {
var outputX:Number;
var outputY:Number;
var outputColor:int;
var idx:int = 0;
for (var y:int = 0; y < mapHeight_; y++) {
for (var x:int = 0; x < mapWidth_; x++) {
var b:uint = map_.getPixel32(x, y) & 0xFF; // perlinNoise の青成分値
// displacementMap 計算用座標値を計算
var calcX:Number = x + ((b - 128) * DEGREE) / 256;
var calcY:Number = y + ((b - 128) * DEGREE) / 256;
if(flg){
// displacementMap 計算
outputX = x * intervalWidth_ + offsetX_;
outputY = y * intervalHeight_ + offsetY_;
outputColor = image_.getPixel32(calcX, calcY);
} else {
// anti displacementMap 計算
// データ格納
outputX = calcX * intervalWidth_ + offsetX_;
outputY = calcY * intervalHeight_ + offsetY_;
outputColor = image_.getPixel32(x, y);
}
data_[idx++] = outputX >> 0;
data_[idx++] = outputY >> 0;
data_[idx++] = outputColor;
}
}
}
}
//}
//package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.geom.Rectangle;
/**
* Viewer
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class Viewer extends Bitmap {
// BitmapData 操作に使う Rectangle
private var rect_:Rectangle;
/**
* コンストラクタ
* @param width 表示領域の幅
* @param height 同高さ
*/
public function Viewer(width:int, height:int) {
rect_ = new Rectangle(0, 0, width, height);
this.bitmapData = new BitmapData(width, height, true, 0xFF000000);
this.smoothing = true;
}
/**
* 更新
* @param data 描画座標データ
*/
public function update(data:Vector.<int>):void {
this.bitmapData.lock();
this.bitmapData.fillRect(rect_, 0x00000000);
var len:int = data.length;
for (var i:int = 0; i < len; i += 3) {
var xx:int = data[i];
var yy:int = data[i + 1];
var color:uint = data[i + 2];
this.bitmapData.setPixel32(xx, yy, color);
}
this.bitmapData.unlock();
}
}
//}
//package {
import flash.display.Stage;
/**
* 対象ステージの中心を基準としたマウスカーソル位置を返す。
* 値は絶対値(座標値)または相対値(±range の範囲)を返す。
* @author YOSHIDA, Akio (Aquioux)
*/
/*public*/ class MousePosition {
// モード
public static const ABSOLUTE:String = "absolute"; // 絶対値
public static const RELATIVE:String = "relative"; // 相対値
public var mode:String = RELATIVE; // 既定値は「相対値」
// モードが相対値の場合の範囲
public function set range(value:Number):void { range_ = value; }
private var range_:Number = 0.5; // 既定値は "0.5"
// 慣性を使うか否か
public var isDelay:Boolean = false; // 既定値は「使わない」
// 対象ステージ
private var stage_:Stage;
// 対象ステージの中心座標値
private var centerX_:Number; // X座標値
private var centerY_:Number; // Y座標値
// 慣性を使うときに必要となる値
// 速度
private var vx_:Number = 0; // X座標値
private var vy_:Number = 0; // Y座標値
// 前回のマウスカーソル位置(絶対値)
private var prevX:Number = 0; // X座標値
private var prevY:Number = 0; // Y座標値
// 物理定数
private const SPRING:Number = 0.05; // バネ係数
private const FRICTION:Number = 0.9; // 摩擦係数
/**
* コンストラクタ
* @param stage マウス基準ステージ
*/
public function MousePosition(stage:Stage) {
stage_ = stage;
// 対象ステージの中心座標値算出
centerX_ = stage_.stageWidth / 2;
centerY_ = stage_.stageHeight / 2;
}
/**
* 現在のマウスカーソル位置(X軸側の値)
* @return Number
*/
public function get positionX():Number {
// 現在座標値の取得
var nowX:Number = stage_.mouseX - centerX_;
// 慣性使用
if (isDelay) {
vx_ += (nowX - prevX) * SPRING;
vx_ *= FRICTION;
prevX = nowX = vx_ + prevX;
}
// mode による振り分け
if (mode == RELATIVE) nowX *= range_ / centerX_;
return nowX;
}
/**
* 現在のマウスカーソル位置(Y軸側の値)
* @return Number
*/
public function get positionY():Number {
// 現在座標値の取得
var nowY:Number = stage_.mouseY - centerY_;
// 慣性使用
if (isDelay) {
vy_ += (nowY - prevY) * SPRING;
vy_ *= FRICTION;
prevY = nowY = vy_ + prevY;
}
// mode による振り分け
if (mode == RELATIVE) nowY *= range_ / centerY_;
return nowY;
}
}
//}