Mit dem BitmapData Objekt in Actionscript 3 kann man Farbwerte Pixel für Pixel auslesen und verändern, Filter anwenden etc. Wichtig sind hierbei die beiden Klassen
Die Pixelmanipultionen werden also in dem BitmapData Objekt vorgenommen. Das Bitmap Objekt enthält einen Verweis auf das BitmapData Objekt. Diese Pixelmanipultionen können sehr schnell berechnet werden und somit kann man hiermit spektakuläre Effekte erzielen.
Auf dieser Seite werden folgende Themen behandelt:
Threshold Schwellenwert / Andy Warhol Effekte
Perlin Noise / natürliche Muster
Displacement Map / Pixelverschiebungen, Fluid, Füssigkeitssimulationen
Convolution Filter / eigene Filter mit Matrix
Siehe auch meinen Tipp AS3 File Reference Klasse
Weitere Infos zum Thema Movieclips als png oder jpg speichern
Adobe Hilfe Arbeiten mit Bitmaps
Links zum Thema Perlin Noise
Threshold zu deutsch Schwellenwert ermöglicht in AS3 die Auswahl bestimmter Pixel, um diese zu manipulieren. Beispielsweise könnte man alle Pixel die dunkler sind als ein bestimmter Farbwert durchsichtig machen. Siehe dazu die Adobe Referenzen
Hier ein Tutorial zum Thema Threshold von sepiroth
Wichtig ist auch hier, dass die Manipulationen am BitmapData Objekt vorgenommen werden und nicht am Bitmap Objekt. Einfach ausgedrückt kann man sagen das Bild wird in einen hellen und einen dunklen Bereich aufgeteilt, so dass es nur noch 2 Farben gibt, eine helle und eine dunkle.
Im folgenden Beispiel habe ich eine Movieclip Klasse mit Klassennamen "Krikel" in der Bibliothek.
var bmpData:BitmapData=new BitmapData(200,100); bmpData.draw(new Krikel()); var bmp:Bitmap = new Bitmap(); bmp.bitmapData=bmpData; addChild(bmp); var rect:Rectangle=this.getRect(bmp); bmpData.threshold(bmpData, rect, new Point(0,0), ">", 0x333333, 0x00ff0000, 0x00ff0000, false);
Die Parameter von threshold sind nicht ganz einfach zu verstehen. Daher stelle ich hier ein Beispiel vor, an dem ich einige Dinge erkläre. Vorab ein Auszug der Adobe Referenzen:
Parameter
sourceBitmapData:BitmapData — Die als Eingabe verwendete Bitmap. Das Quellbild kann ein anderes BitmapData-Objekt oder ein Verweis auf die aktuelle BitmapData-Instanz sein. | |
sourceRect:Rectangle — Ein Rechteck, mit dem der Bereich des als Eingabe zu verwendenden Quellbilds definiert wird. | |
destPoint:Point — Der Punkt innerhalb des Zielbilds (die aktuelle BitmapData-Instanz), der der linken oberen Ecke des Quellrechtecks entspricht. | |
operation:String — Einer der folgenden Vergleichsoperatoren, der als String übergeben wird: "<", "<=", ">", ">=", "==", "!=" | |
threshold:uint — Der Wert, auf den jedes Pixel geprüft wird, um festzustellen, ob der Schwellenwert eingehalten oder überschritten wird. | |
color:uint (default = 0) — Der Farbwert, auf den ein Pixel gesetzt wird, falls der Schwellentest erfolgreich ist. Der Standardwert lautet 0x00000000. | |
mask:uint (default = 0xFFFFFFFF) — Die Maske, die zum Isolieren einer Farbkomponente verwendet wird. | |
copySource:Boolean (default = false) — Lautet der Wert true, werden Pixelwerte aus dem Quellbild auch in das Zielobjekt kopiert, wenn der Schwellentest fehlschlägt. Lautet der Wert false, wird das Quellbild nicht kopiert, wenn der Schwellentest fehlschlägt. Hier nun mein Beispiel es geht um folgende Situtation. Es gibt ein importiertes Bild als BitmapData Klasse namens "Baum" und eine Movieclip-klasse mit einem Farbverlauf namens "Himmel" in der Bibliothek. Es geht nun darum die Farbe des Himmels im Baum-bild durch die Farben des Verlaufs zu ersetzen.
|
Beide BitmapData Klassen werden instanziiert und jeweils eine Bitmap Klasse dazu. Welche mit addChild() der Display Liste hinzugefügt werden.
var bmpData:Baum=new Baum(0,0); var bmp:Bitmap = new Bitmap(); bmp.bitmapData=bmpData; addChild(bmp); var bmpData2:BitmapData=new BitmapData(525,700); bmpData2.draw(new Himmel()); var bmp2:Bitmap = new Bitmap(); bmp2.bitmapData=bmpData2; addChild(bmp2);
Hierbei fällt auf, dass das Verlaufsbild "Himmel" über dem Baum-Bild liegt und somit das Baumbild völlig verdeckt.
Es wird nun folgendes gemacht. Alle Pixel die dunkler sind als die hellen Pixel des Himmels im Baumbild werden mittels threshold ermittelt, oder besser gesagt deren Position wird ermittelt. Diese Pixel werden im "Himmel" Bild durch eine neu definierte Farbe ersetzt. Diese Farbe kann auch einen Alphawert haben.
var rect:Rectangle= new Rectangle(0,0,525,700); bmpData2.threshold(bmpData, rect, new Point(0,0), "<=", 0xdddfed, 0x0000262F, 0xaabbcc, false);
bmpData2 ist das BitmapData Objekt des Himmelsbildes. Dessen Pixel werden verändert. Der erste Parameter bezieht sich auf das BitmapData Objekt des Baum-Bildes, dessen Farbwerte sollen untersucht werden. Man könnte hier auch das gleiche BitmapData Objekt nehmen, aber das wäre ein anderes Beispiel. Es folgt ein Rechteck der Bereich in dem die Pixel untersucht werden. In diesem Falle ist das gesamte Bild. Der nächste Parameter ist ein Punkt, die linke obere Ecke. Dann folgt die Bedingung oder der Vergleichsoperator (siehe oben). Es folgt die Farbe auf die der Vergleichsoperator angewendet wird. Wenn der Alphawert 0 ist kann man ihn auch weglassen, da er am Anfang der Zahl steht. In diesem Beispiel werden alle Pixel die kleiner gleich sind als 0xdddfed ausgewählt. Kleiner heißt auch dunkler. Der nächste Parameter ist der Farbwert, den alle Pixel bekommen, die ermittelt wurden.
Folgendes Beispiel würde zu diesem Ergebnis führen.
var rect:Rectangle= new Rectangle(0,0,525,700); bmpData2.threshold(bmpData, rect, new Point(0,0), "<=", 0xdddfed, 0x0000262F, 0xaabbcc, false);
Hier wurde lediglich der Alphawert der zweiten Farbe den höchsten Wert gesetzt. In dem Falle wäre es auch nicht nötig das Baumbild der Anzeigenliste hinzuzufügen. Das BitmapData Object wäre völlig ausreichend. Man könnte also auf folgende Zeilen verzichten:
var bmp:Bitmap = new Bitmap();Der dritte Farbwert ist ein Parameter dessen Bedeutung sich mir nicht völlig erschlossen hat. Es hat aber etwas mit den Farbwerten zu tun, die für den Schwellenwert ermittelt werden. Wer mehr weiß, möge mir einen Link oder eine Email schicken.
Ihr könnt mit meinem Beispiel die Auswirkungen verschiedener Parameterwerte ausprobieren.
Hier nochmal der komplette Code des Beispiels:
var bmpData:Baum=new Baum(0,0); var bmp:Bitmap = new Bitmap(); bmp.bitmapData=bmpData; addChild(bmp); var bmpData2:BitmapData=new BitmapData(525,700); bmpData2.draw(new Himmel()); var bmp2:Bitmap = new Bitmap(); bmp2.bitmapData=bmpData2; addChild(bmp2); var rect:Rectangle= new Rectangle(0,0,525,700); bmpData2.threshold(bmpData, rect, new Point(0,0), "<=", 0xdddfed, 0x0000262F, 0xaabbcc, false);
Beispiel threshold / thold3.swf
Hier ein Beispiel, an dem man einige der Parameter ausprobieren kann.
Beispiel threshold / thold4.swf
In diesem Beispiel wird der Parameter: threshold mit einer Slider Komponente geändert.
Hier die Actions für dieses Beispiel. In der Bibliothek liegt ein importiertes Bild, welches als BitmapData Klasse mit dem Klassennamen Ich1 definiert wurde.
import flash.display.BitmapData import flash.geom.Point import flash.geom.Rectangle var bmpData1:BitmapData = new Ich1(0,0); var img1:Bitmap = new Bitmap(bmpData1); var bmpData2:BitmapData = new BitmapData(img1.width, img1.height, false); //var bmpData2:BitmapData = new Ich2(0,0); var img2:Bitmap = new Bitmap(bmpData2); var mc:MovieClip = new MovieClip(); //mc.addChild(img1); mc.addChild(img2); addChild(mc); function changeColor():void{ bmpData2.threshold(bmpData1, new Rectangle(0,0, img1.width, img1.height), new Point(0,0), ">=",(slider.value/100)*0xFFFFFF, 0xffa5a7b6, 0xffffff, true); } function onChange(evt:Event):void { changeColor(); } changeColor(); slider.addEventListener(Event.CHANGE, onChange);
Beispiel threshold / thold5.swf
Mit diesem Beispiel entsteht ein Andy Warhol Effekt, achtet darauf das der MovieClip Instanz "mc" bei jedem SliderEvent alle Instanzen gelöscht werden (falls vorhanden) und die jeweils neue Bitmap Instanz hinzugefügt wird.
import flash.display.BitmapData; import flash.geom.Point; import flash.geom.Rectangle; var bmpData1:BitmapData=new Ich1(0,0); var img1:Bitmap=new Bitmap(bmpData1); var mc:MovieClip = new MovieClip(); addChild(mc); function onChange(evt:Event):void { try { mc.removeChildAt(0); } catch (e:Error) { trace("noch keine Instanz vorhanden."); } var bmpData2:BitmapData=new BitmapData(img1.width,img1.height,true,0xff003333); var img2:Bitmap=new Bitmap(bmpData2); mc.addChild(img2); bmpData2.threshold(bmpData1, new Rectangle(0,0, img1.width, img1.height), new Point(0,0), ">=",(slider.value/100)*0xFFFFFF, 0xffa5a7b6, 0xffffff, false); } slider.addEventListener(Event.CHANGE, onChange);
Beispiel threshold / thold6.swf
Hier eine leicht geänderte Variante des vorigen Beispiels die Farbe von bmpData2 hat einen Transparenzwert von 70. Außerdem werden nur alle Instanzen von img2 gelöscht, wenn der Slider auf 0 steht (sofern vorhanden). Daher gibt es diese verschiedenen Schattierungen.
Außerdem habe ich im bmpData2.threshold() den Parameter "color" auch eine Transparenz von 70 zugewiesen. Würde der Wert auf FF stehen würde beim runter- oder zurückfahren des Sliders, die helle Farbe die dunklen Farben verdecken. Probiert es aus. 0x70a5a7b6 oder 0xFFa5a7b6
Also wird bei jeder Veränderung eine transparente dunkle Farbe und eine transparente helle Farbe über die schon vorhandenen Bilder gelegt.
import flash.display.BitmapData; import flash.geom.Point; import flash.geom.Rectangle; var bmpData1:BitmapData=new Ich1(0,0); var img1:Bitmap=new Bitmap(bmpData1); var mc:MovieClip = new MovieClip(); addChild(mc); function onChange(evt:Event):void { try { if (slider.value==0) { while (mc.numChildren>0) { mc.removeChildAt(mc.numChildren-1); } } } catch (e:Error) { trace("noch keine Instanz vorhanden."); } var bmpData2:BitmapData=new BitmapData(img1.width,img1.height,true,0x70003333); var img2:Bitmap=new Bitmap(bmpData2); mc.addChild(img2); bmpData2.threshold(bmpData1, new Rectangle(0,0, img1.width, img1.height), new Point(0,0), ">=",(slider.value/100)*0xFFFFFF, 0x70a5a7b6, 0xffffff, false); } slider.addEventListener(Event.CHANGE, onChange);
Mit AS3 besteht die Möglichkeit Noise und Perlin Noise Störungsfunktionen zu generieren.
import flash.display.Bitmap; import flash.display.BitmapData; var myBitmap:BitmapData = new BitmapData(250, 250,false, 0xff000000); myBitmap.noise(500, 0, 255, BitmapDataChannel.BLUE,false); var image:Bitmap = new Bitmap(myBitmap); addChild(image);
Perlin Noise erzeugt Muster, wie sie in ähnlicher Form auch in der Natur vorkommen. Es wurde 1982 von "Ken Perlin" entwickelt, der auch am Film "Tron" mitgearbeitet hat. Siehe dazu die Infos bei Adobe und die Referenzen bei Adobe
public function perlinNoise(baseX:Number, baseY:Number, numOctaves:uint, randomSeed:int, stitch:Boolean, fractalNoise:Boolean, channelOptions:uint = 7, grayScale:Boolean = false, offsets:Array = null)
import flash.display.Bitmap; import flash.display.BitmapData; var myBitmapDataObject:BitmapData = new BitmapData(150, 150, false, 0x00FF0000); var seed:Number = Math.floor(Math.random() * 100); var channels:uint = BitmapDataChannel.GREEN | BitmapDataChannel.BLUE myBitmapDataObject.perlinNoise(100, 80, 6, seed, false, true, channels, false, null); var myBitmap:Bitmap = new Bitmap(myBitmapDataObject); addChild(myBitmap);
Beispiel noise1.swf
var myBitmapDataObject:BitmapData=new BitmapData(550,550,false,0x00FF0000); var myBitmap:Bitmap=new Bitmap(myBitmapDataObject); addChild(myBitmap); var _offset:Number = 0; this.addEventListener(Event.ENTER_FRAME, changePic); function changePic(evt:Event):void { myBitmapDataObject.perlinNoise(100, 100, 2, 1, false, false,2, false, [new Point(_offset++, _offset)]); }
Beispiel noise10.swf
Hier könnt ihr die Parameter der Perlin Noise Klasse in Actionscript 3 ausprobieren
Beispiel noise3.swf
import flash.display.Bitmap; import flash.display.BitmapData; var myBitmapDataObject:BitmapData=new BitmapData(550,550,false,0x00FF0000); var myBitmap:Bitmap=new Bitmap(myBitmapDataObject); addChild(myBitmap); var _offset:Number = 0; this.addEventListener(Event.ENTER_FRAME, changePic); function changePic(evt:Event):void { myBitmapDataObject.perlinNoise(100, 100, 2, 1, false, false,2, true, [new Point(_offset++, _offset)]); myBitmapDataObject.threshold(myBitmapDataObject, myBitmapDataObject.rect, new Point(0,0), "<", 0x252525, 0xff000000, 0x00ffffff, false); }
Hier habe das voirge Beispiel noch etwas erweitert, allerdings braucht für eine ruckelfreie Darstellung einen einigermaßen schnellen Rechner
Beispiel noise5
import flash.display.Bitmap; import flash.display.BitmapData; var dbo0:BitmapData=new BitmapData(500,500,false,0x00FF0000); dbo0.perlinNoise(100, 100, 2, 1, false, false,2, true); var dbo1:BitmapData=new BitmapData(500,500,true,0x00FF0000); var dbo2:BitmapData=new BitmapData(500,500,true,0x0000ff00); var myBitmap0:Bitmap=new Bitmap(dbo0); var myBitmap1:Bitmap=new Bitmap(dbo1); var myBitmap2:Bitmap=new Bitmap(dbo2); addChild(myBitmap0); addChild(myBitmap1); addChild(myBitmap2); var _offset:Number=0; var _offset2:Number=5; this.addEventListener(Event.ENTER_FRAME, changePic); function changePic(evt:Event):void { dbo1.perlinNoise(100, 100, 2, 1, false, false, 1, true, [new Point(_offset+=0.5*-1, _offset*-1)]); dbo2.perlinNoise(100, 100, 2, 1, false, false, 2, true, [new Point(_offset+=0.5*-1, _offset2*-1)]); dbo1.threshold(dbo1, dbo1.rect, new Point(0,0), "<", 0x252525, 0x00000000, 0x00ffffff, false); dbo2.threshold(dbo2, dbo2.rect, new Point(0,0), "<", 0x252525, 0x00000000, 0x00ffffff, false); }
Beispiel noise6.swf
import flash.display.Bitmap; import flash.display.BitmapData; var dbo0:BitmapData=new BitmapData(500,500,false,0x00FF0000); dbo0.perlinNoise(100, 100, 2, 1, false, false,2, true); var dbo1:BitmapData=new BitmapData(500,500,true,0x00FF0000); var dbo2:BitmapData=new BitmapData(500,500,true,0x0000ff00); var myBitmap0:Bitmap=new Bitmap(dbo0); var myBitmap1:Bitmap=new Bitmap(dbo1); var myBitmap2:Bitmap=new Bitmap(dbo2); myBitmap1.blendMode = BlendMode.LIGHTEN; myBitmap2.blendMode = BlendMode.LIGHTEN; addChild(myBitmap0); addChild(myBitmap1); addChild(myBitmap2); var _offset:Number=0; var _offset2:Number=5; this.addEventListener(Event.ENTER_FRAME, changePic); function changePic(evt:Event):void { dbo1.perlinNoise(100, 100, 2, 1, false, false, 1, true, [new Point(_offset+=0.5*-1, _offset*-1)]); dbo2.perlinNoise(100, 100, 2, 1, false, false, 2, true, [new Point(_offset+=0.5*-1, _offset2)]); dbo1.threshold(dbo1, dbo1.rect, new Point(0,0), "<", 0x252525, 0x00000000, 0x00ffffff, false); dbo2.threshold(dbo2, dbo2.rect, new Point(0,0), "<", 0x252525, 0x00000000, 0x00ffffff, false); }
import flash.display.Bitmap; import flash.display.BitmapData; stage.scaleMode = StageScaleMode.NO_SCALE; var dbo0:BitmapData=new BitmapData(500,500,false,0x00FF0000); dbo0.perlinNoise(100, 100, 2, 1, false, false,2, true); var dbo1:BitmapData=new BitmapData(500,500,true,0x00FF0000); var dbo2:BitmapData=new BitmapData(500,500,true,0x0000ff00); var myBitmap0:Bitmap=new Bitmap(dbo0); var myBitmap1:Bitmap=new Bitmap(dbo1); var myBitmap2:Bitmap=new Bitmap(dbo2); myBitmap1.blendMode = BlendMode.LIGHTEN; myBitmap2.blendMode = BlendMode.DIFFERENCE; addChild(myBitmap0); addChild(myBitmap1); addChild(myBitmap2); var _offset:Number=0; var _offset2:Number=5; this.addEventListener(Event.ENTER_FRAME, changePic); function changePic(evt:Event):void { dbo1.perlinNoise(100, 100, 2, 1, false, false, 1, true, [new Point(_offset+=0.5*-1, _offset*-1)]); dbo2.perlinNoise(100, 100, 2, 1, false, false, 2, true, [new Point(_offset+=0.5*-1, _offset2)]); }
Im Hintergrund habe ich eine Verlauf von hellem Türkis nach hellem Blau.
import flash.display.Bitmap; import flash.display.BitmapD0ata; var ani:Sprite = new Sprite(); var dbo1:BitmapData=new BitmapData(500,500,true,0x00FF0000); var dbo2:BitmapData=new BitmapData(500,500,true,0x0000ff00); var myBitmap1:Bitmap=new Bitmap(dbo1); var myBitmap2:Bitmap=new Bitmap(dbo2); myBitmap1.blendMode=BlendMode.DIFFERENCE; myBitmap2.blendMode=BlendMode.DIFFERENCE; ani.blendMode=BlendMode.DIFFERENCE; ani.addChild(myBitmap1); ani.addChild(myBitmap2); addChild(ani); var _offset:Number=0; var _offset2:Number=5; this.addEventListener(Event.ENTER_FRAME, changePic); function changePic(evt:Event):void { dbo1.perlinNoise(80, 30, 2, 1, false, true, 1, true, [new Point(_offset+=2*-1, _offset*-1)]); dbo2.perlinNoise(80, 30, 2, 1, false, true, 2, true, [new Point(_offset2+=2*-1, _offset2)]); }
Hier das gleiche Prinzip, wie im vorigen Beispiel. Hier wurde lediglich der Movieclip 3d mäßig gekippt.
import flash.display.Bitmap; import flash.display.BitmapData; var ani:MovieClip = new MovieClip(); ani.cacheAsBitmap = true; addChild(ani); ani.x =0 ; ani.y = 100; ani.z = 30; ani.rotationX = -74; ani.rotationY = 0; ani.rotationZ = 0; var dbo1:BitmapData=new BitmapData(500,500,true,0x00FF0000); var dbo2:BitmapData=new BitmapData(500,500,true,0x0000ff00); var myBitmap1:Bitmap=new Bitmap(dbo1); var myBitmap2:Bitmap=new Bitmap(dbo2); myBitmap1.blendMode=BlendMode.DIFFERENCE; myBitmap2.blendMode=BlendMode.DIFFERENCE; ani.blendMode=BlendMode.DIFFERENCE; ani.addChild(myBitmap1); ani.addChild(myBitmap2); trace(ani.x+" "+ani.y+" "+ani.z+" "+ani.rotationX+" "+ani.rotationY+" "+ani.rotationZ+" "); var _offset:Number=0; var _offset2:Number=5; this.addEventListener(Event.ENTER_FRAME, changePic); function changePic(evt:Event):void { dbo1.perlinNoise(80, 30, 2, 1, false, true, 1, true, [new Point(_offset+=2*-1, _offset*-1)]); dbo2.perlinNoise(80, 30, 2, 1, false, true, 2, true, [new Point(_offset2+=2*-1, _offset2)]); }
Hier habe ich eine Perlin Noise Klasse erstellt, an der man alle Parameter eingeben kann, die außerdem ein Bild und einen BlendMode erwartet, mit dem das Bild über die PerlinNoise Animation gelegt wird.
Action der Klasse
package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.GradientType; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.filters.BitmapFilter; import flash.filters.DisplacementMapFilter; import flash.filters.DisplacementMapFilterMode; import flash.geom.Matrix; import flash.geom.Point; import flash.text.TextField; import flash.events.*; import flash.display.BlendMode; public class Perlin extends Sprite { private var bgColor:uint=0xFFCC00; private var picWidth:uint=200; private var picHeight:uint=200; private var _offset:Number=0; private var _offset2:Number=5; private var bitDat:BitmapData=new BitmapData(picWidth,picHeight,true,bgColor); private var bitmap:Bitmap=new Bitmap(); private var bild:BitmapData; private var baseX:Number; private var baseY:Number; private var numOctaves:uint; private var randomSeed:int; private var stitch:Boolean; private var fractalNoise:Boolean; private var channelOptions:uint; private var grayScale:Boolean; private var xSpeed:Number; private var xDir:Number; private var ySpeed:Number; private var yDir:Number; public function Perlin(_bild:BitmapData, _blend:String=BlendMode.LIGHTEN, _baseX:Number=100, _baseY:Number=10, _numOctaves:uint=5, _randomSeed:int=1, _stitch:Boolean=false, _fractalNoise:Boolean=false, _channelOptions:uint=1, _grayScale:Boolean=true, _xSpeed:Number=1, _xDir:Number=-1, _ySpeed:Number=0, _yDir:Number=-1):void { baseX=_baseX; baseY=_baseY; numOctaves=_numOctaves; randomSeed=_randomSeed; stitch=_stitch; fractalNoise=_fractalNoise; channelOptions=_channelOptions; grayScale=_grayScale; xSpeed=_xSpeed; xDir=_xDir; ySpeed=_ySpeed; yDir=_yDir; bild=_bild; picWidth=bild.width; picHeight=bild.height; var anzeigeBild:Bitmap = new Bitmap(); anzeigeBild.bitmapData=bild; addChild(bitmap); anzeigeBild.blendMode=_blend; addChild(anzeigeBild); addEventListener(Event.ENTER_FRAME, createFilter); }; private function createFilter(evt:Event):void { var bitDat:BitmapData=new BitmapData(picWidth,picHeight,true,bgColor); bitDat.perlinNoise(baseX, baseY, numOctaves, randomSeed, stitch, fractalNoise, channelOptions, grayScale, [new Point(_offset+=xSpeed*xDir, _offset+=ySpeed*yDir)]); bitmap.bitmapData=bitDat; } } }
Action der fla
var wasser:Sky= new Sky(0,0); var disp:Perlin= new Perlin(wasser, BlendMode.LIGHTEN, 170, 90, 6, 2, false, true, 1, true, 2, 1, 0, 0); addChild(disp); var landscape:Maske= new Maske(); addChild(landscape);
Displacement Map Adobe Referenzen
Wenn man perlinNoise mit der DisplacementMap Klasse von AS 3 kombiniert, kann man bewegliche Wasseroberflächenn simulieren. DisplacementMap funktioniert in etwa so. Man hat ein Quellbild dessen Pixel verschoben werden. Auf dieses Bild legt man ein anderes Bild die DisplacementMap. Jeder Pixel von DisplacementMap bestimmt anhand des Farb- bzw. Helligkeitswertes, wie stark der Pixel des Quellbildes, der darunter liegt verschoben wird. Das Ergebnis ist ein verändertes Bild. Siehe dazu auch meinen Photoshop Tipp.
DisplacementMapFilter / Konstruktor
public function DisplacementMapFilter(mapBitmap:BitmapData = null, mapPoint:Point = null, componentX:uint = 0, componentY:uint = 0, scaleX:Number = 0.0, scaleY:Number = 0.0, mode:String = "wrap", color:uint = 0, alpha:Number = 0.0)
Der Filtermodus. Zulässige Werte sind die DisplacementMapFilterMode-Konstanten:
Action der Klassendatei DisplacementMap
package { import flash.display.Bitmap; //import flash.display.BlendMode; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.GradientType; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.filters.BitmapFilter; import flash.filters.DisplacementMapFilter; import flash.filters.DisplacementMapFilterMode; import flash.geom.Matrix; import flash.geom.Point; import flash.text.TextField; import flash.events.*; public class DisplacementMap extends Sprite { private var bgColor:uint=0xFFCC00; private var picWidth:uint=200; private var picHeight:uint=200; private var bild:BitmapData; private var _offset:Number=0; private var _offset2:Number=5; private var bitDat:BitmapData=new BitmapData(picWidth,picHeight,true,bgColor); private var bitmap:Bitmap=new Bitmap(); public function DisplacementMap(_bild:BitmapData) { bild=_bild; picWidth=bild.width; picHeight=bild.height; var anzeigeBild:Bitmap = new Bitmap(); anzeigeBild.bitmapData=bild; addChild(anzeigeBild); this.addEventListener(Event.ENTER_FRAME, createFilter); } private function createFilter(evt:Event):void { var bitDat:BitmapData=new BitmapData(picWidth,picHeight,true,bgColor); bitDat.perlinNoise(100,10, 5, 1, false, false, 1, true, [new Point(_offset+=1*-1, _offset*-1)]); bitmap.bitmapData=bitDat; var mapBitmap:BitmapData=bitDat; var mapPoint:Point=new Point(0,0); var channels:uint=BitmapDataChannel.RED; var componentX:uint=channels; var componentY:uint=channels; var scaleX:Number=0.5; var scaleY:Number=-30; var mode:String=DisplacementMapFilterMode.CLAMP; var color:uint=0; var alpha:Number=0; var filter:BitmapFilter=new DisplacementMapFilter(mapBitmap,mapPoint,componentX,componentY,scaleX,scaleY,mode,color,alpha); filters=new Array(filter); } } }
Action in der fla
In der Bibliothek befindet sich ein importiertes Bild, welches den BitmapData Klassennamen "Fluss" bekommen hat. Außerdem habe ich das gleiche Bild ohne das Wasser in einen MC gelegt, der den Klassennamen "Maske" bekommen hat.
var wasser:Fluss= new Fluss(0,0); var disp:DisplacementMap= new DisplacementMap(wasser); addChild(disp); var landscape:Maske= new Maske(); addChild(landscape);
Das vorige Beispiel hat den Nachteil, dass die Perspektive nicht berücksichtigt wird. Das wird in diesem Beispiel geändert. Die Klassendatei hat 2 weitere Parameter bekommen. Die vertikale Position und die perspektivische Drehung.
package { import flash.display.Bitmap; //import flash.display.BlendMode; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.GradientType; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.filters.BitmapFilter; import flash.filters.DisplacementMapFilter; import flash.filters.DisplacementMapFilterMode; import flash.geom.Matrix; import flash.geom.Point; import flash.text.TextField; import flash.events.*; public class WaterMap1 extends Sprite { private var bgColor:uint=0xFFCC00; private var picWidth:uint=200; private var picHeight:uint=200; private var bild:BitmapData; private var _offset:Number=0; private var _offset2:Number=5; private var bitDat:BitmapData=new BitmapData(picWidth,picHeight,true,bgColor); private var bitmap:Bitmap=new Bitmap(); var picContainer:Sprite= new Sprite(); public function WaterMap1(_bild:BitmapData, yPos:int, xRotation:int) { bitmap.x=0; bitmap.y=yPos; bitmap.z=0; bitmap.rotationX=xRotation; bitmap.rotationY=0; picContainer.addChild(bitmap); bild=_bild; picWidth=bild.width; picHeight=bild.height; var anzeigeBild:Bitmap = new Bitmap(); anzeigeBild.bitmapData=bild; addChild(anzeigeBild); this.addEventListener(Event.ENTER_FRAME, createFilter); } private function createFilter(evt:Event):void { var bitDat1:BitmapData=new BitmapData(picWidth,picHeight,true,bgColor); bitDat1.perlinNoise(100,10, 10, 1, false, false, 1, true, [new Point(_offset+=1*-1, _offset*-1)]); bitmap.bitmapData=bitDat1; var bitDat:BitmapData=new BitmapData(picWidth,picHeight,true,bgColor); bitDat.draw(picContainer); //addChild(bitmap); var mapBitmap:BitmapData=bitDat; var mapPoint:Point=new Point(0,0); var channels:uint=BitmapDataChannel.RED; var componentX:uint=channels; var componentY:uint=channels; var scaleX:Number=0.5; var scaleY:Number=-30; var mode:String=DisplacementMapFilterMode.CLAMP; var color:uint=0; var alpha:Number=0; var filter:BitmapFilter=new DisplacementMapFilter(mapBitmap,mapPoint,componentX,componentY,scaleX,scaleY,mode,color,alpha); filters=new Array(filter); } } }
Action in der Fla
var bild:See= new See(0,0); var disp:WaterMap1= new WaterMap1(bild,200,-74); addChild(disp); var detail:Maske= new Maske(); addChild(detail);
Hier noch eine praktische Klassendatei, bei der man alle Parameter für Perlin Noise definieren kann.
package { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.display.GradientType; import flash.display.SpreadMethod; import flash.display.Sprite; import flash.filters.BitmapFilter; import flash.filters.DisplacementMapFilter; import flash.filters.DisplacementMapFilterMode; import flash.geom.Matrix; import flash.geom.Point; import flash.text.TextField; import flash.events.*; public class DisplacePerlin extends Sprite { private var bgColor:uint=0xFFCC00; private var picWidth:uint=200; private var picHeight:uint=200; private var bild:BitmapData; private var _offset:Number=0; private var _offset2:Number=5; private var bitDat:BitmapData=new BitmapData(picWidth,picHeight,true,bgColor); private var bitmap:Bitmap=new Bitmap(); private var baseX:Number; private var baseY:Number; private var numOctaves:uint; private var randomSeed:int; private var stitch:Boolean; private var fractalNoise:Boolean; private var channelOptions:uint; private var grayScale:Boolean; private var xSpeed:Number; private var xDir:Number; private var ySpeed:Number; private var yDir:Number; public function DisplacePerlin(_bild:BitmapData, _baseX:Number=100, _baseY:Number=10, _numOctaves:uint=5, _randomSeed:int=1, _stitch:Boolean=false, _fractalNoise:Boolean=false, _channelOptions:uint=1, _grayScale:Boolean=true, _xSpeed:Number=1, _xDir:Number=-1, _ySpeed:Number=0, _yDir:Number=-1):void { baseX=_baseX; baseY=_baseY; numOctaves=_numOctaves; randomSeed=_randomSeed; stitch=_stitch; fractalNoise=_fractalNoise; channelOptions=_channelOptions; grayScale=_grayScale; xSpeed=_xSpeed; xDir=_xDir; ySpeed=_ySpeed; yDir=_yDir; bild=_bild; picWidth=bild.width; picHeight=bild.height; var anzeigeBild:Bitmap = new Bitmap(); anzeigeBild.bitmapData=bild; addChild(anzeigeBild); addEventListener(Event.ENTER_FRAME, createFilter); }; private function createFilter(evt:Event):void { var bitDat:BitmapData=new BitmapData(picWidth,picHeight,true,bgColor); bitDat.perlinNoise(baseX, baseY, numOctaves, randomSeed, stitch, fractalNoise, channelOptions, grayScale, [new Point(_offset+=xSpeed*xDir, _offset+=ySpeed*yDir)]); bitmap.bitmapData=bitDat; var mapBitmap:BitmapData=bitDat; var mapPoint:Point=new Point(0,0); var channels:uint=BitmapDataChannel.RED; var componentX:uint=channels; var componentY:uint=channels; var scaleX:Number=0.5; var scaleY:Number=-30; var mode:String=DisplacementMapFilterMode.CLAMP; var color:uint=0; var alpha:Number=0; var filter:BitmapFilter=new DisplacementMapFilter(mapBitmap,mapPoint,componentX,componentY,scaleX,scaleY,mode,color,alpha); filters=new Array(filter); } } }
Actions in der fla
var wasser:Sky= new Sky(0,0); var disp:DisplacePerlin= new DisplacePerlin(wasser, 170, 90, 6, 2, false, false, 1, true, 3, 1, 2, -1); addChild(disp); var landscape:Maske= new Maske(); addChild(landscape);
Dieses Beispiel simuliert keine realistischen Flüssigkeitsbewegungen, da sollte man besser mit dem Convolution Filter arbeiten. siehe unten
Aber dennoch finde ich dieses Beispiel ganz nett. Ein ähnliches Beispiel wie dieses findet ihr hier
In der Bibliothek befindet sich eine Grafik mit schwarzem Ring der nach außen hin zu beiden Seiten transparent wird. Dieser MC (Ring) bekommt eine Klassendatei, um seine Bewegung zu steuern. Ich habe 2 Versionen des Codes der fla bereitgestellt. In der einfachen Version werden sämtliche Ringe nur einmal und alle auf einmal erstellt.
Action in der Ring.as
vpackage { import flash.display.MovieClip; import flash.events.*; import flash.events.*; public class Ring extends MovieClip { public var delay:uint; //---------------------------------------------- public function Ring(_delay:uint=6) { super(); delay=_delay; this.scaleX=this.scaleY=delay/10; addEventListener(Event.REMOVED_FROM_STAGE, destroy); addEventListener(Event.ENTER_FRAME, onFrameEnter); } function onFrameEnter(evt:Event):void { this.x=this.x+(this.mouseX)/delay; this.y=this.y+(this.mouseY)/delay; } //----------------------------------------------- private function destroy(evt:Event):void { removeEventListener(Event.REMOVED_FROM_STAGE, destroy); removeEventListener(Event.ENTER_FRAME, onFrameEnter); } } }Actionscript in der Fla (einfache Version)
var displace:MovieClip = new MovieClip(); var tr:Number=1;//alpha var ringCount:uint=30; var ringe:Array=new Array(); for (var i:uint=3; i < ringCount; i++) { var ring:Ring=new Ring(i); this.displace.addChild(ring); ringe.push(ring); } var bg:Bild=new Bild(); addChild(bg); function moveDisplace(evt:Event):void { for (var m:uint=0; m < ringe.length; m++) { ringe[m].alpha-=0.01; } var mapPoint:Point=new Point(0,0); var mapBitmap:BitmapData=new BitmapData(bg.width,bg.height); var displacementMapFilter:DisplacementMapFilter = new DisplacementMapFilter( mapBitmap, mapPoint, BitmapDataChannel.GREEN, BitmapDataChannel.RED , 30, 30, DisplacementMapFilterMode.CLAMP, 0, 0 ); mapBitmap.draw(displace); bg.filters=new Array(displacementMapFilter); } function setVisible(evt:MouseEvent):void { for (var m:uint=0; m < ringe.length; m++) { ringe[m].alpha=1; } } addEventListener(Event.ENTER_FRAME,moveDisplace); addEventListener(MouseEvent.MOUSE_MOVE,setVisible);
Actionscript der Fla (kompliziertere Version)
var myTimer:Timer=new Timer(100); myTimer.addEventListener(TimerEvent.TIMER, TimerUpdate); myTimer.start(); var e:Number=1; var displace:MovieClip = new MovieClip(); var tr:Number=1;//alpha var ringCount:uint=30; var ringe:Array=new Array(); for (var i:uint=1; i < ringCount; i++) { var ring:Ring=new Ring(i); ringe.push(ring); } var bg:Bild=new Bild(); addChild(bg); function moveDisplace(evt:Event):void { if (ringe[0].alpha <= 0) { while (displace.numChildren>0) { displace.removeChildAt(this.numChildren-1); } } else { for (var m:uint=0; m < ringe.length; m++) { ringe[m].alpha-=0.01; } } var mapPoint:Point=new Point(0,0); var mapBitmap:BitmapData=new BitmapData(bg.width,bg.height); var displacementMapFilter:DisplacementMapFilter = new DisplacementMapFilter( mapBitmap, mapPoint, BitmapDataChannel.GREEN, BitmapDataChannel.RED , 30, 30, DisplacementMapFilterMode.CLAMP, 0, 0 ); mapBitmap.draw(displace); bg.filters=new Array(displacementMapFilter); } function setVisible(evt:MouseEvent):void { for (var m:uint=0; m < e; m++) { ringe[m].alpha=1; } if (displace.numChildren==0) { e=1; myTimer.start(); } } function TimerUpdate(event:TimerEvent):void { if (e < ringe.length-1) { e++; this.displace.addChild(ringe[e]); } else { myTimer.stop(); } } addEventListener(Event.ENTER_FRAME,moveDisplace); addEventListener(MouseEvent.MOUSE_MOVE,setVisible);
Mit dem Convolution Filter in Actionscript 3 hat man die Möglichkeit mit einer Matrix verschiedene Pixelberechnungen auf ein Bild anzuwenden.
Ein ganz tolles Beispiel, was man damit machen kann, findet ihr hier: Blog Der Schmale
Diese Convolution Filter findet man auch in Bildbearbeitungsprogrammen wie Photoshop, Gimp etc. In Photoshop findet man ihn unter Filter/ Sonstige Filter/ eigene Filter. Diese Matrix ist gar nicht so schwer zu verstehen, wenn man weiß, was ein Pixel ist und wenn man sich folgendes Tutorial anschaut:
verständliche Info Flatungsmatrix in Gimp
Weichzeichnen | Divisor 9 | Bias 0 | |||||||||||||||||||||||||||||
Farben | Matrix | Ergebnis | |||||||||||||||||||||||||||||
|
* |
|
= |
|
Versatz nach unten | Divisor 1 | Bias 0 | |||||||||||||||||||||||||||||
Farben | Matrix | Ergebnis | |||||||||||||||||||||||||||||
|
* |
|
= |
|
Farbe * Matrixwert / alle Ergebnisse addieren / Ergebnis durch Divisor teilen und Bias hinzuaddieren / Ergebnis dem mittleren Pixel zuweisen
Jedes Feld steht für einen Pixel im Bild bzw. den Farbwert. Es ist einfacher sich die Sache erstmal für ein Graustufenbild vorzustellen, wo man einen Graustufenwert von 0 für schwarz bis 255 für weiß hat. In der Matrix haben wir hier im mittleren Feld einen Pixel mit dem Farbwert 20 und die umliegenden Pixel mit ihren Werten. Die Matrix wird auf diesen mittleren Pixel angewendet und zwar wird jede Farbe mit dem dem dazugehörigen Wert der Matrix multipliziert. Die Ergebnisse werden addiert und durch den Divisor geteilt. Der Bias wird hinzuaddiert somit erhält man den Wert für den mittleren Pixel. Dieses Verfahren wird auf alle Pixel angewendet. In ersten Beispiel bekommen wir einen Mittelwert aller Pixel. Dadurch wird das Bild unscharf. Im zweiten Beispiel wird der Pixel der über dem mittleren Pixel liegt nach unten verschoben.
In der Regel hat man eine Matrix von 3 x 3 es gibt aber auch größen Matrixen sind möglich. Man bestimmt die Reihen und Spalten der Matrix mit den ersten beiden Parametern des Convolution Filters. Der Bias Wert kann auch negativ sein. Er dient dazu Farbwerte die zu tief liegen anzuheben oder die zu hoch liegen, niedriger zu setzen.
Adobe Referenzen Convolution Filter
ConvolutionFilter / Konstruktor
public function ConvolutionFilter(matrixX:Number = 0, matrixY:Number = 0, matrix:Array = null, divisor:Number = 1.0, bias:Number = 0.0, preserveAlpha:Boolean = true, clamp:Boolean = true, color:uint = 0, alpha:Number = 0.0);Ich habe hier ein paar einfache Beispiele. In beiden Beispielen liegt ein Movieclip auf der Bühne mit Instanznamen "bild".
//Movieclip Instanzname "bild" liegt auf der Bühne var matrix:Array = [-30, 30, 0, -30, 30, 0, -30, 30, 0]; var matrixX:Number=3; var matrixY:Number=3; var divisor:Number=9; var convFilter:BitmapFilter=new ConvolutionFilter(matrixX,matrixY,matrix,divisor); var myfilter:Array = new Array(); myfilter.push(convFilter); bild.filters=myfilter;
//Movieclip Instanz namens "bild" liegt auf der Bühne function applyFilter(child:DisplayObject, matrix:Array, divisor:Number):void { var matrixX:Number=3; var matrixY:Number=3; var filter:BitmapFilter=new ConvolutionFilter(matrixX,matrixY,matrix,divisor); var filters:Array = new Array(); filters.push(filter); child.filters=filters; } function applyNone(evt:MouseEvent):void { var matrix:Array = [0, 0, 0, 0, 1, 0, 0, 0, 0]; applyFilter(bild, matrix, 1); } function applyBrightness(evt:MouseEvent):void { var matrix:Array = [5, 5, 5, 5, 0, 5, 5, 5, 5]; applyFilter(bild, matrix, 9); } function applySharpness(evt:MouseEvent):void { var matrix:Array = [0, -1, 0, -1, 5, -1, 0, -1, 0]; applyFilter(bild, matrix, 1); } function applyOutline(evt:MouseEvent):void { var matrix:Array = [-30, 30, 0, -30, 30, 0, -30, 30, 0]; applyFilter(bild, matrix, 9); } function applyBlur(evt:MouseEvent):void { var matrix:Array = [1, 1, 1, 1, 1, 1, 1, 1, 1]; applyFilter(bild, matrix, 9); } function applyRelief(evt:MouseEvent):void { var matrix:Array = [-2, -1, 0, -1, 1, 1, 0, 1, 2]; applyFilter(bild, matrix, 1); } none_btn.addEventListener(MouseEvent.CLICK, applyNone); bright_btn.addEventListener(MouseEvent.CLICK, applyBrightness); sharp_btn.addEventListener(MouseEvent.CLICK, applySharpness); out_btn.addEventListener(MouseEvent.CLICK, applyOutline); blur_btn.addEventListener(MouseEvent.CLICK, applyBlur); relief_btn.addEventListener(MouseEvent.CLICK, applyRelief);
siehe dazu auch den Tipp Bitmapfüllung bei Adobe
Im folgenden Beispiel habe ich ein importiertes Bitmap in der Bibliothek und habe es dort als Bitmapklasse mit dem Klassennamen DevilDog deklariert.
var bmpData:DevilDog = new DevilDog(0,0); var mySprite:Sprite = new Sprite(); var matrix:Matrix = new Matrix(); //matrix.rotate(Math.PI/4); matrix.scale(0.2,0.2); mySprite.graphics.beginBitmapFill(bmpData, matrix, true); mySprite.graphics.drawRect(0, 0, 500, 400); mySprite.graphics.endFill(); addChild(mySprite);