水面のような揺らぎを与えるエフェクトを作成してみた。
※枠内クリックで実行
ソースはこんな感じ
package {
import flash.display.*;
import flash.events.Event;
import flash.events.TimerEvent;
import flash.filters.DisplacementMapFilter;
import flash.filters.DisplacementMapFilterMode;
import flash.geom.Point;
import flash.utils.Timer;
public class WaterSurface extends Sprite {
//ソースイメージ、ノイズイメージ、ビットマップ
public var sourceImage:BitmapData;
private var noiseImage:BitmapData;
private var softLightBitmap:Bitmap;
private var bitmap:Bitmap;
//ノイズのパラメータ
public var basex:uint;
public var basey:uint;
public var octaves:uint;
public var seed:uint;
public var offsetArray:Array;
public var speedArray:Array;
//置き換えマップの移動強度
public var filterScale:uint;
private var filterScaleTemp:uint;
//減衰が始まるまでの時間(ms)
public var delayTime:Number;
//減衰の時間(ms)
public var decayTime:Number;
//減衰値
private var decayValue:Number;
private var softLightDecayValue:Number;
//減衰フラグ
private var decayFlag:Boolean;
//コンストラクタ(エフェクトをかけるBitmapData、フィルタの強さ、減衰までの時間、減衰の時間、水面の光を入れるか)
public function WaterSurface(g:BitmapData, _fs:uint = 20, _delay:Number = 1000, _decay:Number = 1000, _softlightFlag:Boolean = true )
{
sourceImage = g;
init(_fs,_delay,_decay);
bitmap = new Bitmap(sourceImage);
addChild(bitmap);
softLightBitmap = new Bitmap();
if (_softlightFlag) {
softLightBitmap.blendMode = BlendMode.HARDLIGHT;
softLightBitmap.alpha = 0.5;
addChild(softLightBitmap);
}
}
//初期化
private function init(_fs,_delay,_decay):void
{
basex = 100;
basey = 40;
octaves = 2;
seed = 0;
filterScale = _fs;
filterScaleTemp = filterScale;
delayTime = _delay;
decayTime = _decay;
decayFlag = false;
initOffsetArray(octaves);
}
//オフセットを初期化
private function initOffsetArray(octaves:int)
{
offsetArray = new Array();
speedArray = new Array();
if ( octaves > 0 )
{
for ( var i:int = 0; i < octaves; i++ )
{
offsetArray.push(new Point(0, 0))
speedArray.push(new Point(
Math.floor(Math.random() * 4 + 4),
Math.floor(Math.random() * 4 + 4) ));
}
}
}
//実行
public function start() {
decayValue = filterScale / (stage.frameRate * decayTime/1000)
softLightDecayValue = softLightBitmap.alpha / (stage.frameRate * decayTime/1000);
var timer:Timer = new Timer(decayTime, 1);
timer.addEventListener(TimerEvent.TIMER_COMPLETE,startDecayHandler)
timer.start();
addEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
//減衰をはじめる関数
private function startDecayHandler(e:TimerEvent):void
{
decayFlag = true;
}
//EnterFrame
private function enterFrameHandler(e:Event):void
{
for (var i:int = 0; i < octaves; i++) {
offsetArray[i].x += speedArray[i].x;
offsetArray[i].y += speedArray[i].y;
}
updatePerlinNoise();
updateBitmap();
if (decayFlag) {
filterScaleTemp -= decayValue;
softLightBitmap.alpha -= softLightDecayValue;
}
if (filterScaleTemp == 0) {
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
filterScaleTemp = filterScale;
decayFlag = false;
}
}
//ノイズを生成する
private function updatePerlinNoise():void
{
noiseImage = new BitmapData(sourceImage.width, sourceImage.height, false, 0x00000000);
noiseImage.perlinNoise(basex, basey, octaves, seed, false, true, 2, true, offsetArray);
softLightBitmap.bitmapData = noiseImage;
}
//ソースイメージにフィルタをかける
private function updateBitmap():void
{
var filter:DisplacementMapFilter = getBitmapFilter();
bitmap.filters = [filter]
}
//置き換えフィルタを得る
private function getBitmapFilter():DisplacementMapFilter {
var mapPoint:Point = new Point(0, 0);
var channels:uint = 2
var componentX:uint = channels;
var componentY:uint = channels;
var scaleX:Number = filterScaleTemp;
var scaleY:Number = filterScaleTemp;
var mode:String = DisplacementMapFilterMode.CLAMP;
var color:uint = 0;
var alpha:Number = 0;
return new DisplacementMapFilter(noiseImage,
mapPoint,
componentX,
componentY,
scaleX,
scaleY,
mode,
color,
alpha);
}
}
使い方は
obj:WaterSurface = new WaterSurface(BitmapData, FilterScale, DelayTime, DecayTime,SoftlightFlag);
addChild(obj);
obj.start()
といった感じ。引数は(ビットマップデータ、エフェクトの強さ、減衰するまでの時間(ms)、減衰時間(ms)、水面のテカリの有無(bool))
プログラムの流れは
- PerlinNoiseを発生させる(発生ごとにオフセットで位置をずらしてアニメーション。)
- それを元に置き換えフィルタを作成し、ソースの画像にかける。
- 1.へ戻る
- filterScaleが0になれば終了
I couldn’t resist commenting. :)