Waving Leda

Dezember 8th, 2009 by admin

Jetzt habe ich die letzten Tage ein Paar tuts für meine geschätzten Leser produziert, doch es wurde Zeit mal wieder was Neues auszuprobieren. Und weil ich im Rahmen der Grundlagen-Tutorials für Papervision mal wieder ein paar Kleinigkeiten im 3D Bereich coden konnte, hab ich mich auch für ein 3D Experiment entschieden. Das Ergebnis ist “Wavin’ Leda”, Davinci wäre begeistert (oder er dreht sich gerade in Grabe um). Dabei stellt man schnell fest, dass Bitmapmaterials mit Shader und Modifikatoren die FrameRate in die Knie gehen lassen. Ich habe das Plane hier mit nur 10×10 Segmenten mit “Leda bezogen” (hoffentlich liest das ein Kunsthistoriker) und komplett auf shading verzichtet und ich merke wie meine Athlon CPU leidet. Mit einfachen ShadeMaterials läuft die Sache schon flüssiger, aber dass niemand einen falschen Eindruck von meiner Meinung bekommt: AS3Dmod ist großartig, seht selbst:

FlashPlayer starten

Hier könnt ihr könnt ihr WavinLeda downloaden.
Um den Code zu kompilieren, benötigt ihr AS3DMod und natürlich Papervision3D Great White

Papervision3D Grundlagen V Shader

Dezember 7th, 2009 by admin

Weiter gehts mit (wie man in Nürnberg sagen würde) “Gescheidn Schäiding”. Mehr Nürnbergerisch werde ich euch auch garnicht antun. In diesem (vorletzten) Grundlagen Tutorial wird kein Cube mehr genutzt sondern ein Plane, welches im Grunde eine zweidimensionale Fläche (Planes haben kein Maß auf der Z Achse) im dreidimensionalem Raum ist. Statt Gauroudshading kommt nun Phong zum Einsatz und auch die langweilige Dauerrotation ist pasè.

FlashPlayer starten

Der angepasste Code aus Teil III:

package{
import flash.display.Bitmap;
import flash.display.MovieClip;
import flash.events.Event;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.materials.shaders.ShadedMaterial;
import org.papervision3d.materials.shaders.PhongShader;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.primitives.*;
import org.papervision3d.view.BasicView;
import org.papervision3d.lights.PointLight3D;
 
public class Leda extends BasicView 
{
	private var materialsList :MaterialsList;
	private var bitmapMaterial: BitmapMaterial;
	private var shadedMaterial: ShadedMaterial;
	private var shader :PhongShader;
	private var primitive:Plane;
	private var lightPoint :PointLight3D
 
	public function Leda()
	{
		super(500, 500, true);
 
		lightPoint = new PointLight3D();
		shader = new PhongShader(lightPoint, 0xffffff, 0x000000, 15);
 
		// leda ist eine bitmap in der bibliothek
		var leda :BitmapData = new leda_data(500, 500) 
 
		bitmapMaterial = new BitmapMaterial(leda, true); 
		shadedMaterial = new ShadedMaterial(bitmapMaterial, shader);
 
		// die z position für pixelpräzision
		primitive = new Plane(shadedMaterial, 250, 250)
		primitive.z = (camera.zoom * camera.focus) - Math.abs(camera.z);
 
		scene.addChild(primitive);
		startRendering();
	}
 
	override protected function onRenderTick(e:Event = null):void {
		primitive.rotationY = viewport.containerSprite.mouseX / 4;
		primitive.rotationX = viewport.containerSprite.mouseY / 4;
		super.onRenderTick();
	}
}
}

Es wird ein PointLight Objekt erstellt und dieses dem Constructor des Shaders übergeben, genau wie die Farbcodes für Licht und Schatten, in diesem Fall weis und schwarz. Der vierte Parameter bestimmt über die Intensität der Reflektion des Lichtes. Für Leda soll diese nicht zu hoch sein, damit sie die Textur nicht überblendet.

