某ウル○○Qみたいなエフェクト
displacementMapフィルターを使って某ウル○○Qのようなエフェクトを作りました。
これまた結構前に(flash8が出たころ)作ったものなんですが
日の目を見てなかったので晒しときます。
当初、例の音が付いてたのですが、都合によりカットです(笑)
テキストは書き換えられます(日本語しか埋め込んでません)
テキスト以外の部分を左右にドラッグすると巻き戻し、早送りが出来ます。
以下、解説です。
displacementMapフィルターは本来画像を画像に作用させて
レンズのゆがみのような画像効果を得るための物なのですが、
フィルタ画像の各ドットのRGB要素の値によって
対象画像のドットを上下左右に移動するというな実装をされているため、
逆に移動したいドットの距離からRGB値を計算で割り出して
フィルタ画像を作ってやれば狙った形に画像をゆがませることが出来ます。
そんな事をわざわざするのは面倒くさくてややこしいのですが、
setPixel()で1ドットづつ描画するのに比べて
フィルタの処理は段違いに速いので、自分の場合、
ハデで大きな範囲が動く演出を作る時には、この技を多用してます。
(そのようにdisplacementMapフィルターを使う人はけっこういるようです。)
このウル○○Qエフェクトの場合は、左右別々に渦を巻く変形を
させるためのフィルタ画像を128枚ずつ用意しています。
(画像一覧)
最初はActionScript2でフィルタ画像も毎フレームリアルタイムで作成していたのですが
setPixel()メソッドは遅いので、アニメーション自体も遅くなってしまいました。
なので、このサンプルの場合は、vb(.NET)を使って事前にフィルタ画像を256枚作成し
それをflash8に素材として読み込みました。
(仕組み上、ロスレス圧縮で書き出さないと、意図しないRGB値となり、とんでもない画面になります…。)
ActionScript3なら全部リアルタイム演算でいけるかもしれません。
(いやそもそもフィルタなんぞ使わないでも、いい速度がでるのかも…)
◆asソース
毎フレームdisplacementMapフィルターを適用させています
import flash.display.*;
import flash.geom.*;
import flash.filters.*;
//
var self = this;
var mc:MovieClip;
var width = 80;//フィルタのサイズ
var height = 120;//フィルタのサイズ
var multiple:Number = 2;//フィルタの倍率
var noMoveCol:Number = 128 + (128<<8);//フィルタがかからない色(緑:横方向、青:縦方向)
var mapBitmap:BitmapData = new BitmapData(480,360,false,noMoveCol);
var displacementMap:DisplacementMapFilter = new DisplacementMapFilter(mapBitmap, new Point(0,0), 4, 2, 256*multiple, 256*multiple,"clamp");
var blurFilter:BlurFilter = new BlurFilter(2,2);
var workBitmapL:BitmapData;
var workBitmapR:BitmapData;
//フィルタの変形用
var offsetX = 0;
var offsetY = 0;
var rect1:Rectangle = new Rectangle(0,0,320,240);
var rect2:Rectangle = new Rectangle(0,0,320,240);
var matrix1:Matrix = new flash.geom.Matrix();
var matrix2:Matrix = new flash.geom.Matrix();
matrix1.scale(multiple,multiple);
matrix2.scale(multiple,multiple);
matrix1.translate(offsetX,offsetY);
matrix2.translate(160+offsetX,offsetY);
var ctr:ColorTransform = new ColorTransform();
//アニメパターン番号
var maxAnimNum:Number = 128;
var j:Number = maxAnimNum;//カウンタ
//他、演出用
var bDrag:Boolean = false;
var bt:Button;
//var sn:Sound;
/**
* 初期化
*/
function init(){
var fArr:Array = mc.filters;
fArr.unshift(blurFilter);
fArr.unshift(displacementMap);
mc.filters = fArr;
this.onEnterFrame = animation;
animation();
this.stop();
}
/**
* ライブラリのビットマップを返す
* @param アニメパターン番号
* @param rl 右か左か("r"||"l")
* @return ビットマップデータ
*/
function getBitmaps(i:Number,rl:String):BitmapData{
var bmp:BitmapData = BitmapData.loadBitmap("qBitmap"+i+"_"+rl+".png");
return bmp;
}
/**
* マイフレームのアニメーション処理
*/
function animation(){
//左右にドラッグできる
if(bDrag){
j = Math.min(maxAnimNum-1,Math.floor(_xmouse/(320/maxAnimNum)));
}
workBitmapL.dispose();
workBitmapR.dispose();
workBitmapL = getBitmaps(j,"l");
workBitmapR = getBitmaps(j,"r");
mapBitmap.draw(workBitmapL,matrix1,ctr,"normal",rect,false);
mapBitmap.draw(workBitmapR,matrix2,ctr,"normal",rect,false);
if(--j <=0){
j=0;
}
//アルファをいじるとフィルタ設定しなおさなくても描画が更新する
mc._alpha = (mc._alpha==100)? 101: 100;
tf.text= "frame "+j;
}
//ドラッグ処理
bt.onPress = function(){
self.bDrag = true;
}
bt.onRelease = bt.onReleaseOutside = function(){
self.bDrag = false;
self.playSound();
}
init();
◆画像128枚x左右2セット作成のVisualBasic.NETのソース
初めて(で最後)のVB。専門外のため適当です。専門でも適当ですが…。
Imports System.Drawing.Bitmap
Imports System.Drawing.Imaging
'Option Strict On
Public Class Form1
Private _width As Integer = 80
Private _height As Integer = 120
Private _files As Integer = 128
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim image1 As New Bitmap(_width, _height)
Dim image2 As New Bitmap(_width, _height)
Dim count As Integer = _files - 1
Dim x, y As Integer
Dim theta As Double = 0.0
_initBar()
Dim newColor As Color
While count >= 0
For x = 0 To image1.Width - 1
For y = 0 To image1.Height - 1
newColor = _getCol(x, y, count, +1)
image1.SetPixel(x, y, newColor)
newColor = _getCol(x, y, count, -1)
image2.SetPixel(x, y, newColor)
Next
Next
PictureBox1.Image = image1
PictureBox2.Image = image2
PictureBox1.Refresh()
PictureBox2.Refresh()
saveImage(count, image1, "l")
saveImage(count, image2, "r")
_setBar(_files - count)
count = count - 1
End While
ProgressBar1.Value = 0
End Sub
Private Sub _initBar()
ProgressBar1.Minimum = 0
ProgressBar1.Maximum = _files
ProgressBar1.Step = 1
End Sub
Private Sub _setBar(ByVal num As Integer)
ProgressBar1.Value = num
End Sub
Private Sub saveImage(ByVal count As Integer, ByVal image As Bitmap, ByVal lr As String)
Dim filename As String = TextBox1.Text & CStr(count) & "_" & lr & ".png"
image.Save(filename, ImageFormat.Png)
Console.WriteLine(filename + "を書き出し")
End Sub
Private Function _getCol(ByVal x As Integer, ByVal y As Integer, ByVal i As Integer, ByVal dir As Double) As Color
Dim red As Double = 0
Dim green As Double = 0
Dim blue As Double = 0
Dim speed As Double = 1.0
Dim cx As Double = _width * 0.5
Dim cy As Double = _height * 0.5
Dim dx As Double = (x - cx)
Dim dy As Double = (y - cy)
Dim nx As Double
Dim ny As Double
Dim r As Double = Math.Sqrt(dx * dx + dy * dy)
Dim theta As Double
Dim bAll As Boolean = True
Dim newColor As Color
If r = 0 Then
r = 0.1
End If
Dim rRatio As Double = Math.Max(0,0.07*speed*i*(1 - (Math.Sqrt(r/(cY*1.1))) ))
Dim bInSide As Boolean = (r < cx And r < cy)
If (Not bAll) And (Not bInSide) Then
newColor = Color.FromArgb(0, 128, 128)
Else
theta = Math.Atan2(-dy, dx)
nx = (cx + Math.Cos(theta - dir * rRatio) * r) - x
ny = (cy - Math.Sin(theta - dir * rRatio) * r) - y
blue = Math.Max(0, Math.Min(nx + 128, 255))
green = Math.Max(0, Math.Min(ny + 128, 255))
newColor = Color.FromArgb(CInt(red), CInt(green), CInt(blue))
End If
Return newColor
End Function
End Class
コメント一覧