Verwenden von Dreiecken für 3D-Effekte

Flash Player 10 und höher, Adobe AIR 1.5 und höher

In ActionScript führen Sie Bitmaptransformationen mithilfe der Graphics.drawTriangles() -Methode durch, da 3D-Modelle durch eine Sammlung von Dreiecken im Raum dargestellt werden. (Allerdings unterstützen Flash Player und AIR keinen Tiefenpuffer, sodass Anzeigeobjekte im Prinzip weiterhin flach, d. h. zweidimensional, sind. Dies wird beschrieben unter Informationen zu 3D-Anzeigeobjekten in Flash Player und der AIR-Laufzeitumgebung .) Die Graphics.drawTriangles() -Methode funktioniert ähnlich wie die Graphics.drawPath() -Methode, da zum Zeichnen eines Dreieckpfads ebenfalls ein Satz an Koordinaten benötigt wird.

Um sich mit der Arbeit mit der Graphics.drawPath() -Methode vertraut zu machen, sollten Sie den Abschnitt Zeichenpfade lesen.

Die Graphics.drawTriangles() -Methode verwendet die Syntax „Vector.<Number>“, um die Position der Punkte für den Dreieckpfad anzugeben:

drawTriangles(vertices:Vector.<Number>, indices:Vector.<int> = null, uvtData:Vector.<Number> = null, culling:String = "none"):void

Der erste Parameter von drawTriangles() ist der einzige obligatorische Parameter, nämlich der Parameter vertices . Dieser Parameter ist ein Zahlenvektor, der die Koordinaten definiert, aus denen die Dreiecke gezeichnet werden. Alle drei Koordinatensets (sechs Zahlen) repräsentieren einen Dreieckpfad. Ohne den indices -Parameter sollte die Länge des Vektors immer das Sechsfache betragen, da jedes Dreieck drei Koordinatenpaare benötigt (d. h. drei Sätze an x/y-Werten). Zum Beispiel:

graphics.beginFill(0xFF8000); 
graphics.drawTriangles( 
    Vector.<Number>([ 
        10,10,  100,10,  10,100, 
        110,10, 110,100, 20,100]));

Keine dieser Dreiecke teilen sich einen Punkt, aber wenn das der Fall wäre, könnte der Wert mithilfe des zweiten drawTriangles() -Parameters, indices , verwendet werden, um die Werte im Vektor vertices für mehrere Dreiecke wiederzuverwenden.

Wenn Sie den indices -Parameter verwenden, sollten Sie sich darüber im Klaren sein, dass die indices -Werte Punktindizes sind und keine Indizes, die sich direkt auf die vertices -Arrayelemente beziehen. Anders ausgedrückt: Eine durch indices im vertices -Vektor definierte Indexposition ist in Wahrheit die echte Indexposition dividiert durch 2. Für den dritten Punkt eines vertices -Vektors verwenden Sie beispielsweise den indices -Wert 2, obwohl der erste numerische Wert dieses Punkts bei der Vektorindexposition 4 beginnt.

Sie können zum Beispiel zwei Dreiecke mithilfe des indices -Parameters so zusammenführen, dass sie sich in der Diagonale eines Vierecks eine Kante teilen:

graphics.beginFill(0xFF8000); 
graphics.drawTriangles( 
    Vector.<Number>([10,10, 100,10, 10,100, 100,100]), 
    Vector.<int>([0,1,2, 1,3,2]));

Beachten Sie, dass im vertices -Vektor nur vier Punkte angegeben wurden, obwohl ein Quadrat unter Verwendung von zwei Dreiecken gezeichnet wurde. Durch die Verwendung von indices werden die beiden Punkte, die beide Dreiecke gemeinsam haben, für jedes Dreieck verwendet. Dadurch reduziert sich die Gesamtzahl der Vertizes von 6 (12 Zahlen) auf 4 (8 Zahlen):

Ein mithilfe des vertices-Parameters aus zwei Dreiecken gezeichnetes Quadrat

Je größer die Dreieckgitter, desto nützlicher wird diese Technik, da dabei die meisten Punkte von mehreren Dreiecken geteilt werden.

Auf Dreiecke können alle Füllungen angewendet werden. Die Füllungen werden auf das resultierende Dreieckgitter wie auf jede sonstige Form angewendet.

Transformieren von Bitmaps

