opengl
Grundbeleuchtung
Suche…
Phong-Beleuchtungsmodell
HINWEIS: Dieses Beispiel ist WIP. Es wird mit Diagrammen, Bildern, weiteren Beispielen usw. aktualisiert.
Was ist Phong?
Phong ist ein sehr einfaches, aber echt aussehendes Lichtmodell für Oberflächen, das aus drei Teilen besteht: Umgebungslicht, diffuses und spiegelndes Licht.
Umgebungsbeleuchtung:
Die Umgebungsbeleuchtung ist die einfachste der drei Komponenten, die man verstehen und berechnen kann. Umgebungslicht ist Licht, das die Szene durchflutet und das Objekt gleichmäßig in alle Richtungen beleuchtet.
Die zwei Variablen bei der Umgebungsbeleuchtung sind die Stärke der Umgebung und die Farbe der Umgebung. In Ihrem Fragment-Shader funktioniert Folgendes für die Umgebung:
in vec3 objColor;
out vec3 finalColor;
uniform vec3 lightColor;
void main() {
float ambientStrength = 0.3f;
vec3 ambient = lightColor * ambientStrength;
finalColor = ambient * objColor;
}
Diffuse Beleuchtung:
Die diffuse Beleuchtung ist etwas komplexer als die Umgebungsbeleuchtung. Bei diffusem Licht handelt es sich um gerichtetes Licht. Im Wesentlichen bedeutet dies, dass Gesichter, die auf die Lichtquelle gerichtet sind, besser beleuchtet werden und weggerichtete Gesichter dunkler werden, da das Licht sie trifft.
Hinweis: Für die diffuse Beleuchtung müssen für jedes Gesicht Normalen verwendet werden, die hier nicht dargestellt werden. Wenn Sie mehr darüber erfahren möchten, besuchen Sie die 3D-Mathematikseite.
Zur Modellierung der Lichtreflexion in der Computergrafik wird eine bidirektionale Reflexionsverteilungsfunktion (BRDF) verwendet. BRDF ist eine Funktion, die die Beziehung zwischen dem entlang einer Ausfallsrichtung reflektierten Licht und dem aus einer Einfallsrichtung einfallenden Licht angibt.
Eine perfekte diffuse Oberfläche hat ein BRDF, das für alle Einfalls- und Abgangsrichtungen den gleichen Wert hat. Dies reduziert die Berechnungen erheblich und wird daher üblicherweise zur Modellierung diffuser Oberflächen verwendet, da dies physikalisch plausibel ist, obwohl es in der Realität keine reinen diffusen Materialien gibt. Dieses BRDF wird als Lambert'sche Reflexion bezeichnet, weil es dem Lambertschen Kosinusgesetz gehorcht.
Lambertsche Reflexion wird häufig als Modell für diffuse Reflexion verwendet. Diese Technik bewirkt, dass alle geschlossenen Polygone (z. B. ein Dreieck innerhalb eines 3D-Netzes) bei der Wiedergabe Licht in alle Richtungen gleichmäßig reflektieren. Der Diffusionskoeffizient wird aus dem Winkel zwischen dem Normalenvektor und dem Lichtvektor berechnet.
f_Lambertian = max( 0.0, dot( N, L )
wobei N
der Normalenvektor der Oberfläche ist und L
der Vektor in Richtung der Lichtquelle ist.
Wie es funktioniert
Im Allgemeinen ist das Punktprodukt von 2 Vektoren gleich dem Cosinus des Winkels zwischen den 2 Vektoren, multipliziert mit der Größe (Länge) beider Vektoren.
dot( A, B ) == length( A ) * length( B ) * cos( angle_A_B )
Daraus folgt, dass das Punktprodukt von 2 Einheitsvektoren gleich dem Cosinus des Winkels zwischen den 2 Vektoren ist, da die Länge eines Einheitsvektors 1 ist.
uA = normalize( A )
uB = normalize( B )
cos( angle_A_B ) == dot( uA, uB )
Wenn wir die cos (x) -Funktion zwischen den Winkeln -90 ° und 90 ° betrachten, können wir feststellen, dass sie bei einem Winkel von 0 ° ein Maximum von 1 hat und bei einem Winkel von 90 ° auf 0 fällt und -90 °.
Dieses Verhalten ist genau das, was wir für das Reflexionsmodell wollen. Wenn der vromal vetor der Oberfläche und die Richtung zur Lichtquelle in der gleichen Richtung liegen (der Winkel zwischen 0 °), dann wollen wir ein Maximum an Reflexion. Wenn dagegen die Vektoren orthonormalisiert sind (der Winkel dazwischen beträgt 90 °), wollen wir ein Minimum an Reflexion und wir wollen einen glatten und kontinuierlichen funktionellen Verlauf zwischen den beiden Grenzen von 0 ° und 90 °.
Wenn das Licht pro Scheitelpunkt berechnet wird, wird die Reflexion für jede Ecke des Grundelements berechnet. Zwischen den Grundelementen werden die Reflexionen entsprechend ihren Schwerpunktkoordinaten interpoliert. Sehen Sie die resultierenden Reflexionen auf einer sphärischen Oberfläche:
Ok, um mit unserem Fragment-Shader zu beginnen, benötigen wir vier Eingänge.
- Scheitelpunktnormalen (sollten in Puffer sein und durch Scheitelpunktattributzeiger angegeben werden)
- Fragmentposition (sollte vom Vertex-Shader in den Frag-Shader ausgegeben werden)
- Lichtquellenposition (einheitlich)
- Lichtfarbe (einheitlich)
in vec3 normal;
in vec3 fragPos;
out vec3 finalColor;
uniform vec3 lightColor;
uniform vec3 lightPos;
uniform vec3 objColor;
Innerhalb von main müssen wir etwas rechnen. Das gesamte Konzept der diffusen Beleuchtung basiert auf dem Winkel zwischen der Normalen und der Lichtrichtung. Je größer der Winkel, desto weniger Licht gibt es bis 90 °, wo überhaupt kein Licht vorhanden ist.
Bevor wir mit der Berechnung der Lichtmenge beginnen können, benötigen wir den Lichtrichtungsvektor. Dies kann abgerufen werden, indem einfach die Lichtposition von der Fragmentposition abgezogen wird, die einen Vektor von der Lichtposition zurückgibt, die auf die Fragmentposition zeigt.
vec3 lightDir = lightPos-fragPos;
Normalisieren Sie auch die normal
und lightDir
Vektoren, damit sie gleich lang sind.
normal = normalize(normal);
lightDir = normalize(lightDir);
Nun, da wir unsere Vektoren haben, können wir die Differenz zwischen ihnen berechnen. Dazu verwenden wir die Punktproduktfunktion. Im Grunde nimmt dies 2 Vektoren und gibt den cos () des gebildeten Winkels zurück. Dies ist perfekt, da es bei 90 Grad 0 und bei 0 Grad 1 ergibt. Wenn das Licht direkt auf das Objekt gerichtet ist, leuchtet es vollständig und umgekehrt.
float diff = dot(normal, lightDir);
Es gibt noch etwas, was wir mit der berechneten Anzahl tun müssen. Wir müssen sicherstellen, dass es immer positiv ist. Wenn Sie darüber nachdenken, ist eine negative Zahl im Kontext nicht sinnvoll, da das Licht hinter dem Gesicht ist. Wir könnten eine if -Anweisung verwenden, oder wir können die Funktion max()
verwenden, die das Maximum von zwei Eingaben zurückgibt.
diff = max(diff, 0.0);
Damit können wir nun die endgültige Ausgabefarbe für das Fragment berechnen.
vec3 diffuse = diff * lightColor;
finalColor = diffuse * objColor;
Spiegelnde Beleuchtung:
Arbeite in Fortschritten, schau später nach.
Kombiniert
In Arbeit, später nochmal nachschauen.
Der folgende Code und das Bild zeigen diese drei Beleuchtungskonzepte zusammen.