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:

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

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

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

Options: