Betrachten wir die erste Voraussetzung. In der Realität wird das Erscheinungsbild eines Körpers nicht nur durch Primär-Lichter (also Lichtquellen wie die Sonne, Lampen, Feuer, Taschenlampen, etc.) beeinflusst sondern auch durch sogenanntes sekundäres (von den Körper-Oberflächen reflektiertes) Licht. Sie kennen es alle, selbst am schlimmsten Regentag, wenn von der Sonne (eine direktionale Lichtquelle) nicht die geringste Spur zu entdecken ist, herrscht keine absolute Dunkelheit. Es gibt noch genügend Licht, dass sich ohne eine ausgezeichnete Richtung durch den Raum bewegt und in der Umgebung wieder und wieder reflektiert wird. Man spricht hier von der ambienten Reflexion. In modernen Computerspielen wird diese zunehmend im Rahmen des Post Processings berücksichtigt (Screen Space Ambient Occlusion Rendering, Global Illumination), in ihrer einfachsten Form wird die ambiente Reflexion lediglich durch einen konstanten Farbwert berücksichtigt, welcher im Fragment Shader mit der Texturfarbe multiplikativ verknüpft wird:
Ambiente Reflexion
// Rückgabewert des Fragment Shaders berechnen,
// Texturfarbe und Zusatzfarbe multiplikativ verknüpfen:
gs_FragColor = AmbientColor*SurfaceTextureColor;
// die Pixelfarbe berechnet sich hierbei wie folgt:
// gs_FragColor = vec4(AmbientColor.r*SurfaceTextureColor.r,
// AmbientColor.g*SurfaceTextureColor.g,
// AmbientColor.b*SurfaceTextureColor.b,
// AmbientColor.a*SurfaceTextureColor.a);
// Texturfarbe und Zusatzfarbe multiplikativ verknüpfen:
gs_FragColor = AmbientColor*SurfaceTextureColor;
// die Pixelfarbe berechnet sich hierbei wie folgt:
// gs_FragColor = vec4(AmbientColor.r*SurfaceTextureColor.r,
// AmbientColor.g*SurfaceTextureColor.g,
// AmbientColor.b*SurfaceTextureColor.b,
// AmbientColor.a*SurfaceTextureColor.a);
Kommen wir nun zu Voraussetzung 2, der Oberflächenbeschaffenheit eines Körpers, die eine Reflexion von Licht überhaupt erst ermöglicht. Obwohl sie sich sowohl auf die Reflexion von primärem wie auch sekundärem Licht gleichermaßen auswirkt, werden bei einfachen Beleuchtungsmodellen nur primäre Lichtquellen berücksichtigt. Die Reflexion von sekundärem Licht (ambiente Reflexion) wird separat behandelt. Die zweite Vereinfachung betrifft die Oberflächenbeschaffenheit selbst. Man unterscheidet zwischen rauen, glatten und spiegelnden Oberflächen.
Diffuse Reflexion
Bei rauen Oberflächen wird das eingestrahlte Licht lediglich diffus reflektiert. Die diffuse Reflexion ist einmal ausgenommen von der einfachen ambienten Reflexion mathematisch am einfachsten zu beschreiben. Ihr liegt die Alltagserfahrung zugrunde, dass eine beleuchtete Oberfläche umso heller erscheint, je kleiner der Einfallswinkel des Lichts ist. Treffen die Lichtstrahlen senkrecht auf die Oberfläche (Einfallswinkel = 0°), dann ist die Helligkeit maximal, bei einem Einfallswinkel von 90° ist die Helligkeit gleich null.
Bei der Berechnung des diffusen Reflexionsanteils werden im Fragment Shader die Lichtintensität, die Lichtfarbe sowie die Texturfarbe multiplikativ miteinander verknüpft:
gs_FragColor = max(dot(NormalPerTexel, negLightDir), 0.0)*
LightColor*SurfaceTextureColor;
// Hinweis:
// Mithilfe der max()-Funktion lassen sich negative Lichtintensitäten
// vermeiden.
// In der obigen Gleichung ist die kleinstmögliche Intensität gleich 0.0.
LightColor*SurfaceTextureColor;
// Hinweis:
// Mithilfe der max()-Funktion lassen sich negative Lichtintensitäten
// vermeiden.
// In der obigen Gleichung ist die kleinstmögliche Intensität gleich 0.0.
Kombination von ambienter und diffuser Reflexion
Für die Kombination von ambienter und diffuser Reflexion bieten sich mehrere Möglichkeiten an. Betrachten wir zwei Beispiele:
gs_FragColor = (AmbientColor +
max(dot(NormalPerTexel, negLightDir), 0.0)*LightColor)*
SurfaceTextureColor;
max(dot(NormalPerTexel, negLightDir), 0.0)*LightColor)*
SurfaceTextureColor;
gs_FragColor = max(dot(NormalPerTexel, negLightDir), AmbientValue)*
LightColor*SurfaceTextureColor;
LightColor*SurfaceTextureColor;
Spiegelnde Reflexion
Spiegelnde Reflexionen treten an spiegelnden Oberflächen bzw. an glatten Oberflächen in Kombination mit ambienter und diffuser Reflexion auf. Genau genommen handelt es sich dabei um Spiegelungen einer oder mehrerer Lichtquellen. Mit Taschenlampe und Spiegel können Sie sich davon überzeugen, dass die Intensität der Spiegelung vom Winkel (genauer vom Skalarprodukt) zwischen dem reflektierten Lichtstrahl und der Betrachter-Blickrichtung abhängig ist. Bei einem Winkel von 0° blickt man direkt in den reflektierten Lichtstrahl und die Intensität erreicht ihr Maximum. Die Größe der Reflexionsfläche (des Glanzpunkts – eine weitere übliche Bezeichnung) wird durch Verwendung eines zusätzlichen Exponenten n eingestellt. Die nachfolgende Abbildung illustriert die Berechnung des spiegelnden Reflexionsanteils nach Phong.
Im Fragment Shader lässt sich der spiegelnde Reflexionsanteil wie folgt berechnen:
float tempDot = dot(NormalPerTexel, negLightDir);
// Schritt 1: reflektierten Lichtvektor berechnen:
// reflektierter Lichtvektor = 2.0*tempDot*NormalPerTexel-negLightDir
// Je genauer die negative Richtung der reflektierten Lichtstrahlen mit der
// Blickrichtung übereinstimmt, umso stärker erscheinen die Spiegelungen:
// intense = -dot(reflektierter Lichtvektor, ViewDirection)
// kombiniert man beide Rechenschritte, dann erhält man:
float intense = max(-dot(2.0*tempDot*NormalPerTexel-negLightDir, ViewDirection), 0.0);
// Durch die nachfolgenden Multiplikationsschritte wird die Fläche
// Schritt 1: reflektierten Lichtvektor berechnen:
// reflektierter Lichtvektor = 2.0*tempDot*NormalPerTexel-negLightDir
// Je genauer die negative Richtung der reflektierten Lichtstrahlen mit der
// Blickrichtung übereinstimmt, umso stärker erscheinen die Spiegelungen:
// intense = -dot(reflektierter Lichtvektor, ViewDirection)
// kombiniert man beide Rechenschritte, dann erhält man:
float intense = max(-dot(2.0*tempDot*NormalPerTexel-negLightDir, ViewDirection), 0.0);
// Durch die nachfolgenden Multiplikationsschritte wird die Fläche
// der Spiegelung schrittweise verkleinert:
intense *= intense;
intense *= intense;
intense *= intense;
intense *= intense;
// Im letzten Schritt wird die so berechnete Intensität noch mit den
// Reflexionseigenschaften der Oberfläche multipliziert, die in der
// Specular Map gespeichert sind:
vec4 SpecColor = intense*texture(SpecularTexture, gs_TexCoord[0].st)*LightColor;
// Im letzten Schritt wird die so berechnete Intensität noch mit den
// Reflexionseigenschaften der Oberfläche multipliziert, die in der
// Specular Map gespeichert sind:
vec4 SpecColor = intense*texture(SpecularTexture, gs_TexCoord[0].st)*LightColor;
Im Gegensatz zu spiegelnden Oberflächen müssen bei der Beleuchtungs-Berechnung von glatten Oberflächen neben dem spiegelnden Reflexionsanteil sowohl der ambiente als auch der diffuse Reflexionsanteil berücksichtigt werden. Die Addition aller Reflexionsanteile geht auf Phong zurück:
gs_FragColor = AmbientReflection + DiffuseReflection + SpecularReflection;
Hinweis:
Der Addition der einzelnen Reflexionsanteile liegt kein physikalisches Gesetz zugrunde. Es hat sich jedoch gezeigt, dass das Resultat optisch recht ansprechend aussieht.