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 →