Computergrafik

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
  • —(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; 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 mv;           // ModellView-Matrix
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

varying vec4 col;

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 mv;           // ModellView-Matrix
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

// Ambient + Diffus (Blinn-Phong)
// 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 mv;           // ModellView-Matrix
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 Ca = vec4 ( 0,  0, .3, 0);
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

Options: