« 2006年02月 | Main | 2006年04月 »

2006年03月20日

波紋エフェクト - DisplacementMapFilter

結構スタンダードなエフェクトだと思いますけど、一応のせときます。perlinNoiseで移動させた画像を置き換えマップに使用しただけ。マウス位置で変化量が変わります。他のフィルタと組み合わせて立体感を出すつもりでしたが、フィルタを重ねるとどうしても重くなってしまうので断念。ま、これだけでも充分重い気がしますけど。

数値を調整したり他のエフェクトと組み合わせれば、陽炎や磨りガラスなどの効果も可能ですが、モーションをつける場合は、ちょっと大きい描画領域に適用すると重くなってしまうので、なかなか使いどころが難しいです。

ちなみに、DisplacementMapFilterを適用する画像は表示領域よりも少し大きめの画像を用意する必要があります。そうしないと、フィルタを適用した際、アウトラインがはみ出してしまってかっこ悪いのです。

DisplacementMapFilterについては以前のエントリー(ゆらゆら揺れる文字 - DisplacementMapFilter)を参考に。

import flash.display.*;
import flash.filters.*;
import flash.geom.*;

var w:Number = 360;
var h:Number = 240;
var octaves:Number = 3;
var offset:Array = new Array();
var speed:Array = new Array();
var rdm:Number = Math.floor(Math.random()*10000);

// -- MC "image"をステージ上に配置。

var image:MovieClip;
var bmp:BitmapData = new BitmapData(w, h, false, 0);
var mappoint:Point = new Point((image._width-w)/2, (image._height-h)/2);

// -- perlinNoise 移動量設定

for (var i:Number = 0; i < octaves; i++) {
	offset[i] = new Point(Math.random()*w, Math.random()*h);
	speed[i] = new Point(Math.random()*2-1, Math.random()*2-1);
}

// -- エフェクト適用

this.onEnterFrame = function ():Void {
	for(var i:Number = 0; i < octaves; i++){
		offset[i].x += speed[i].x;
		offset[i].y += speed[i].y;
	}
	//  マウス位置によって変化量を調整
	var scalex:Number = (this._xmouse-w/2)/5;
	var scaley:Number = (this._ymouse-h/2)/5;

	bmp.perlinNoise(w/30, h/30, octaves, rdm, false, true, 1, true, offset);
	var dmf:DisplacementMapFilter = 
	new DisplacementMapFilter(bmp, mappoint, 1, 1, scalex, scaley);
	image.filters = [dmf];
}

2006年03月14日

渦巻きトランジッション - threshold

スクリプトの内容は前回のエントリー(紙が燃えていくようなトランジッション)とほぼ同様ですが、今後も使い回せると思うのでClassファイルにしておきました。数値なども少しだけ変更を加えています。

前回はperlinNoiseでしきい値を計るソースイメージを生成していましたが、今回はあらかじめ用意した画像を読み込む形にしています。これで画像を入れ替えるだけでまったく違った効果ができてしまうわけです。

今回はPhotoshopで作った下の画像を使用。

マスク用イメージ

黒い部分から白い部分にかけて消えていきます。この場合、中心から外側にかけて白を強くしているので、中心からイメージが切り替わるようになります。

ちなみに今回は1回目と2回目でマスク用のソース画像を反転させているため、逆再生になっています。

Transition.as

import flash.display.*;
import flash.filters.*;
import flash.geom.*;

class Transition {
	
	private var W:Number;
	private var H:Number;
	
	private var blur:BlurFilter;
	private var glow:GlowFilter;

	private var src_bmp:BitmapData;
	private var mask1_bmp:BitmapData;
	private var mask2_bmp:BitmapData;
	
	private var image1:MovieClip;
	private var image2:MovieClip;
	private var mc1:MovieClip;
	private var mc2:MovieClip;
	private var gmc:MovieClip;
	private var disp:MovieClip;
	
	function Transition (p:MovieClip, m1:MovieClip, m2:MovieClip, id:String) {
		
		W = m1._width;
		H = m1._height;
		image1 = m1;
		image2 = m2;

		blur = new BlurFilter(1.5, 1.5, 2);
		glow = new GlowFilter(0xFFFFFF, 100, 8, 8, 3, 2, false, true);
		
		src_bmp = BitmapData.loadBitmap(id);
		mask1_bmp = new BitmapData(W, H, true, 0xFFFFFFFF);
		mask2_bmp = new BitmapData(W, H, true, 0xFFFFFFFF);
		
		mc1 = p.createEmptyMovieClip("mc1", 0);
		mc2 = p.createEmptyMovieClip("mc2", 1);
		gmc = p.createEmptyMovieClip("g_mc", 2);
		disp = p.createEmptyMovieClip("d_mc", 3);
		
		mc1.attachBitmap(mask1_bmp, 0);
		mc2.attachBitmap(mask2_bmp, 0);
		gmc.attachBitmap(mask1_bmp, 0);
		
		gmc.blendMode = "overlay";
		gmc.filters = [glow];
		mc1.filters = [blur];
		mc2.filters = [blur];
		
		p.onLoad = function ():Void {
			mc1.cacheAsBitmap = true;
			mc2.cacheAsBitmap = true;
			image1.cacheAsBitmap = true;
			image2.cacheAsBitmap = true;
			image1.setMask(mc1);
			image2.setMask(mc2);
		}
		p.onLoad();
	}
	
