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:
- Modeling-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 (Matrizen-Berechnung wie in Beleuchtung Standard-Codesequenz):
Gouraud Shading
Vertex-Shader
uniform mat4x4 mvp; // ModelViewProjection-Matrix
uniform mat4x4 mvit; // Inverse transponierte ModelView-Matrix
attribute vec4 vertex_position;
attribute vec3 vertex_normal;
// Ambient + Diffus
// Hier nur kombinierte Farben Cx = kx*Ix
// in der Praxis Materialkonstanten und Lichtfarben miteinander multiplizieren
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 -> Projizierte 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 verändern kann.
Achtung: so wenig wie möglich im Fragment-Shader berechnen, da dieser Code wesentlich häufiger ausgeführt wird!
Vertex-Shader
uniform mat4x4 mvp; // ModelViewProjection-Matrix
uniform mat4x4 mvit; // Inverse transponierte ModelView-Matrix
attribute vec4 vertex_position;
attribute vec4 vertex_color;
attribute vec3 vertex_normal;
// Ausgabe für den Fragment-Shader
varying vec3 color;
varying vec3 normal;
void main()
{
// Vertex Trafo -> Projizierte Koordinaten (Clipkoordinaten)
gl_Position = mvp * vertex_position;
// Farbattribute interpolieren
color = vertex_color;
// Normalen Trafo -> Augenkoordinaten
// Werden nicht durch Translationen verändert -> 3x3 Matrix reicht
normal = normalize (mat3x3 (mvit) * vertex_normal);
}
Fragment-Shader
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;
}
Blinn-Phong Shading mit spekularen Hilights
Jetzt fügen wir spekulare Hilights hinzu:
Vertex-Shader
uniform mat4x4 mvp; // ModelViewProjection-Matrix
uniform mat4x4 mvit; // Inverse transponierte ModelView-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 -> Projizierte 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 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 + Cd * Id + Cs * Is;
}
Hinweis: den emissiven Term simulieren wir mit Hilfe von Ca = Ce!
Fragment-Shader mit Materialfarbattributen für den ambienten und diffusen Term:
varying vec4 color;
void main()
{
...
gl_FragColor = color * (Ca + Cd) * Id + Cs * Is;
}
Live Demo: Phong vs. Bling-Phong (T#12c)
← Phong Shading | ● | Punktlichtquellen →