Dokumentation Java 3D – Licht und Material (Von Nicolaus Walter) 1. Licht In Java 3D können Objekte von einer festgelegten Lichtquelle beleuchtet werden. Um eine solche Beleuchtung, welche eine Simulation der Realität darstellt, als dreidimensionales Bild auf einem Computer darstellen zu können, bedient sich Java 3D Unterschiedlicher Mechanismen, welche, richtig kombiniert, den gewünschten Eindruck erwecken. Die Mechanismen sind im Einzelnen Shading, das Model des „Szene Graph“, um die benötigten Elemente zu modellieren und zusammen wirken zu lassen und die einzelnen Lichtklassen, welche unterschiedliche Methoden zum Erscheinen der Lichtquellen zur Verfügung stellen. Nachfolgend sollen diese Mechanismen genauer erklärt werden. 1.1 Shading Unter dem Begriff „Shading“ wird in Java eine Methode zum steuern des Verlaufs von Licht und Schatten auf einem beleuchteten Objekt verstanden. Dazu stellt Java 3D ein Lichtmodel und einige Funktionen zum steuern von Qualität und CPU-Nutzung zur Verfügung. Im weiteren werden die relevanten Sachverhalte des Shading behandelt. 1.1.1 Lighting Model Das Lighting Model simuliert die physikalischen Eigenschaften von Licht. Dabei werden drei Vektoren eingeführt, welche zur Berechnung der Winkel für ein- und ausfallendes Licht benötigt werden. Dies sind der Lichtvektor, der die Richtung in welcher die Lichtquelle liegt bestimmt, der Augenvektor, welcher in Richtung des Betrachters weist und der Vektor der Normalen des Objektes. Die Bedeutung der Surface Normalen wird weiter unten noch ausführlicher behandelt. (Quelle: Java 3D Sun Tutorial) Neben diesen drei Vektoren werden drei in der Realität vorkommende Reflektionstypen definiert: ambient, diffuse und specular. Dabei ist eine ambient-Reflektion die Spiegelung einer ambient-Lichtquelle, wobei die einzelnen Lichtklassen im weiteren Verlauf dieser Dokumentation noch erklärt werden. Die diffuse-Reflektion ist dagegen die normale Spiegelung einer Lichtquelle und eine specular-Reflektion simuliert die hellerleuchteten Stellen auf einem beleuchteten Objekt, welche in manchen Situationen durch direkte Beleuchtung von einer Lichtquelle entstehen können. (Quelle: Java 3D Sun Tutorial) 1.1.2 Qualität oder Schnelligkeit Wie oben bereits erwähnt bietet das Lichtmodel einen Augen- und einen Lichtvektor. Diese Vektoren können vom Programmierer als konstant oder nicht konstant gewählt werden. Ist zum Beispiel die Lichtquelle ein „directional Light“, so ist der Lichtvektor stets konstant. Während ein konstanter Vektor die Prozessorlast stark vermindert, bietet ein variabler Vektor dem Programmierer natürlich mehr Möglichkeiten in der Gestaltung einer Szene. Daher sollte die Wahl je nach Anforderung wohl überlegt sein. 1.1.3 Zusammenspiel von Objekten Java 3D generiert immer nur ein beleuchtetes Objekt auf einmal und kann daher Beziehungen zwischen Objekten, also zum Beispiel den Schatten des einen Objektes auf ein anderes zu werfen oder die Reflektion des einen Objektes als Lichtquelle auf dem anderen zu berücksichtigen, nicht realisieren. Dies ist erforderlich, da die Komplexität solcher Effekte die meisten Graphiksysteme überfordern würde. Um einen solchen Effekt dennoch zu erzielen, müssen daher auf anderem Wege, wie weiter unten erklärt, die Effekte später in den Szenengraph eingesetzt werden. 1.1.4 Farbmodel Als Farbmodel wird das bekannte RGB-Model verwendet. Mit diesem lassen sich eine große Anzahl von Farben generieren, welche auf der Mischung der Farben rot, grün und blau basieren. 1.1.5 Einflussbereich des Lichtes Um ein Objekt beleuchten zu können, muss es im Einflussbereich der Lichtquelle stehen. Dazu muss der Einflussbereich bei der Generierung einer Lichtquelle angegeben werden. Im einfachsten Fall kann die Methode setInfluencingBounds() mit einem bounds-Objekt aufgerufen werden. Weitere Möglichkeiten werden weiter unten noch aufgezeigt. 1.1.6 Shading Model Beim Schattieren eines Objektes wird jeder Vertex des Objektes einzeln schattiert. Dies geschieht, indem die Schatten, die von jeder Lichtquelle für den Vertex generiert werden, aufsummiert werden. Der Rest des Objektes wird dann abhängig von der gewählten Shading-Funktion auf Grund der Vertices schattiert. Dabei sind folgende Schading-Funktionen möglich: SHADE_GOURAUD SHADE_FLAT FASTEST NICEST Wobei bei gouraud jedes Pixel einzeln schattiert wird, währen bei flat jedes Pixel eines Polygons den gleichen Wert für den Schatten erhält. SHADE_FLAT (Quelle: Java 3D Sun Tutorial) SCHADE_GOURAUD 1.2 Lichtklassen in Java 3D Java 3D bietet vier unterschiedliche Lichtklassen, von denen drei von der Klasse Light erben. Dies sind die AmbientLight-, die DirectionalLightund die PointLight-Klasse. Von der PointLight-Klasse erbt wiederum die vierte Klasse, die Klasse SpotLight. 1.2.1 AmbientLight AmbientLight ist eine Lichtquelle, die überall in der Szene gleichermaßen auf die Objekte wirkt. Dabei ist das Licht meist ein schwacher Schimmer und soll den realen Sachverhalt von reflektiertem Licht darstellen, das von allen Objekten in einem Raum zurückgeworfen und in geringen Maße auf alle anderen Objekte wirkt, simulieren. Daher ist eine AmbientLightQuelle richtungsunabhängig, die Intensität ist an jeder Stelle gleich und bewirkt eine ambient-Reflektion bei einem Objekt. Wie bereits im Lighting Model gezeigt, ist eine ambient-Reflektion eine Graufärbung an dem Objekt. 1.2.2 DirectionalLight Möchte man eine sehr weit entfernte Lichtquelle simulieren, etwa die Sonne, so empfiehlt sich eine DirectionalLight-Quelle einzusetzen. Hierbei handelt es sich um eine Quelle, deren Intensität nicht mit zunehmender Entfernung der Lichtquelle zum Objekt abnimmt und bei welcher der Lichtvektor (siehe oben) für jeden Vertex der gleiche, also konstant, ist. (Quelle: Java 3D Sun Tutorial) Die DirectionalLight-Quelle ist dabei ein helles Licht, welches diffuseund speculart-Reflektionen auslöst. Im Gegensatz zur AmbientLightQuelle gibt es hier eine Richtung des Lichtes, so dass das Objekt schattiert wird. Ändert man die Richtung des Lichtes, ändert sich auch die Schattierung auf dem Objekt. 1.2.3 PointLight Um nun eine Lichtquelle in eine Szene einzusetzen, welche etwa eine Glühbirne oder Kerzenflamme simulieren soll, verwendet man ein PointLight. Diese Klasse von Licht strahlt gleichmäßig in alle Richtungen und die Intensität des Lichtes nimmt mit der Entfernung des Objektes zur Lichtquelle hin ab. Der Lichtvektor ist dabei für jeden Vertex abhängig von der relativen Position zur Lichtquelle und daher nicht konstant. Auch das PointLight erwirkt diffuse- und specular-Reflektionen. (Quelle: Java 3D Sun Tutorial) Das PointLight besitzt außerdem eine Position in der Szene. Diese Position ist entscheidend für das Shading. Mit sich Ändernder Position der Quelle ändert sich auch die Schattierung auf den beeinflussten Objekten. 1.2.4 SpotLight Unter einem SpotLight versteht man eine Lichtquelle, welche, ähnlich dem PointLight, eine Position im Raum besitzt und deren Lichtintensität mit der Entfernung von der Lichtquelle abnimmt. Natürlich werden auch von dieser Lichtklasse duffuse- und specular-Reflektionen ausgelöst. (Quelle: Java 3D Sun Tutorial) Anders als beim PointLight besitzt jedoch das SpotLight eine Richtung. Neben der Richtung wird auch noch ein Winkel, in dem das Licht ausgestrahlt wird, angegeben. Außerhalb des Winkels wird kein Licht produziert. Der Schatten, der vom Shading erzeugt wird, ist abhängig von dem Winkel, den der Richtungsvektor der Lichtquelle mit dem Lichtvektor des Objektes aufspannt. Folglich ändern sich die Schatten der Objekte, wenn sich die Richtung der Lichtquelle oder ihre Position verändert wird. Das SpotLight kommt immer dann zum Einsatz, wenn man zum Beispiel eine Taschenlampe oder eine Tischlampe mit Lampenschirm erzeugen möchte. 1.2.5 Hierarchie der Lichtklassen (Quelle: Java 3D Sun Tutorial) 1.3 Beleuchtete Szenen Zum generieren einer gesamten Szene mit Lichtquellen und beleuchteten Objekten sind mehrere Schritte notwendig. Neben dem Erstellen der Lichtquellen, müssen für jedes Licht der Wirkungsbereich festgelegt werden. Außerdem kann ein Objekt nur dann beleuchtet werden, wenn es eine Surface Normale und Material Eigenschaften besitzt. Java 3D benutzt, wie oben beschrieben, das Lighting Model aus den Materialeigenschaften eines Objektes, ohne welches das Objekt zwar koloriert, nicht aber mit Licht und Schatten dargestellt wird. Sollte auch die Surface Normale fehlen, so bleibt das Objekt völlig weiß. Gibt man ein Materialobjekt an, aber keine Normale, so wird eine Fehlermeldung ausgegeben und das Objekt kann nicht gerendert werden. Eine fehlende Lichtquelle oder das Weglassen oder Falschbestimmen des Einflussbereiches lässt dagegen ein Objekt von der Lichtquelle unberührt und ist dementsprechend schwieriger zu entdecken. (Quelle: Java 3D Sun Tutorial) 1.3.1 Erstellen einer Lichtquelle Eine neue Lichtquelle kann auf, für Java, gewöhnliche Weise mit dem „new“-Konstruktor erstellt werden: AmbientLight ambient = new AmbientLight(); DirectionalLight directional = new DirectionalLight(); PoinLight point = new PointLight(); SpotLight spot = new SpotLight(); AmbientLight Konstruktoren: AmbientLight() Produziert ein AmbientLight-Objekt mit default-Werten: lightOn true color (1, 1, 1) AmbientLight(Color3f color) AmbientLight(boolean lightOn, Color3f color) DirectionalLight Konstruktoren: DirectionalLight() Produziert ein DirectionalLight-Objekt mit default-Werten: lightOn true color (1, 1, 1) direction (0, 0, -1) DirectionalLight(Color3f color, Vector3f direction) DirectionalLight(boolean lightOn, Color3f color, Vector3f direction) PointLight Konstruktoren: PointLight() Produziert ein PointLight-Objekt mit default-Werten: lightOn true color (1, 1, 1) position (0, 0, 0) attenuation (1, 0, 0) PointLight(Color3f color, Point3f position, Point3f attenuation) PointLight(boolean lightOn, Color3f color, Point3f position, Point3f attenuation) SpotLight Konstruktoren: SpotLight() Produziert ein SpotLight-Objekt mit default-Werten: lightOn true color (1, 1, 1) position (0, 0, 0) attenuation (1, 0, 0) direction (0, 0, -1) spreadAngle PI (180 degrees) concentration 0.0 SpotLight(Color3f color, Point3f position, Point3f attenuation, Vector3f direction, float spreadAngle, float concentration) SpotLight(boolean lightOn, Color3f color, Point3f position, Point3f attenuation, Vector3f direction, float spreadAngle, float concentration) 1.3.2 Methoden der Lichtquellen (Auszug aus dem Java 3D Tutorial von Sun) 2. Material In der Erscheinung(appearence bundle) eines Gegenstandes, kann ein Objekt der Klasse Material, eine Unterklasse von NodeComponent, initiiert werden. Dieses Objekt enthält Werte für ambient-, diffuse-, specular-, emessiveFarben und außerdem noch einen Wert shininess. Aus dem Lighting Model kennen wir bereits die drei ersten Begriffe und in der Tat werden diese Werte dazu genutzt, die entsprechenden Reflektionen zu berechnen. Der emessiveColor Wert erlaubt es einem Gegenstand im Dunkeln zu leuchten und der Wert für shininess ist zusätzlich zur Ermittlung der specularReflektion benötigt. (Quelle: Java 3D Sun Tutorial) (Veränderung des shininess-Wertes) Um überhaupt die Nutzung von Licht zu ermöglichen ist das Material-Objekt unbedingt notwendig. Um einem sichtbare Objekt seine Materialeigenschaften zu übergeben, kann in die appearance das MaterialObjekt mit der setMaterial() Methode eingefügt werden. 2.1 Konstruktoren und Methoden für Material (Auszug aus dem Java 3D Tutorial von Sun) 3. Surface Normale Wie oben bereits erwähnt, wird für das Shading eine Surface Normale benötigt. Für ein geometrisches Objekt kann dazu eine der setNormal() Methoden verwendet werden. Auch ist bei Java 3D ein Normalengenerator enthalten. Dieser benutzt ein GemetryInfo-Objekt, um eine passende Normale zu erzeugen. Daher muss der Programmierer seine Geometrie in ein GeometryInfo-Objekt setzen und anschließend den Normalengenerator mit jenem Objekt aufrufen, um seiner Figuren mit Normalen auszustatten. Geometrische Primitive generieren übrigens ihre Normalen automatisch selbst. Es kann vorkommen, dass eine Fläche von beiden Seiten zu sehen ist, somit ihre Normale nur für die Oberseite gültig ist. Deshalb kann man mit der Methode setBackFaceNormalFlip() für die Unterseite eine eigene Normale generieren. (Quelle: Java 3D Sun Tutorial) Fläche mit Normalen auf Vor- Fläche mit nur einer und Rückseite Normalen auf der Oberseite 4. Einflussbereich des Lichtes festlegen Wir haben schon festgestellt, dass eine Lichtquelle einen Einflussbereich besitzt, in dem sie auf sich darin befindliche Objekte wirkt. Nachfolgend wollen wir die Möglichkeiten zum Festlegen dieses Bereichs besprechen. 4.1 setInfluencingBounds() Dies ist die einfachste Lösung zum Bestimmen des Einflusses der Lichtquelle. Man generiert ein bounds-Objekt und verwendet es als Parameter für die setInfluencingBounds() Methode. Beispiel: BoundingSphere myBounds = new BoundingSphere(); _ myLight.setInfluencingBounds( myBounds ); Erzeugt eine Kugel mit default-Werten, deren Mittelpunkt sich im Koordinatenursprung befindet. Alle sichtbaren Objekte, die sich innerhalb der Kugel befinden oder diese berühren, werden beleuchtet. Dabei spielt es keine Rolle, ob sich das Objekt voll in der Kugel befindet, denn sie bestimmt nur welche Objekte beleuchtet werden und nicht welche Teile dieser Objekte. Wird die Lichtquelle verschoben, dann verschiebt sich auch ihr Einflussbereich entsprechend in der Umgebung. Daher können zwei Lichtquellen, welche mit dieser Methode den gleichen Einflussbereich bestimmt haben, letztlich einen unterschiedlichen Einflussbereich haben, wenn nämlich eine der Lichtquellen verschoben wird. 4.2 setInfluencingBoundingLeaf() Eine weitere Möglichkeit den Einflussbereich einer Lichtquelle festzulegen, ist ein BoundingLeaf-Objekt zu erstellen. Ein BoundingLeaf wird direkt in den Szenen Graph eingefügt und kann daher unabhängig von der Lichtquelle verschoben werden. Die Methode setInfluencingBoundingLeaf() überschreibt die setInfluencingBounds() Methode und ersetzt so einen eventuell bestimmten Einflussbereich. Das erstellte BoundinLeaf-Objekt kann auch von mehreren Lichtquellen verwendet werden und die Lichtquellen unabhängig davon mit einer TransferGroup verschoben werden. Daher haben zwei Lichtquellen mit dem gleichen BoundingLeaf auch immer den gleichen Einflussbereich. (Quelle: Java 3D Sun Tutorial) Die beiden Lichtquellen werden durch eine TransferGroup bewegt, der Einflussbereich, ein BoundingLeaf, ändert sich jedoch dadurch nicht! 4.3 Beschränken des Einflussbereiches Hat man für eine Lichtquelle einen Einflussbereich bestimmt, ob mit einem bounds- oder mit einem BoundingLeaf-Objekt, ist es möglich mit einer sogenannten Scope den gewählten Bereich noch einzuschränken. Dies kann nützlich sein, um Objekte, welche nahe beieinander liegen, leichter zu trennen, um einen Teil zu beleuchten und einen anderen nicht. Dabei hat jede Lichtquelle als Standard eine Scope, welche der Scope der sichtbaren virtuellen Welt entspricht. Ein weiterer Vorteil einer Scope ist, dass die Renderingzeit verkürzt wird, da nur der Ausschnitt, welcher durch die Scope bestimmt ist, gerendert werden muss. (Quelle: Java 3D Sun Tutorial) Möglicher Szenen Graph für das Lciht-Scoping 5. Objekte im Dunkeln leuchten lassen Im Kapitel 2 „Material“ wurde erwähnt, dass einem Sichtbaren Objekt mit Material-Objekt ein emissiveColor-Wert mitgegeben werden kann. Es wurde bereits angesprochen, dass er dazu genutzt werden kann, um das Objekt im Dunkeln leuchten zu lassen. Dabei ist zu beachten, dass das Objekt nicht zu einer Lichtquelle wird, denn es leuchtet zwar, aber das Licht wirkt sich nicht auf andere Objekte aus. Die unbeleuchtete Box am hinteren Ende der Szene wurde mit emissiveColor zum leuchten gebracht. Man kann erkennen, dass die restlichen Objekte in der Szene davon nicht beeinflusst werden. (Quelle: Java 3D Sun Tutorial) 6. Schatten Die Berechnung von Schatten ist dermaßen komplex, dass es von Java 3D nicht unterstützt wird. Die Komplexität resultiert aus der Frage, ob irgend ein Punkt beleuchtet wird oder nicht. Dabei müssen alle anderen Punkte in der Szene, die sich zwischen dem betrachteten Punkt und der Lichtquelle befinden berücksichtigt werden. Dennoch gibt es viele Verfahren, um Schatten simulieren zu können. Eine einfache Technik werden wir nun nachfolgend besprechen. 6.1 Schatten Polygone Möchte man einen Schatten für ein bestimmtes Objekt generieren, so kann man ein Polygon in Form des gewünschten Schattens erstellen und an der benötigten Stelle in die Szene einsetzen. Gibt man dem Objekt keine Material Komponente, dann wird wie oben beschrieben das Objekt nicht beleuchtet, aber Koloriert. Folglich kann es schwarz dargestellt werden und damit einen Schatten simulieren. Da das Polygon scharf dargestellt wird und von keiner Lichtquelle beeinflusst wird, erscheint es meist unrealistisch. Gibt man dem Schatten eine Materialkomponente und lässt sie aus dem Einflussbereich einer oder mehrerer Lichtquellen raus, so erhält man einen sogenannten schattierten Schatten. Dieser wirkt etwas realistischer als ein Schatten der mit obiger Methode generiert wurde. Auf jeden Fall wird es sehr schwierig den Schatten mit einem Objekt mit zu bewegen. Um dies zu realisieren müssen viele Objekte erstellt werden, wodurch die Renderperformence deutlich verschlechtert werden kann.