Tutorial Lektion 8

Aus DGL Wiki
Version vom 20. Dezember 2005, 17:52 Uhr von Flo (Diskussion | Beiträge) (Uebertragung von Tutorial 8 ins Wiki)

(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Wechseln zu: Navigation, Suche

Das Wesen von hell und dunkel - Licht

Licht an

Hi, und wieder melde ich mich mit einem neuen Tutorial zurück. Heutiges Thema: Licht. Ohne Licht wäre die Umwelt dunkel und kein Pflänzchen könnte Zucker mittels Photosynthese erzeugen. Gäbe es kein Licht, so gäbe es die Welt nicht, so wie wir sie kennen. Um in den eigenen OpenGl Programmen eine auch nur annähernd realistische Atmosphäre zu erzeugen ist Licht entsprechend unabdingbar. Man kann Szenen dadurch einen düsteren, geheimnisvollen oder auch einen farbenfrohen, fast kitschigen Stil verleihen.

Arten des Lichts

In OpenGl gibt es unterschiedliche Arten des Lichts. Umgebungs Licht (Ambient) ist eine Art des Lichts, dessen Richtung nicht zu erkennen ist. Es ensteht durch mehrfache Refelktion an Wänden oder anderen Flächen. Entsprechend gut vertreten ist es in Räumen, aber auch in der freien Natur umgibt uns nicht zu wenig davon.

"Diffuses" Licht(Diffuse) dagegen lässt die Richtung aus der es kommt erkennen. Je stärker eine Seite in das Licht gehalten wird, desto heller erscheint die Oberfläche. Je weiter man sie herausdreht, desto dunkler wird die Oberfläche. Die eintreffenden Lichtstrahlen werden in alle Richtungen verteilt, so dass die Oberfläche von allen Augenpositionen gleich hell erscheint.

Glanz(Specular) kommt aus einer bestimmten Richtung und tendiert dazu in eine bestimmte Richtung gespiegelt zu werden. Platik, Metall und geschliffene Edelsteine, etc. haben einen grossen Glanzanteil, während Holz, kohle und Wandfarbe einen eher geringen Anteil haben.

Materialeigenschaften

Der vom Licht erzeugte Eindruck hängt aber nicht nur vom Licht selbst ab, sondern auch vom beleuchteten Matrial. Eine Kohleplatte wirkt im selben Licht anders, als eine polierte Edelstahloberfläche oder das Blech eines lackierten Autos. Deshalb gibt es in OpenGl die sog. Materialeigenschaften. Sie entsprechen denen des Lichts selbst, nur geben sie den Reflexionsgrad und nicht die Intensität wieder.

Objekte können nun aber auch selbst Licht emittieren. Deshalb befindet sich unter den Materialeigenschaften zusätzlich noch ein Emissionswert. Ein Objekt mit Emission ist also auch bei kompletter Dunkelheit noch sichtbar, so z.B. die grüne Leuchtdiode an meinen Boxen, wenn ich die Rolläden geschlossen habe. Schade nur, dass OpenGl dieses emittierte Licht nicht auch auf andere Objekte überträgt :-(.

Normalen

OpenGl berechnet die Farbe des Lichts, das auf ein Objekt trifft mithilfe der Richtung aus der das Licht kommt und der Ausrichtung der Oberfläche eines Vertex. Nun stellt sich die Frage: in welche Richtung zeigt denn die Oberfläche? Wir können bei der Übergabe der Vertexdaten einfach mit glNormalx auch einfach die Normalen mitgeben. Normalerweise sollte die Länge des übergebenen Vektors = 1 sein. Bei einer Kugel entspricht die Normale übrigens immer der Richtung des Mittelpunktes zum betrachteten Vertex. Für andere Körper gibts weiter unten ein Paar Tipps zum Berechnen der Normalen.

Erste Lichtstrahlen

Licht anwerfen

Tutorial lektion8 test.gif

Es wird Zeit, die ersten Gehversuche mit dem OpenGl Licht zu bewerkstelligen. Wir brauchen dazu eigentlich nicht viel zu tun. Wir müssen am Anfang das Licht mittels glEnable(GL_LIGHTING) erst einmal scharf schalten und dann einen unserer Lichtquellen aktivieren. Das geht mittels glEnable(GL_LIGHTx) (Zur Information: normale OpenGl Implementationen unterstützen 8 Lichter. Es gibt aber Implementationen, die wesentlich mehr unterstützen. Um Zugriff auf alle vorhandenen Lichter zu erhalten, muss man einfach zu GL_LIGHT0 einen entsprechenden Wert hinzuaddieren. GL_LIGHT0+8 wäre dann z.B. das Licht #9).

  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);

Lichtquelleneigenschaften übergeben

Der nächste Schritt besteht darin, die Eigenschaften der Lichtquelle(n) an OpenGl weiterzureichen. Dies läuft immer mit der Funktion glLightx. Sie nimmt 3 Parameter: light: Licht das gerade eingestellt wird, z.B. GL_LIGHT0. pname: Eigenschaft, die gerade eingestellt wird. params: Neuer Wert für die Eigenschaft.


Es stehen eine ganze Reihe Eigenschaften zur Verfügung:

Eigenschaft Standard Wert Bedeutung
GL_AMBIENT 0.0, 0.0, 0.0, 1.0 Ambienter RGBA Anteil des Lichts
GL_DIFFUSE 1.0, 1.0, 1.0, 1.0 Diffuser RGBA Anteil des Lichts
GL_SPECULAR 1.0, 1.0, 1.0, 1.0 Glanz RGBA Anteil des Lichts
GL_POSITION 0.0, 0.0, 1.0, 0.0 Koordinaten des Lichts
GL_SPOT_DIRECTIONT 0.0, 0.0, -1.0 Richtung des Spotlights
GL_SPOT_EXPONENT 0.0 Spotlight-Exponent
GL_SPOT_CUTOFFT 180.0 Spotlight Sperrwinkel
GL_xxx_ATTENUATION ... Abschwächung des Lichts mit der Entfernung

Materialeigenschaften übergeben

Das gleiche Spielchen müssen wir jetzt auch mit den Materialeigenschaften treiben. Diesmal allerdings mit der Funktion glMaterialx. Die Parameter sind die gleichen wie bei glLightx.

Eigenschaft Standard Wert Bedeutung
GL_AMBIENT 0.2, 0.2, 0.2, 1.0 RGBA Reflexionsgrad von ambientem Licht
GL_DIFFUSE 0.8, 0.8, 0.8, 1.0 RGBA Reflexionsgrad von diffusem Licht
GL_SPECULAR 0.0, 0.0, 0.0, 1.0 RGBA Glanzreflexionsgrad des Lichts
GL_SHININESS 0.0 Glanz-Exponent
GL_EMISSION 0.0, 0.0, 0.0, 1.0 RGBA Emission des Materials
GL_COLOR_INDEXES 0, 1, 1 Ambient, diffuse, specular Farbindices

Beispiellicht

Das obige Beispiel mit den Kugeln wurde mit den folgenden Code erzeugt:

  procedure TStationarySphereState.RenderState;
  const
    mat_specular   : Array[0..3] of GlFloat = (1.0, 1.0, 1.0, 1.0);
    mat_shininess  : Array[0..0] of GlFloat = (50.0);
    mat_ambient    : Array[0..3] of GlFloat = (0.4, 0.4, 0.4, 1.0);
    mat_diffuse    : Array[0..3] of GlFloat = (0.4, 0.8, 0.4, 1.0);

    light_position : Array[0..3] of GlFloat = (10.0, 10.0, 0.0, 1.0);
    light_ambient  : Array[0..3] of GlFloat = (0.8, 0.8, 0.8, 1.0);
    light_diffuse  : Array[0..3] of GlFloat = (0.8, 0.8, 0.8, 1.0);

  begin
    glMaterialfv(GL_FRONT, GL_SPECULAR,  @mat_specular[0]);
    glMaterialfv(GL_FRONT, GL_SHININESS, @mat_shininess[0]);
    glMaterialfv(GL_FRONT, GL_AMBIENT,   @mat_ambient[0]);
    glMaterialfv(GL_FRONT, GL_DIFFUSE,   @mat_diffuse[0]);

    glLightfv(GL_LIGHT0, GL_AMBIENT,  @light_ambient[0]);
    glLightfv(GL_LIGHT0, GL_DIFFUSE,  @light_diffuse[0]);
    glLightfv(GL_LIGHT0, GL_POSITION, @light_position[0]);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    ...Szene Rendern...
  end;

Etwas schwächer bitte

Viele Lichtquellen werden mit der Entfernung schwächer. Bei unserer Sonne tritt dieser Effekt so gut wie nicht auf, bei kleinen Funzeln dagegen ist recht bald die maximale Ausleuchtweite erreicht. Diesen Effekt können wir natürlich auch mit OpenGl erzeugen.

Tutorial lektion8 attenuation.gif

Auf diesem Bild ist schön zu erkennen, dass die Kugeln rechts oben heller sind als die links unten, die weiter von der Lichtquelle entfernt sind (wer nichts sieht, spiele doch mal bitte mit dem Kontrast und der Helligkeit des Monitors - ich kann nicht versprechen, dass der Effekt auf jedem Monitor so schön wie auf meinem herauskommt). Wie wird nun dieser Effekt erzeugt?

OpenGl kennt einen sog. Attenuation Factor:

Attenuation Factor = 1 / (kc + kl*d + kq*d*d)

  • d = Entfernung zwischen dem berechneten Vertex und der Lichtquelle
  • kc = GL_CONSTANT_ATTENUATION
  • kl = GL_LINEAR_ATTENUATION
  • kq = GL_QUADRATIC_ATTENUATION

Dieser Faktor bei der Lichtberechnung einfach mit den Lichtstärken multipliziert, so dass das Licht schwächer wird. Übergeben werden die Werte wieder mittels glLightx:

  glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0);
  glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.001);
  glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.004);

