Object3D und dessen Beeinflussung durch Licht

Started by Marlon, November 08, 2011, 01:03:14 PM

Previous topic - Next topic

Marlon

Mittels setLighting() kann bei einer Instanz der Klasse Object3D festgelegt werden, ob sie von Lichtquellen beeinflusst werden soll oder nicht. Dabei spielt AmbientLighting keine Rolle.
Reduziere ich AmbientLighting nun auf ein Minimum (um beispielsweise eine dunkle Waldszene darzustellen), so sind Objekte, die durch Licht nicht beeinflusst werden sollen kaum noch sichtbar.

Dieses Problem äussert sich bei mir bei der Darstellung von Partikeln, genauer gesagt planen Ebenen, die eine Feuertextur besitzen und nicht durch das eigene Licht (alle Partikel zusammen ergeben ein Feuer, welches ein Licht ausstrahlt) bestrahlt werden sollen. Stelle ich ein, dass diese nicht durch Licht beeinflusst werden sollen, so sind sie kaum sichbar, im anderen Fall sind die Partikel viel zu hell und kaum als Feuer zu erkennen.
Ist es möglich, dass ein Objekt nur auf manche Lichtquellen reagiert oder gibt es eine andere Möglichkeit dieses Problem zu bewältigen?
www.forgottenelements.com
Free Action JAVA MMORPG

Marlon

Ich habe mich noch länger mit dem Lighting System befasst.

Ich erstelle eine Ebene...

        plane = Primitives.getPlane(20, 120);
        plane.rotateX(PI / 2f);
        plane.rotateMesh();
        plane.setRotationMatrix(new Matrix());
        plane.setTexture("grass");
        plane.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
        plane.setCollisionOptimization(Object3D.COLLISION_DETECTION_OPTIMIZED);
        tileTexture(plane, 16);


...und ein Licht, welches vom Spieler "getragen" (oberhalb des Kopfes positioniert) werden soll.

        characterlight = new Light(world);
        characterlight.setIntensity(196, 180, 150);
        characterlight.setAttenuation(256);


Dieses Licht wird nach jeder Bewegung aktualisiert:

        SimpleVector vec = character.getTransformedCenter();
        vec=new SimpleVector(vec.x, vec.y-40, vec.z);
        characterlight.setPosition(vec);


Seltsamerweise wird die Ebene bei Bewegung des Charakters in gleichmässigen Abständen bei einigen Stellen mehr und bei anderen Stellen weniger beleuchtet.
Ich habe schon versucht, das Licht zu rotieren, das hat das Problem jedoch nicht lösen können (ausserdem sollte das Licht ja standartmässig korrekt rotiert sein, sprich es zeigt auf den Charakter. Ich hoffe das stimmt.).

Desweiteren scheint es nicht möglich zu sein, aus einem Licht einen klassischen Lichtradius (finstere Höhle, nur ein kleiner Bereich um den Charakter ist sichtbar) zu machen.  light.setDiscardDistance() und light.setAttenuation() liefern hier leider nicht das gewünscht Ergebnis.
Gibt es hier Möglichkeiten, von denen ich noch nicht weiss?

Ich würde mich sehr auf eine Antwort freuen! :)

P.S.: Ich werde die Tage ein komplexes Beispiel zur Verfügung stellen, welches die JPCT-Grundlagen zur Erstellung eines isometrischen Rollenspiels näherbringen sollte.
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

Zum ersten Post (Sorry, ich habe irgendwie verpeilt, zu antworten...): Die kannst den Partikeln eine zusätzliche Farbe zuweisen (setAdditionalColor()). Damit werden sie unabhängig vom Ambientlight heller.

Zum zweiten Post: Ich vermute, du meinst so eine Art Pulsieren der Helligkeit, wenn du dich bewegst!? Das liegt an der Art, wie OpenGL Beleuchtung realisiert. jPCT nutzt Vertex-Lighting, d.h. die Beleuchtungsstärke wird nur an den Eckpunkte der Dreiecke berechnet und dann über die Fläche interpoliert. D.h. eine Fläche dann in der Mitte niemals stärker beleuchtet sein, als an den Ecken. Wenn man nicht nun von einer Ecke entfernt, nimmt die Lichtstärke dort ab und damit nimmt sie auf der ganzen Fläche ab. Dadurch sieht das Licht etwas uneinheitlich aus, wenn man sich zwischen den Eckpunkten bewegt (was man ja zwangsläufig tut).
Das ist erstmal eine systembedingte Schwäche...über die normale Beleuchtung durch OpenGL kann man da nichts gehen tun. Du kannst den Detailgrad der beleuchteten Flächen erhöhen, dadurch vermindert sich der Effekt.
Alternativ kannst du mit Shadern arbeiten, aber kann verlierst du die Unterstützung durch den ShadowHelper und musst den ganzen Schattenkram ebenfalls im Shader von Hand machen.
Ich würde es (wenn möglich) erstmal mit einer Detailerhöhung des Bodens probieren...*2 sollte schon viel besser aussehen.