Bitmaptransformationen schaffen die Illusion von Perspektive oder „Textur“ auf einem dreidimensionalen Objekt. Insbesondere können Sie eine Bitmap zum Fluchtpunkt hin verzerren, sodass das Bild kleiner zu werden scheint, während es sich dem Fluchtpunkt nähert. Sie können auch eine zweidimensionale Bitmap verwenden, um eine Oberfläche für ein dreidimensionales Objekt zu erstellen und so den Eindruck erwecken als ob die Textur das dreidimensionale Objekt umwickelte.

Eine zweidimensionale Oberfläche, die einen Fluchtpunkt verwendet und ein von einer Bitmap umwickeltes dreidimensionales Objekt

UV-Zuordnung

Wenn Sie erst einmal mit der Arbeit mit Texturen begonnen haben, werden Sie auch den uvtData-Parameter von drawTriangles() verwenden wollen. Dieser Parameter ermöglicht es Ihnen, die UV-Zuordnung für Bitmapfüllungen einzurichten.

Die UV-Zuordnung ist ein Verfahren zur Texturierung von Objekten. Sie ist von zwei Werten abhängig, einem horizontalen U-Wert (x) und einem vertikalen V-Wert (y). Diese basieren nicht auf Pixelwerten, sondern auf Prozentwerten. 0 U und 0 V bezeichnet die Bildecke links oben und 1 U und 1 V die Bildecke rechts unten:

Die Positionen UV 0 und 1 auf einem Bitmapbild

Die Vektoren eines Dreiecks können als UV-Koordinaten angegeben werden, um sich mit den entsprechenden Positionen in einem Bild zu verbinden:

Die UV-Koordinaten eines Dreieckbereichs aus einem Bitmapbild

Die UV-Werte bleiben mit den Punkten des Dreiecks konsistent:

Die Vertizes des Dreiecks werden verschoben und die Bitmap wird verzerrt, um den UV-Wert für einen einzelnen Punkt unverändert zu lassen:

Während auf das mit der Bitmap verbundene Dreieck ActionScript-3D-Transformationen angewendet werden, wird das Bitmapbild aufgrund der UV-Werte auf das Dreieck angewendet. Anstatt also Matrixberechnungen zu verwenden, werden einfach die UV-Werte gesetzt oder angepasst, um einen dreidimensionalen Effekt zu erschaffen.

Die Graphics.drawTriangles() -Methode akzeptiert auch eine optionale Information für dreidimensionale Transformationen: den T-Wert. Der T-Wert in „uvtData“ repräsentiert die 3D-Perspektive, oder genauer: den Skalierungsfaktor des verbundenen Vertex. Die UVT-Zuordnung ergänzt die UV-Zuordnung um perspektivische Korrekturen. Wenn ein Objekt beispielsweise in einem 3D-Raum vom Betrachterstandpunkt entfernt positioniert wird, sodass es nur 50 % der „Originalgröße“ zu haben scheint, würde der T-Wert dieses Objekts 0,5 betragen. Da Dreiecke gezeichnet werden, um die Objekte im 3D-Raum zu repräsentieren, bestimmen deren Positionen auf der z-Achse deren T-Werte. Die Gleichung, die den T-Wert bestimmt, lautet wie folgt:

T = focalLength/(focalLength + z);
In dieser Gleichung steht „focalLength“ für die Brennweite oder eine berechnete Bildschirmposition, die bestimmt, wie stark die Perspektive in dieser Sicht ist.
Die Brennweite und der z-Wert
A.
Standpunkt

B.
Bildschirm

C.
3D-Objekt

D.
focalLength-Wert (Brennweite)

E.
z-Wert

Der Wert von T wird verwendet, um die Grundformen zu verkleinern, sodass sie scheinbar weiter entfernt sind. Mit diesem Wert werden in der Regel 3D-Punkte auf 2D-Punkte abgebildet. Im Fall der UVT-Daten wird er auch dazu verwendet, um eine Bitmap zwischen den Punkten innerhalb eines Dreiecks mit Perspektive zu skalieren.

Wenn Sie UVT-Werte definieren, folgt der T-Wert direkt den UV-Werten für einen Vertex. Wird der T-Wert miteinbezogen, sind alle drei Werte im uvtData -Parameter (U, V und T) stimmig mit den beiden Werten im vertices -Parameter (x und y). Mit UV-Werten allein ist uvtData.length == vertices.length. Unter Miteinbeziehung eines T-Werts ist uvtData.length == 1,5*vertices.length.