lightPoint = new PointLight3D();
shader = new PhongShader(lightPoint, 0xffffff, 0x000000, 15);

Leda liegt im JPG Format in der Bibliothek vor, exportiert man Sie in den Film, wird ihr eine Klasse zugewiesen, die von BitmapData erbt, wesshalb Breite und Höhe für die Initialisierung angegeben werden müssen. Dieses BitmapData Objekt übergibt man an das BitmapMaterial, der zweite parameter “true” aktiviert den Präzisionsmodus. Ohne diesen würde die Textur auf dem Plane hässliche Beulen bilden, wenn man es bewegt. Fehlt noch das ShadedMaterial, dieses erwartet das BitmapMaterial Objekt als ersten parameter, als zweiten den Shader selbst.

var leda :BitmapData = new leda_data(500, 500) 
 
bitmapMaterial = new BitmapMaterial(leda, true); 
shadedMaterial = new ShadedMaterial(bitmapMaterial, shader);

Die erste Zeile aus dem folgenden Abschnitt sollte leicht verständlich sein. Das Plane ist ein Primitive, es erhält das ShadedMaterial als Textur und anschließend werden Breite und Höhe bestimmt – die Tiefe fällt logischer Weise weg. Die nächste Zeile ist die magische Formel, mit der man erreicht, dass die Entfernung des Objekts im Raum zur Kamera (die ebenfalls “im Raum” ist) genau soviel beträgt, dass Ledas Textur nicht skalliert werden muss. Die sogenannte Pixelperfektion.

primitive = new Plane(shadedMaterial, 250, 250)
primitive.z = (camera.zoom * camera.focus) - Math.abs(camera.z);

Ein kleiner Exkurs zur Pixelperfektion: FlashPlayer ist überwiegend für Vektoren konzeptioniert, diese können verlustfrei skalliert werden, da sie auf Berechnungen basieren, nicht aber Bitmaps, welche bekannter maßen verpixeln (da sie auf Pixelinformationen basieren), sobald man sie vergrößert. Verkleinert man sie, ist der Rechenaufwand für ein scharfes Bild immer noch zu hoch, um flüssige Animationen rendern zu können.

Will man also eine Textur in PV3D scharf darstellen, muss die Z position des Objekts so bestimmt werden, dass die Textur auf dem Objekt in Orginalgröße gezeigt wird. So wie bei Leda, welche 250×250 pixel misst.
Wer mehr dazu wissen möchte, möge bei Bartek Drozdz vorbeischauen, dem die weite Verbreitung dieser überaus heiligen Formel zu verdanken ist.

Zu guter Letzt wird die statische Rotation ersetzt, nun kann der User mit der Maus über Ledas Rotation auf X und Y Achse im Raum bestimmen.

primitive.rotationY = viewport.containerSprite.mouseX / 4;
primitive.rotationX = viewport.containerSprite.mouseY / 4;

Glückwunsch, wer all die bissherigen Grundlagen gemeistert hat, dem fehlt nur noch eines um sie abzurunden. Interaktivität. Zwar reagiert leda hier bereits auf Benutzerinteraktion, doch sie feuert noch keine Events, auf die ein Handler reagieren könnte. Damit beschäftigt sich das letzte Grundlagentutorial.

Papervision3D Grundlagen IV ShadeMaterials

Dezember 6th, 2009 by admin

Grundlagenwissen: Shading bezeichnet grundsätzlich den Vorgang, sowohl bei pv3d als auch bei jeder anderen 3D Egnine (unabhängig von Flash), bei dem die Textur eines Objektes zur Laufzeit manipuliert wird, um einen Effekt von Licht und Schatten auf dieser zu erzeugen. Beim Shading werden also Bereiche der Textur aufgehellt und abgedunkelt um stärkere Assoziationen zur Räumlichkeit herzustellen.

In Papervision3D gibt es verschiedene Shader und ShadeMaterials, wobei ShadeMaterials mit ColorMaterials vergleichbar sind, nur eben mit Shading inklusive. In diesem Fall dient das GouraudMaterial als texturelle Grundlage, welches ein ShadeMaterial ist. Falls das uninteressant klingt, im nächsten Teil werden “richtige” Texturen geshadet.