Marlon

Ah, alles klar, danke für die Hilfe! :)

Nochmal zum ersten Problem: Ich habe mit setAdditionalColor() gearbeitet. Allerdings färbt sich das ganze Object mit der zusätzlichen Farbe, was nicht erwünscht ist, da transparente Teile des Bildes auf der Plane nicht zu sehen sein sollen.

Zu Punkt 2: Ja, ich habe auch überlegt die Anzahl der Polygone der Plane zu erhöhen, allerdings würde die Anzahl ja exponentiell steigen und bei 100X100 bereits 10000 betragen. Aber ich bin mir sicher, es lässt sich hier ein akzeptabler Mittelweg finden. Weisst du eigentlich wie viele Polygone (Faces) bei einer durchschnittlichern Grafikkarte und Rechenleistung bei 25 FPS angezeigt werden können?

Nochmal zu dem Problem mit dem Lichtradius. Es scheint wohl nicht möglich zu sein, aus einem Licht einen klassischen Lichtradius (finstere Höhle, nur ein kleiner Bereich um den Charakter ist sichtbar) zu machen.  light.setDiscardDistance() und light.setAttenuation() liefern hier leider nicht das gewünscht Ergebnis.
Gibt es hier Möglichkeiten, von denen ich noch nicht weiss?
Und wohin zeigt der Lichtkegel eigentlich standartmässig?
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

Es kommt darauf an, mit welcher Renderpipeline du jPCT benutzt. Im Software-Modus oder mit der Hybrid-Pipeline, liegt das Polygonlimit sehr viel niedriger, als wenn du mit kompilierten Objekten arbeitest (http://www.jpct.net/wiki/index.php/Compiled_objects). Eine generelle Aussage kann man nicht treffen, zumal schon "durchschnittliche Grafikkarte" problematisch ist. Was ist das?

Das Licht breitet sich nach allen Seiten hin von der Lichtquelle ausgehend aus. Wieso funktionieren DiscardDistance und Attenuation nicht? Sollten sie eigentlich. Vielleicht haben wir eine unterschiedliche Vorstellung vom Resultat. Ein Screenshot könnte helfen...

EgonOlsen

Einfaches Beispiel für einen Lichtklecks auf einer Ebene:


import com.threed.jpct.*;
import com.threed.jpct.util.Light;


/**
* A simple HelloWorld using the OpenGL-renderer.
*
* @author EgonOlsen
*
*/
public class HelloWorld
{
  // Change to make lighting more/less detailed by changing the detail level of the plane
  private static int TESSELLATION = 20;

  // Change to fade out faster/slower (higher is slower, lower faster)
  private static int ATTENUATION = 100;

  private World world;
  private FrameBuffer buffer;
  private Object3D plane;


  public static void main(String[] args)
    throws Exception
  {
    new HelloWorld().loop();
  }


  public HelloWorld()
    throws Exception
  {
    world = new World();
    world.setAmbientLight(50, 50, 50);

    Light light = new Light(world);
    light.setAttenuation(ATTENUATION);
    light.setIntensity(255, 255, 255);
    light.setPosition(new SimpleVector(0, 0, -10));

    plane = Primitives.getPlane(TESSELLATION, 10f * (10f / (float) TESSELLATION));
    plane.build();
    plane.compile();
    world.addObject(plane);

    world.getCamera().setPosition(0, 0, -150);
  }


  private void loop()
    throws Exception
  {
    buffer = new FrameBuffer(800, 600, FrameBuffer.SAMPLINGMODE_NORMAL);
    buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
    buffer.enableRenderer(IRenderer.RENDERER_OPENGL);

    while (!org.lwjgl.opengl.Display.isCloseRequested())
    {
      buffer.clear(java.awt.Color.BLACK);
      world.renderScene(buffer);
      world.draw(buffer);
      buffer.update();
      buffer.displayGLOnly();
      Thread.sleep(10);
    }
    buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
    buffer.dispose();
    System.exit(0);
  }
}


Marlon

#6
Danke für das Programm!

Tatsächlich wird dort ein schöner Lichtradius angezeigt, wenn man world.setAmbientLight(0, 0, 0) setzt.
Auch verdeutlicht das Programm sehr gut das Phenomen, das auftritt, wenn die Polygonanzahl der Plane zu gering ist.
Genau das war mein Problem. Habe das jetzt bei meinem Programm angepasst und es schaut besser aus! :)

