Blinn Phong in GLSL
← Phong Shading | ● | Punktlichtquellen →
Beleuchtungsberechnung benötigt Winkel, deshalb kann sie nur mit affin transformierten Koordinaten durchgeführt werden. D.h. entweder Modell- oder Welt-Koordinate, oder Augenkoordinaten (nicht aber in Clipkoordinaten oder normalisierten Devicekoordinaten).
Normalen als (beliebiges) Dreikomponenten-Attribut (vec3) je Vertex übergeben, hier nehmen wir die von den VBOs im Framework in vertex_normal übergebenen Werte.
Zur Erinnerung:
- Modellkoordinaten
- —(Modelling-Matrix)→ Weltkoordinaten
- —(Viewing-Matrix)→ Augenkoordinaten
- —(Projection-Matrix)→ Clipkoordinaten
- —(Perspectivische Division)→ Normalisierte Devicekoordinaten
- —(Viewport Trafo)→ Windowkoordinaten
Man benötigt also die reine ModellView (ohne Projektion) für den View-Vektor. Man benötigt die invertierte transponierte ModellView für die Normalen. Die beiden sind gleich wenn ausschließlich affine Transformationen durchgeführt wurden.
Im Beispiel werden die benötigten Matrizen in mv, mvp und mvit genutzt; arbeitet man mit lglProjection() und lglModelView(), kann man das berechnen und setzen der Matrizen ignorieren, das Framework setzt die uniformen Variablen automatisch.
Matrizen-Berechnung wie in Beleuchtung Standard-Codesequenz
Gouraud Shading
Vertex-Shader
uniform mat4x4 mvp; // ModellViewProjection-Matrix
uniform mat4x4 mvit; // Inverse transponierte ModellView-Matrix
attribute vec4 vertex_position;
attribute vec3 vertex_normal;
// Ambient + Diffus (Blinn-Phong)
// Emmissiv + Specular hier zur Vereinfachung weggelassen
// Hier nur kombinierte Farben, in der Praxis Materialkonstanten und Lichtfarben dafür miteinander multiplizieren (Lichtfarbe: uniform, Material: Per-Vertex Attribut)
const vec4 Ca = vec4 ( 0, 0, .3, 0);
const vec4 Cd = vec4 ( 0, .5, 0, 0);
// Direktionales Licht (im Unendlichen)
const vec3 light = normalize (vec3 (2, 1, 3));
// Ausgabe für den Fragment-Shader
varying vec4 col;
void main()
{
// Vertex trafo -> Projezierte Koordinaten (Clipkoordinaten)
gl_Position = mvp * vertex_position;
// Normalen trafo -> Augenkoordinaten
// Werden nicht durch Translationen verändert -> 3x3 Matrix reicht
vec3 normal = normalize (mat3x3 (mvit) * vertex_normal);
// Diffuses Licht
float Id = max (dot (light, normal), 0);
// Zusammenaddieren
col = Ca + Cd * Id;
}
Fragment-Shader
void main()
{
gl_FragColor = col;
}
Phong Shading
Gleicher Code wie zuvor, nur werden Normalen interpoliert, nicht Farben, und fast alles im Fragment-Shader berechnet. Man muss die Normale (und den Halfway-Vektor) im Fragment-Shader renormalisieren, da bei der Interpolation sich die Länge des Vektors u.U. verändert.
Achtung! So wenig wie möglich im Fragment-Shader berechnen - der Code hier wird wesentlich häufiger ausgeführt!
Vertex-Shader
uniform mat4x4 mvp; // ModellViewProjection-Matrix
uniform mat4x4 mvit; // Inverse transponierte ModellView-Matrix
attribute vec4 vertex_position;
attribute vec3 vertex_normal;
// Ausgabe für den Fragment-Shader
varying vec3 normal;
void main()
{
// Vertex trafo -> Projezierte Koordinaten (Clipkoordinaten)
gl_Position = mvp * vertex_position;
// Normalen trafo -> Augenkoordinaten
// Werden nicht durch Translationen verändert -> 3x3 Matrix reicht
normal = normalize (mat3x3 (mvit) * vertex_normal);
}
Fragment-Shader
// Emmissiv + Spekular hier zur Vereinfachung weggelassen
// Hier nur kombinierte Farben, in der Praxis Materialkonstanten und Lichtfarben dafür miteinander multiplizieren (Lichtfarbe: uniform, Material: Per-Vertex Attribut)
const vec4 Ca = vec4 ( 0, 0, .3, 0);
const vec4 Cd = vec4 ( 0, .5, 0, 0);
// Direktionales Licht (im Unendlichen)
const vec3 light = normalize (vec3 (2, 1, 3));
varying vec3 normal;
void main()
{
// Renormalisieren
vec3 n = normalize (normal);
// Diffuses Licht
float Id = max (dot (light, n), 0);
// Zusammenaddieren
gl_FragColor = Ca + Cd * Id;
}
Phong Shading mit spekularen Hilights
Jetzt fügen wir spekulare Hilights (und den emmissiven Term) hinzu.
Vertex-Shader
uniform mat4x4 mvp; // ModellViewProjection-Matrix
uniform mat4x4 mvit; // Inverse transponierte ModellView-Matrix
attribute vec4 vertex_position;
attribute vec3 vertex_normal;
const vec3 light = normalize (vec3 (2, 1, 3));
// Ausgabe für den Fragment-Shader
varying vec3 normal, halfway;
void main()
{
// Vertex trafo -> Projezierte Koordinaten (Clipkoordinaten)
gl_Position = mvp * vertex_position;
// Normalen trafo -> Augenkoordinaten
// Werden nicht durch Translationen verändert -> 3x3 Matrix reicht
normal = normalize (mat3x3 (mvit) * vertex_normal);
// View-Vektor berechnen - Vertex in Augenkoordinaten, nur umgedreht
vec3 view = normalize (- vec3 (mv * vertex_position));
// Halfway-Vektor für Spekulares Hilight
halfway = normalize (view + light);
}
Fragment-Shader
const vec4 Ce = vec4 ( 0, 0, 0, 0);
const vec4 Cd = vec4 ( 0, .5, 0, 0);
const vec4 Cs = vec4 (.8, .8, .8, 0);
const float kse = 30;
const vec3 light = normalize (vec3 (2, 1, 3));
// Eingabe für den Fragment-Shader
varying vec3 normal, halfway;
void main()
{
vec3 n = normalize (normal);
vec3 h = normalize (halfway);
float Id = max (dot (light, n), 0);
float Is = pow (max (dot (h, n), 0), kse);
gl_FragColor = Ca + Ce + Cd * Id + Cs * Is;
}
← Phong Shading | ● | Punktlichtquellen →