FlashPlayer starten

Zunächst der gesamte Code:

package{
import flash.display.MovieClip;
import flash.events.Event;
import org.papervision3d.materials.shadematerials.GouraudMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.view.BasicView;
import org.papervision3d.lights.PointLight3D;
 
public class ShadedCube extends BasicView 
{
	private var materialsList :MaterialsList;
	private var material: GouraudMaterial;
	private var primitive: Cube;
	private var lightPoint :PointLight3D
 
	public function ShadedCube()
	{
		super(500, 500, true);
 
		lightPoint = new PointLight3D(false);
		lightPoint.x = -50;
		lightPoint.y = -50;
		lightPoint.z = 50;
 
		material = new GouraudMaterial(lightPoint, 0xcc0000, 0x370000);
		materialsList = new MaterialsList( { all:material } );
 
		primitive = new Cube(materialsList, 500, 500, 500);
		scene.addChild(primitive);
 
		startRendering();
	}
 
	override protected function onRenderTick(e:Event = null):void {
		primitive.rotationY += 2
		primitive.rotationX += 2
		primitive.rotationZ += 2
		super.onRenderTick();
	}
}
}

Es benötigt eigentlich nur (im weitesten Sinne) ein Licht, das PointLight3D Objekt. Dieses kann über x, y und z im Raum positioniert werden. Anschließend wird das ShadeMaterial initialisiert, welchem die Hexcodes für die Farbe des Lichts sowie die Farbe der Umgebung übergeben werden. In diesem Fall ein heller und ein dunkler Rotton. Daraufhin wird das Material auf alle Seiten des Cubes angewendet, Voila.

lightPoint = new PointLight3D(false);
lightPoint.x = -50;
lightPoint.y = -50;
lightPoint.z = 50;
 
material = new GouraudMaterial(lightPoint, 0xcc0000, 0x370000);
materialsList = new MaterialsList( { all:material } );

Papervision3D Grundlagen II BasicView

Dezember 6th, 2009 by admin

Vorhergehendes Beispiel lässt sich auch schneller und einfacher realisieren. Wenn man die Grundlagen von Papervision (Viewport, Scene, Camera, Renderer, Objekt und Material) versteht, erhält man mit dem Basicview ein praktisches Template für einfache 3D Scenes. Das setzt natürlich voraus, dass die gesamte Szene in einem Viewport dargestellt und gerendert wird. In einem solchen Fall ist das Ergebniss ist das Gleiche:

FlashPlayer starten

Doch die Arbeit dahinter wird minimiert:

package{
import flash.events.Event;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.objects.primitives.Arrow;
import org.papervision3d.view.BasicView;
 
public class BasicViewArrow extends BasicView 
{
	private var material: ColorMaterial;
	private var primitive: Arrow;
 
	public function BasicViewArrow()
	{
		super(500, 500, true);
		material = new ColorMaterial(0x000099);
		primitive = new Arrow(material);
		scene.addChild(primitive);
 
		startRendering();
	}
 
	override protected function onRenderTick(e:Event = null):void {
		primitive.rotationY += 2;
		primitive.rotationX += 2;
		primitive.rotationZ += 2;
		super.onRenderTick();
	}
}
}

Die Klasse erbt von org.papervision3d.view.BasicView. Der BasicView selbst erbt (um eine Ecke) vom Sprite und hält bereits einen ein Camera3D-, Scene3D-, ViewPort3D, und ein BasicRenderer Objekt für seine Nachkommen bereit.

Ausserdem ist hier der EnterFrame Listener überflüssig geworden, da auch diese Funktion vom BasicView bereitgestellt wird. Sie heißt startRendering, um das Rendern zu stoppen genügt ein Aufruf von stopRender und mit singleRender wird ein einzelnes Bild ausgerendert. Die Funktion startRendering tut im Grunde genau das, was im ersten Tutorial von unserem EventHandler erledigt wurde, indem es in jedem Frame onRenderTick aufruft. Also habe ich in diesem Fall die Funktion onRenderTick überschrieben um die Rotation zu erzeugen.