Im folgenden Beispiel wird gezeigt, wie eine Fläche mithilfe von TVT-Daten im 3D-Raum gedreht werden kann. In diesem Beispiel wird das Bild „ocean.jpg“ mithilfe der „Hilfsklasse“ „ImageLoader“ geladen, sodass es dem BitmapData-Objekt zugewiesen werden kann.

Hier der Quellcode der ImageLoader-Klasse (speichern Sie diesen Code in eine Datei mit dem Namen „ImageLoader.as“):

package { 
    import flash.display.* 
    import flash.events.*; 
    import flash.net.URLRequest; 
    public class ImageLoader extends Sprite { 
        public var url:String; 
        public var bitmap:Bitmap; 
    public function ImageLoader(loc:String = null) { 
        if (loc != null){ 
            url = loc; 
            loadImage(); 
        } 
    } 
    public function loadImage():void{ 
        if (url != null){ 
            var loader:Loader = new Loader(); 
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete); 
            loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIoError); 
             
                var req:URLRequest = new URLRequest(url); 
                loader.load(req); 
            } 
        } 
         
    private function onComplete(event:Event):void { 
            var loader:Loader = Loader(event.target.loader); 
            var info:LoaderInfo = LoaderInfo(loader.contentLoaderInfo); 
            this.bitmap = info.content as Bitmap; 
            this.dispatchEvent(new Event(Event.COMPLETE)); 
    } 
         
    private function onIoError(event:IOErrorEvent):void { 
            trace("onIoError: " + event); 
    } 
    } 
}

Es folgt das ActionScript-Codebeispiel, das Dreiecke, die UV-Zuordnung und T-Werte verwendet, um den Eindruck zu vermitteln, dass das Bild zum Fluchtpunkt hin kleiner wird und sich dreht. Speichern Sie diesen Code in eine Datei mit dem Namen „Spinning3dOcean.as“:

package { 
    import flash.display.* 
    import flash.events.*; 
    import flash.utils.getTimer; 
     
    public class Spinning3dOcean extends Sprite { 
        // plane vertex coordinates (and t values) 
        var x1:Number = -100,    y1:Number = -100,    z1:Number = 0,    t1:Number = 0; 
        var x2:Number = 100,    y2:Number = -100,    z2:Number = 0,    t2:Number = 0; 
        var x3:Number = 100,    y3:Number = 100,    z3:Number = 0,    t3:Number = 0; 
        var x4:Number = -100,    y4:Number = 100,    z4:Number = 0,    t4:Number = 0; 
        var focalLength:Number = 200;      
        // 2 triangles for 1 plane, indices will always be the same 
        var indices:Vector.<int>; 
         
        var container:Sprite; 
         
        var bitmapData:BitmapData; // texture 
        var imageLoader:ImageLoader; 
        public function Spinning3dOcean():void { 
            indices =  new Vector.<int>(); 
            indices.push(0,1,3, 1,2,3); 
             
            container = new Sprite(); // container to draw triangles in 
            container.x = 200; 
            container.y = 200; 
            addChild(container); 
             
            imageLoader = new ImageLoader("ocean.jpg"); 
            imageLoader.addEventListener(Event.COMPLETE, onImageLoaded); 
        } 
        function onImageLoaded(event:Event):void { 
            bitmapData = imageLoader.bitmap.bitmapData; 
            // animate every frame 
            addEventListener(Event.ENTER_FRAME, rotatePlane); 
        } 
        function rotatePlane(event:Event):void { 
            // rotate vertices over time 
            var ticker = getTimer()/400; 
            z2 = z3 = -(z1 = z4 = 100*Math.sin(ticker)); 
            x2 = x3 = -(x1 = x4 = 100*Math.cos(ticker)); 
             
            // calculate t values 
            t1 = focalLength/(focalLength + z1); 
            t2 = focalLength/(focalLength + z2); 
            t3 = focalLength/(focalLength + z3); 
            t4 = focalLength/(focalLength + z4); 
             
            // determine triangle vertices based on t values 
            var vertices:Vector.<Number> = new Vector.<Number>(); 
            vertices.push(x1*t1,y1*t1, x2*t2,y2*t2, x3*t3,y3*t3, x4*t4,y4*t4); 
            // set T values allowing perspective to change 
            // as each vertex moves around in z space 
            var uvtData:Vector.<Number> = new Vector.<Number>(); 
            uvtData.push(0,0,t1, 1,0,t2, 1,1,t3, 0,1,t4); 
             
            // draw 
            container.graphics.clear(); 
            container.graphics.beginBitmapFill(bitmapData); 
            container.graphics.drawTriangles(vertices, indices, uvtData); 
        } 
    } 
}