Allerdings ist das Problem mit dem Lichtradius dadurch bei meinem Programm nicht behoben. Ich habe alle Lichtquellen ausgeschaltet zum Test, danach war der gesamte Bildschirm schwarz, beziehungsweise nur noch das Menuobjekt zu sehen.
Danach habe ich das Licht eingeschaltet, welches an den Charakter gebunden wird:


        characterlight = new Light(world);
        characterlight.setIntensity(255, 255, 255);
        characterlight.setAttenuation(200);


Dieses wird bei jedem Loop aktualisiert:

        SimpleVector vec = character.getTransformedCenter();
        vec=new SimpleVector(vec.x, vec.y-40, vec.z);
        characterlight.setPosition(vec);


Leider wird dabei die komplette Landschaft erhellt. Im Hintergrund ist die Lichtintesität zwar nicht so stark, trotzdem bekommt man nicht den Eindruck vermittelt, der Charakter würde eine Fackel mit sich tragen und nur die 2 Meter um sich herum erhellen. Komischerweise tritt das nur bei der Plane und den Bäumen auf, das Feuer ist tatsächlich nicht mehr sichtbar, wenn der Charakter sich zu weit davon entfernt.
Auf dem ersten angehangenen Bild ist das hoffentlich recht gut zu erkennen.

--------------------------------------------------------

Füge ich nun folgendes ein...


        characterlight.setDiscardDistance(75f);


...wird die Plane komplett unsichtbar, wenn man 3 Schritte gelaufen ist. Die Bäume werden dann entweder ganz sichtbar oder sind überhaupt nicht zu sehen, man hat nicht das Gefühl das sie langsam vom Lichtradius erfasst werden, wenn der Charakter sich ihnen nähert. Ich hoffe das zweite Bild verdeutlicht diese Problematik recht deutlich.

---------------------------------------------------------

Und nun nochwas zur Ausrichtung des Lichts. Dass das Licht nach allen Seiten hin ausgebreitet wird stimmt nicht so ganz. Wenn ich mittels light.rotate(vector1, vector2) das Licht rotiere, so wird die Lichtintensität in manchen Bereichen grösser. Ich habe das getestet. Zwar wird alles ausgeleuchtet, aber nicht gleichermassen. Möchte man jedoch, dass das Licht zu allen Seiten hin gleichermassen ausgesendet wird, lässt man light.rotate(vector1, vector2) am besten unangetastet? In welche Richtung zeigt denn dann das Licht standartmässig, nach oben oder nach unten?

P.S.: Ups.. ich habe gerade gesehen, dass man gar keine Attachments anfügen kann. Ich werde das dann später von zu Hause aus machen und hier editieren. Ich kann dir die Screenshots aber auch gerne per Mail zukommen lassen wenn du magst, Egon. Schick mir in diesem Fall doch einfach ne PM. Danke!

Edit: So hier die versprochenen Screenshots:

www.forgottenelements.com
Free Action JAVA MMORPG

Marlon

Ich habe wie versprochen mittlerweile meinen letzten Post editiert und die Screenshots hinzugefügt. Ich hoffe dass diese das Problem ein wenig verdeutlichen.
Ausserdem habe ich noch ein wenig weitergebastelt und stelle hier einen aktuellen Screenshot zur Verfügung:
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

#8
Die Lichtintensität nimmt mit der Entfernung linear ab. Von daher entsprechen die Ergebnisse dem, was ich erwarten würde. Ich persönlich finde das jetzt auch nicht hässlich oder unecht oder so...sieht doch eigentlich gut aus...!? Was das Vertexlighting angeht, sehe ich jetzt auch nicht, was man da groß ändern sollte. Von der discardDistance würde ich die Finger lassen. Die ist eigentlich eher für den Softwarerenderer gedacht gewesen. Besonders bei kompilierten Objekten verhält sie sich anders und ist vermutlich nicht gewinnbringend einsetzbar...siehe deine Screenshots. Wenn es umbedingt ein harter Lightkegel sein soll, kann man vielleicht mit Projective Texturing was machen...müsste ich aber selber mal erstmal sehen, ob das vernünftig geht.

Was die Sache mit dem rotate angeht: Doch, das Licht breitet sich in alle Richtungen gleich aus. Du änderst durch das Rotate nur die Position der Lichtquelle und damit die Beleuchtung. Einen Lichtkegel im eigentlichen Sinne gibt es nicht.

Marlon