override protected function onRenderTick(e:Event = null):void {
		primitive.rotationY += 2
		primitive.rotationX += 2
		primitive.rotationZ += 2
		super.onRenderTick();
	}

Aus funktioneller Sicht wurde das vorhergehende Beispiel also nicht verändert, doch es ist jetzt viel weniger Code notwendig, um das selbe Ergebniss zu erreichen. Weiter gehts bei den Grundlagen von Papervision mit animierten Materials, siehe Papervision Grundlagen Teil 3.

Papervision3D Grundlagen III Animierte Materials

Dezember 6th, 2009 by admin

In den bissherigen Papervision Beispielen wurden lediglich simple ColorMaterials zur Texturierung von Objekten genutzt – Zeit diese durch animierte MovieClips zu ersetzen. Hierzu wird ein entsprechender MC erstellt und mit dem Namen “tex_mc” verknüpft. Ich arbeite hier mit der Flash IDE und als Animation habe ich eine Farbtransformation gewählt, weil das den 70er Jahre Style hat… disco…

FlashPlayer starten

Als Grundlage hierfür kommt vorhergehendes Beispiel gerade recht. Zunächst die modifizierte Version des Codes – Erläuterungen folgen weiter unten:

package{
import flash.display.MovieClip;
import flash.events.Event;
import org.papervision3d.materials.MovieMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.view.BasicView;
 
public class DiscoCube extends BasicView
{
	private var tex :MovieClip;
	private var materialsList :MaterialsList;
	private var material: MovieMaterial;
	private var primitive: Cube;
 
	public function DiscoCube()
	{
		super(500, 500, true);
		tex = new tex2_mc;
		material = new MovieMaterial(tex, false, true)
		materialsList = new MaterialsList( {
						left:material,
						right:material,
						front:material,
						back:material,
						top:material,
						bottom:material
						} );
 
		primitive = new Cube(materialsList, 500, 500, 500);
		scene.addChild(primitive);
 
		startRendering();
	}
 
	override protected function onRenderTick(e:Event = null):void {
		primitive.rotationY += 2
		primitive.rotationX += 2
		primitive.rotationZ += 2
		super.onRenderTick();
	}
}
}

Der MovieClip wird dem MovieMaterial als erster Parameter übergeben. Der Kubus besitzt sechs Seiten und für jede dieser Seiten wird das selbe Material verwendet. Einfacher ginge das in einem solchen Fall, indem man die MaterialsList mit {all:myMaterial} initialisiert, doch der Vollständigkeit halber habe ich die Sache ausgeschrieben, denn so lässt sich leichter nachvollziehen, wie die einzelnen Seiten des Würfels mit eventuell verschiedenen Texturen belegt werden können:

materialsList = new MaterialsList( {all:material} );
// oder, für alle die gern viel tippen...
materialsList = new MaterialsList( {
					left:material,
					right:material,
					front:material,
					back:material,
					top:material,
					bottom:material
					} );

Die MaterialsList wird anschließend dem Cube zur Initialisierung übergeben, es folgen dann noch drei weitere Parameter, nähmlich die Maße auf x, y und z-Achse. Wie bereits im ersten PV3D Grundlagen Tutorial erwähnt, wäre es möglich als fünften Parameter noch die Anzahl an Segmenten für das Objekt anzugeben, was sich negativ auf die Performance auswirkt.

materialsList = new MaterialsList( {all:material} );
primitive = new Cube(materialsList, 500, 500, 500);

Die Materials funktionieren alle ähnlich, desshalb muss nicht jedes im Detail erklärt werden. Wer dieses Tutorial einfach findet, wird auch mit dem Rest zurecht kommen. Einen Tick schwerer wird es dann mit ShadeMaterials, dem sollte man ein eigenes Grundlagentutorial einräumen, in Teil IV.