Das Positionsproblem

Lichter sind unterschiedlich - Manche sind fest an den Betrachter der Szene gekoppelt, andere irgendwo fest in der Szene und wieder andere sind unendlich weit entfernt und ihr Licht trifft parallel auf die Gegenstände. All dies will simuliert werden. OpenGl hält alles parat, was wir dafür benötigen. Uns kommt auch noch zugute, dass Openg die Übergabe der Licht Positions genauso behandlet wie ein Vertex und entsprechend durch die Modelview Matrix jagd.

Unendlich weit entfernte Lichter

Das Universum, die Milchstarsse, der äussere Ring, unser Sonnensystem, die Sonne und unsere Erde. Das Licht, das unsere Sonne ausstrahlt und bei uns auf der Erde ankommt, trifft uns in nahezu parallelen Lichtstrahlen. Damit ist die Sonne eine unendlich entfernte Lichtquelle. Wenn wir eine Solche Lichtquelle angeben wollen, ist also nicht ihre Position intressant(wobei das auch ginge), sondern allein die Richtung aus der das Licht kommt. Um das mit OpenGl zu bewerkstelligen, müssen wir die w-Koordinate(=die letzte Koordinate) der Position bei der Übergabe auf 0 setzen und voila. Unsere unendlich weit entfernte Lichtquelle ist komplett. Sinnigerweise ist dann im übrigen auch Attenuation abgeschaltet.