Um dieses Beispiel zu testen, speichern Sie diese beiden Klassendateien im selben Verzeichnis als ein Bild mit dem Namen „ocean.jpg“. Sie können sehen, wie die Orignalbitmap so transformiert wird, dass sie den Eindruck vermittelt, als ob sie im 3D-Raum in der Ferne verschwindet und sich dreht.

Culling

Culling ist ein Verfahren zur Bestimmung der Oberflächen eines dreidimensionalen Objekts, die nicht gerendert werden sollen, da sie vom aktuellen Betrachterstandpunkt aus nicht zu sehen sind. Im dreidimensionalen Raum ist die Oberfläche der Rückseite eines dreidimensionalen Objekts vom Sichtbereich ausgeschlossen.
Die Rückseite eines 3D-Objekts ist vom Sichtbereich ausgeschlossen
A.
Standpunkt

B.
3D-Objekt

C.
Rückseite des 3D-Objekts

Normalerweise werden immer alle Dreiecke gerendert, unabhängig von ihrer Größe, Form oder Position. Das Cullingverfahren stellt sicher, dass Flash Player oder AIR Ihre 3D-Objekte korrekt rendert. Für kürzere Renderzeiten sollten bestimmte Dreiecke vom Renderprozess ausgeschlossen werden. Stellen Sie sich z. B. einen Würfel vor, der sich im Raum dreht. Egal, zu welchem Zeitpunkt, Sie werden nie mehr als drei Seiten dieses Würfels zu Gesicht bekommen, da sich die anderen Seiten auf der Rückseite des Würfels befinden. Und da diese Seiten verdeckt sind, sollte der Renderer sie auch nicht berechnen und zeichnen. Ohne Culling rendert Flash Player oder AIR sowohl die Vorder- als auch die Rückseite des Würfels.

Ein Würfel hat Seiten, die vom aktuellen Betrachterstandpunkt nicht zu sehen sind

Daher besitzt die Graphics.drawTriangles() -Methode einen vierten Parameter zum Festlegen des Cullingwertes:

public function drawTriangles(vertices:Vector.<Number>, indices:Vector.<int> = null, uvtData:Vector.<Number> = null, culling:String = "none"):void

Dieser Parameter gehört zur TriangleCulling -Aufzählungsklasse und kann die Werte TriangleCulling.NONE , TriangleCulling.POSITIVE und TriangleCulling.NEGATIVE annehmen. Diese Werte sind abhängig von der Richtung des Dreieckpfads, der die Oberfläche des Objekts definiert. Die ActionScript-API, die das Culling steuert, setzt voraus, dass alle nach außen gerichteten Dreiecke einer dreidimensionalen Form in derselben Pfadrichtung gezeichnet werden. Wenn ein Dreieck umgedreht wird, ändert sich auch dessen Pfadrichtung. An diesem Punkt kann das Dreieck „ausgelesen“ und vom Renderprozess ausgeschlossen werden.

Der TriangleCulling -Wert POSITIVE schließt alle Dreiecke mit positiver Pfadrichtung (im Uhrzeigersinn) aus. Der TriangleCulling -Wert NEGATIVE schließt alle Dreiecke mit negativer Pfadrichtung (entgegen dem Uhrzeigersinn) aus. Bei einem Würfel haben die Oberflächen der Vorderseite eine positive Pfadrichtung und die der Rückseite eine negative:

Ein „aufgeklappter“ Würfel, in dem die Pfadrichtung angezeigt wird. Im „zusammengeklappten“ Zustand ist die Pfadrichtung der Rückseite umgekehrt

Um zu sehen, wie das Cullingverfahren funktioniert, beginnen Sie mit dem Beispiel aus dem Abschnitt UV-Zuordnung und setzen Sie den culling-Parameter der drawTriangles() -Methode auf TriangleCulling.NEGATIVE :

container.graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.NEGATIVE);

Beachten Sie, dass die jeweilige Rückseite des Bildes beim Drehen des Objekts nicht dargestellt wird.