	public function changeImage(m:MovieClip):Void {
		
		var mask:BitmapData;
		var m2:MovieClip;
		var thr:Number = 0;
		var b:BitmapData = src_bmp;
		var clr:ColorTransform = new ColorTransform(-1, -1, -1, 1, 255, 255, 255, 0);

		if (m == image1) { 
			mask = mask1_bmp;
			m2 = image2; 
		} else {
			mask = mask2_bmp;
			m2 = image1; 
		}
		gmc.attachBitmap(mask, 0);
		
		m.onEnterFrame = function():Void {
			if (thr > 0xCFFFFF) {
				mask.fillRect(mask.rectangle, 0xFFFFFFFF);
				m.attachBitmap(mask);
				thr = 0x000000;
				m.swapDepths(m2);
				b.colorTransform(b.rectangle, clr);
				delete m.onEnterFrame;
			} else {
				thr += (0xFFFFFF-thr)*0.03;
				mask.threshold(b, b.rectangle, new Point(0, 0), "<=", thr, 0, 0x00FFFFFF , false);
			}
		}
	}
	
}

1フレーム目


// -- ステージ上にMC image1, image2を配置。
// -- 現在のパス、画像MC1、画像MC2、マスク用画像のid (リンケージ設定で書き出しておく)

var ts:Transition = new Transition(this, image1, image2, "imageID");

image1.onRelease = function():Void{
	ts.changeImage(image1);
}

image2.onRelease = function():Void{
	ts.changeImage(image2);
}

2006年03月09日

紙が燃えていくようなトランジッション - threshold/perlinNoise

perlinNoiseで生成した雲模様ビットマップをアルファチャンネルマスクのしきい値を計るソースイメージとして使用し、thresholdメソッドを使ってしきい値の値を黒(0x000000)から白(0xFFFFFF)に近づけ、しきい値以下をアルファ(0x00000000)に置き換えていくことで、こんな感じに。しきい値の数値が一定に達したところで処理を停止し、画像の深度を入れ替えています。

今回、焦点がずれるので2つの画像は外部から読み込まずにステージ上にMCを配置しました。画像を外部から読み込む形にする場合、loadClipを使って読み込み処理を終えたことを確認してからsetMaskでマスク処理、という手順が必要なのでご注意を。

import flash.display.*;
import flash.filters.*;
import flash.geom.*;

var w:Number = 360;
var h:Number = 240;
var thr:Number = 0x000000;
var rdm:Number = Math.floor(Math.random()*10000);

// -- image1, image2 はステージ上に配置。 ( image1を上に配置)

var image1:MovieClip;
var image2:MovieClip;

var blur:BlurFilter = new BlurFilter(1.2, 1.2, 2);
var glow:GlowFilter = new GlowFilter(0x4C2E00, 100, 8, 8, 4, 2, true, true);

var bmp:BitmapData = new BitmapData(w+20, h+20, false, 0);
var mask_bmp:BitmapData = new BitmapData(w+20, h+20, true, 0xFFFFFFFF);
var mask2_bmp:BitmapData = new BitmapData(w+20, h+20, true, 0xFFFFFFFF);

var mc:MovieClip = this.createEmptyMovieClip("mc", 0);
var mc2:MovieClip = this.createEmptyMovieClip("mc2", 1);
var gmc:MovieClip = this.createEmptyMovieClip("g_mc", 3);

mc._x = mc._y = -10;
mc2._x = mc2._y = -10;
gmc._x = gmc._y = -10;

mc.attachBitmap(mask_bmp, 0);
mc2.attachBitmap(mask2_bmp, 0);
gmc.attachBitmap(mask_bmp, 0);

gmc.blendMode = "multiply";
gmc.filters = [glow];
mc.filters = [blur];
mc2.filters = [blur];

// -- アルファチャンネルマスクとして使用するため、cacheAsBitmapを有効に。

mc.cacheAsBitmap = true;
mc2.cacheAsBitmap = true;
image1.cacheAsBitmap = true;
image2.cacheAsBitmap = true;

image1.setMask(mc);
image2.setMask(mc2);

// -- トランジッション処理 

function burnImage(mask:BitmapData, m:MovieClip):Function {
	gmc.attachBitmap(mask, 0);
	return function():Void {
		if (thr > 0xCFFFFF) {
			mask.fillRect(mask.rectangle, 0xFFFFFFFF);
			m.attachBitmap(mask);
			thr = 0x000000;
			if (m == image1) { m2 = image2; }
			else { m2 = image1; }
			m.swapDepths(m2);
			delete this.onEnterFrame;
		} else {
			thr += (0xFFFFFF-thr)*0.03;
			mask.threshold(bmp, bmp.rectangle, new Point(0, 0), "<=", thr, 0, 0x00FFFFFF , false);
		}
	}
}

// -- 雲模様画像生成 

function createPerlinNoise():Void {
	rdm = Math.floor(Math.random()*10000);
	bmp.perlinNoise(100, 100, 5, rdm, false, true, 1, true);
}

// -- ボタン処理 

image1.onRelease = function():Void{
	createPerlinNoise();
	this.onEnterFrame = burnImage(mask_bmp, image1);
}

image2.onRelease = function():Void{
	createPerlinNoise();
	this.onEnterFrame = burnImage(mask2_bmp, image2);
}