Stationäre Lichtquellen

Lichtquellen, die an einer bestimmten Stelle in der Szene stehen, müssen mit der Kamerabewegung mitgeschoben werden. Entsprechend muss das Licht angegeben werden, bevor die Modelviewmatrix durch Objekte verschoben wird, aber nachdem die Kamera initialisiert wurde.

  glLoadIdentity;
  //..glRotatef, glTransaltef.. für die Kamera
  glLightfv(GL_LIGHT0, GL_POSITION, @position[0]);
  glPushMatrix;
    //..glRotatef, glTransaltef.. für die Objekte
    DrawObjekts;
  glPopMatrix;

Relatives Licht

Wollen wir stattdessen unsere Lichtquelle relativ zu einem bestimmten Objekt bewegen, so müssen wir ein wenig anders vorgehen: erst Modelviewmatrix so bearbeiten, dass wir das Objekt zeichen könnten, vor dem Zeichen selbst jedoch die Matrixfunktion für das Licht ausführen und die Lichtposition übergeben:

  //..glRotatef, glTransaltef, glMultMatrix, etc...
  glPushMatrix;
    glRotatef(Drehung, 0.0, 1.0, 0.0);
    glLightfv(GL_LIGHT0, GL_POSITION, @position[0]);
  glPopMatrix;
  DrawObject;

Lichtquellen, die an der Kamera kleben

Wenn eine Lichtquelle immer relativ zum Betrachter sein soll(wenn also das Licht z.B. feste am Kopf montiert ist, wie bei einem Minenarbeiter), so langt es die Position des Lichts zu definieren, bevor die Modelviewmatrix mit der Kameraposition und Drehung bearbeitet wurde:

  glLoadIdentity;
  glLightfv(GL_LIGHT0, GL_POSITION, @position[0]);

Spot an!

Wer kennt sie nicht, die Spotlights? Im Fernsehen und Theater oft gebraucht, aber auch im Realen leben, z.B. bei Taschenlampen, Suchscheinwerfern, etc. oft zu sehen. Häufig benötigt man eine ganze Schaar von ihnen, dort eigen sich die OpenGl Lichter nur schlecht oder gar nicht. Man nehme hier z.B. eine Lightmap um einen äquivalenten Effekt zu erzielen. Bei wenigen, oder wenn man mit entsprechenden Techniken noch keine Erfahrungen hat, eignen sich die OpenGl Spotlights vorerst einmal besser.

Parameter setzen