Alles klar, das gibt ein wenig Aufschluss! :)
Ich bin eigentlich auch zufrieden mit der Waldbeleuchtung. Bloss kann ich mir auch vorstellen, dass es Waldszenen gibt, die weniger beleuchtet sind, dafür das Licht vom Charakter eine gewisse Atmosphäre schafft. Auch in Höhlen stelle ich mir sowas vor. Aber eventuell kann ich das erreichen, indem ich ein halbtransparentes png darüber lege. Dürfte nur beim Zoom etwas blöd aussehen :/.
Aber ich denke, das kann man später noch perfektionieren, das sollte für eine erste Version erstmal reichen. Kleine Schritte gehen, um zum grossen Ziel zu gelangen...
Und das mit der Rotation der Lichtquellen löst nach deiner Erklärung nun endlich meine Verwirrung. Ich dachte, dass sich rotate() beim Licht genauso verhält wie bei den Objekten (wegen dem Namen). Aber um so besser wenn es nicht so ist. Ich persönlich werde das auch nicht brauchen und so bleibt dann auch alles überschaubarer! ::)
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

Also bzgl. des Lichtradius...ich habe das mit dem projective Texturing mal bei einem modifizierten Car-Beispiel ausprobiert. Sieht dann so aus:



Das hat aber nicht nur Vorteile. Es ist komplizierter zu verwenden, weil jedes zu beleuchtende Objekt eine zusätzliche Texturebene bekommen muss und das Licht kommt halt wie von einem Scheinwerfer von oben. Im Beispiel stört das nicht, aber für Innenräume könnte das komisch aussehen. Ich würde das mal als Machbarkeitsstudie betrachten und erstmal mit dem normalen Vertexlighting weitermachen...

Marlon

Wow super! Mich würde echt brennend der Quellcode interessieren. Könntest du mir den irgendwie schicken, bzw. die Änderungen hier posten?

Noch eine Frage, die mich zur Zeit ziemlich beschäftigt (sorry für Offtopic): Ist es möglich, die Koordinate des tiefsten Punkts eines Meshs zu erhalten?
Mir geht es darum, Objekte auf einer Ebene zu positionieren und mit der Methode, die bei der Cardemo angewendet wird komme ich nicht weiter, da die Objekte nicht unbedingt wie Reifen immer ihren tiefsten Punkt in der Mitte haben.
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

#12
Hier ist das komplette Eclipse-Projekt: http://jpct.de/download/tmp/Car.zip. Es ist nur als Studie gedacht, von daher ist es weit von Perfektion oder wirklich hübschem Aussehen entfernt. Aber vielleicht hilft es. Mit "L" kannst den Effekt an/ausschalten.

Was den tiefsten Punkt angeht: Du könntest die Bounding-Box des Meshs nehmen und daraus den tiefsten Punkt errechnen. Wenn das nicht genau genug ist, müsstest du über einen VertexController direkt auf die Geometrie zugreifen, um den tiefsten Punkt zu finden.

Bild:

Marlon

Danke für das Beispiel! :O)
Mit einer Textur (spotlight) wird dieser Effekt also erzeugt!
Ich habe es mal versucht auf mein Programm anzuwenden, habe es jedoch noch nicht geschafft.
Aber ich werde mich später noch damit beschäftigen, sobald ich die anderen Sachen abgeschlossen habe.

Eine wichtige Frage noch: Wie entferne ich ein Light? World bietet lediglich removeAllLights() an, da müsste ich dann nochmal alle Lichter hinzufügen, die nicht gelöscht werden sollen, ziemlich unhandlich. Ausserdem gibt es ja eine Grenze für Lichter, die ist schnell erschöpft, wenn man einige Lichter hinzufügt und wieder entfernt. Desshalb bringt mich Light.disable() auch nicht wirklich weiter. Irgendwelche Tipps?

Viele Grüsse,
Marlon
www.forgottenelements.com
Free Action JAVA MMORPG

EgonOlsen

Die Idee mit der Spot-Textur ist, jedem Objekt eine zusätzliche Texturlage mit dieser Textur zu verpassen. Die Textur selber bekommt dann einen Projector zugewiesen. Damit wird sie zu einer Projective Texture. Der Projector muss dann von oben auf die zu "beleuchtende" Szene gerichtet werden. Das ist natürlich keine wirkliche Beleuchtung, sondern nur eine Aufhellung in einem bestimmten Radius, d.h. es berücksichtigt z.B. nicht, dass Rückseiten von Objekten nicht beleuchtet werden. Aber evtl. kann man es mit normalem Vertexlighting kombinieren, damit man auch diesen Effekt hat.

Das mit den Lichtern ist in der Tat unschön. Ich habe es immer so gelöst, dass ich einen einfachen Pool geschrieben habe, der einfach die nächste freie Lichtquelle liefert oder eine neue erzeugt, wenn möglich und nötig. In diesem Thread gibt es eine Implementation dazu: http://www.jpct.net/forum2/index.php?topic=1288.0