In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

DiaplacementMapFilter の計算を実感してみた

Get Adobe Flash player
by Aquioux 11 Aug 2011
/**
 * 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;
        }
    }
//}