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 →