Beleuchtung Standard-Codesequenz
← Lichtquellen zeichnen | ● | GL Primitive →
Annahme: Framework-Funktionen werden verwendet, es gibt eine Initialisierungs-Funktion, und eine Display-Funktion, die für jeden Frame aufgerufen wird.
Das ganze sind nur Code-Vorschläge, diese müssen für eigene Funktionalität verändert werden, und die Variablennamen den eigenen Konventionen angepasst werden.
Die hier vorgestellte Shader-Kombination ermöglicht beliebig viele Lightquellen, direktionale und Punktlichtquellen, mit Abschwächung. Sie benutzt Uniform Arrays, um mehrere Lichtquellen zu verwalten. Die können wie einzelne Variablen auch benutzt werden. Die Abschwächung wird über Vorfaktoren für die drei Möglichkeiten Konstant (keine Abschwächung), Linear und Quadratisch bestimmt.
Es werden wieder die Standard-Matrizen mv
, mvp
und mvit
benutzt, die automatisch mit lglProjection()
und lglModelView()
berechnet und gesetzt werden; arbeitet man mit anderen Variablen, muss man diese selber berechnen und mit lglUniformfv()
bzw. glUniformMatrix4fv()
setzen.
Aufgeführt sind nur die Änderungen zur Einfache Standard-Codesequenz.
Shader
- Vertex-Shader
uniform mat4 mvp; // ModellViewProjection
uniform mat4 mv; // ModellView
uniform mat4 mvit; // inverse transposed ModellView
attribute vec4 vertex_position;
attribute vec3 vertex_normal;
// Lights
const int max_numlights = 4;
uniform int numlights;
uniform vec4 lightpos [max_numlights];
varying vec3 normal, view;
varying vec3 light[max_numlights];
void main () {
// Vertex trafo -> projected coords
gl_Position = mvp * vertex_position;
// Normals trafo -> eye coords (unprojected)
normal = normalize(mat3 (mvit) * vertex_normal);
// View vector: normalized reverted transformed vertex in eye coords
// we ignore normalizing the view vector in the fragment shader,
// thus we do it here
view = normalize (- vec3 (mv * vertex_position));
// Light vector in eye coords
// Loop over all lights, lights with homogenous component '0' are directional
// Mulitplication is faster than if/else
for (int i=0; i < numlights; i++)
light[i] = vec3 (lightpos[i] - lightpos[i].w * mv * vertex_position);
}
- Fragment-Shader
// Ambient, emissive, diffuse, specular material colors
uniform vec4 Ca;
uniform vec4 Ce;
uniform vec4 Cd;
uniform vec4 Cs;
uniform float kse = 100; // specular exponent
// Lights
const int max_numlights = 4;
uniform int numlights;
uniform vec4 lightcol[max_numlights];
uniform vec4 lightatt[max_numlights]; // attenuation: constant, linear, quadratic, max. value
varying vec3 normal, view;
varying vec3 light[max_numlights];
void main () {
// the normal vector needs to be normalized for C1 smoothness
vec3 n = normalize (normal);
// we ignore normalizing the view vector - apparently not necessary
vec4 col = Ce;
// Loop over all lights
for (int i=0; i < numlights; i++) {
float invdist = 1 / length (light[i]);
vec3 l = light[i] * invdist;
float atten = min (lightatt[i].x + lightatt[i].y * invdist +
lightatt[i].z * invdist*invdist, lightatt[i].w);
// Diffuse light
float Id = max (dot (l, n), 0);
// Specular light
vec3 h = normalize (l + view);
float Is = pow (max (dot (h, n), 0), kse);
// Final Blinn-Phong Lighting
col += lightcol[i] * (Ca + atten * (Cd*Id + Cs*Is));
}
// In-Pixel Blooming: If max > 1, bloom out to other channels
vec4 bloom = max (col - vec4(1), 0);
col += vec4 ((bloom.x+bloom.y+bloom.z));
// Alpha is now by definition alpha of diffuse color
col.a = Cd.a;
gl_FragColor = col;
}
Init-Funktion
- Shader (und Locations und VBOs) wie in Einfache Standard-Codesequenz
- Uniform-Arrays bzw. Elemente können komplett mit
lglUniformfv("array", ...)
oder einzeln mitlglUniformfv("array[1]", ...
gesetzt werden. - Initialisieren der Uniforms die selten (nie?) geändert werden
- Alles weitere wie gewohnt
Display-Funktion
- Projektions- und View-Matrizen berechnen, trackball
mat4 proj = mat4::perspective(fovy, aspect, nearp, farp);
mat4 view = mat4::lookat(vec3(0,0,10), vec3(0,0,0), vec3(0,1,0));
mat4 mv = view * lglGetManip(); - Lichter konfigurieren: Position in Augenkoordinaten berechnen (am besten die ModelView-Matrix für die spätere Proxy Geometrie) und setzen, dafür braucht man ggf. bereits manche ModellView-Matrix (Lichtquelle am Roboterarm?)
mat4 mv_light0 = rotate (view, lightangle2, vec3 (0, 0, 1));
mv_light0 = translate (mv_light0, vec3 (2.2, 0, .8));
vec4 light_eye = mv_light0 * vec4(0,0,0,1);
lglUniformfv ("lightpos[0]", light_eye); - Für jede Geometrie:
- ModellView, ModellViewProjection, Invers Transponierte ModellView berechnen und setzen
mat4f mv = translate (view, vec3 (0, 0, -1));
mat4f mvp = proj * mv;
mat4f mvit = inverse (transpose (mv));
lglUniformfv ("mv", (float *) mv);
lglUniformfv ("mvp", (float *) mvp);
lglUniformfv ("mvit", (float *) mvit); - Erinnerung: Wenn nur Translationen und Rotation eingesetzt wurden, kann anstatt
mvit
auch direktmv
benutzt werden! - oder vereinfacht (bei Benutzung von mv, mvp und mvit)
lglProjection (proj); // Nur 1x nötig
lglModelView (mv); // Je Änderung - Materialparameter setzen
lglUniformfv ("Ca", vec4( 0, 0, .3, 0));
lglUniformfv ("Ce", vec4( 0, 0, 0, 0));
lglUniformfv ("Cd", vec4(.3, .5, 0, 0));
lglUniformfv ("Cs", vec4(.8, .8, .8, 0));
lglUniformf ("kse", 100); - Geometrie zeichnen
- ModellView, ModellViewProjection, Invers Transponierte ModellView berechnen und setzen
Lichtquellen zeichnet man über Proxy-Geometrien.
Kompletter Quelltext mit Geometrie und 3 Lichtquellen findet man im Content-Service unter BSP_standardLighting.cpp
← Lichtquellen zeichnen | ● | GL Primitive →