Lighting
17.1 The Phong reflection model
Color = ambient + diffuse + specular:
Ambient: constant approximation of indirect light.
Diffuse (Lambert): max(dot(N, L), 0) — brightness depends on the angle between surface normal and light direction.
Specular (Phong): pow(max(dot(R, V), 0), shininess) where R = reflect(-L, N).
Blinn–Phong uses the half vector H = normalize(L + V): pow(max(dot(N, H), 0), shininess) — cheaper, better behaved at grazing angles.
17.2 Blinn–Phong fragment shader with multiple lights
#version 330 core
in vec3 vWorldPos;
in vec3 vNormal;
in vec2 vUV;
out vec4 outColor;
struct PointLight {
vec3 position;
vec3 color;
float constant, linear, quadratic; // attenuation
};
struct DirLight { vec3 direction; vec3 color; };
struct SpotLight { vec3 position; vec3 direction; vec3 color; float cutOff, outerCutOff; };
#define MAX_LIGHTS 8
uniform PointLight uPointLights[MAX_LIGHTS];
uniform int uNumPointLights;
uniform DirLight uSun;
uniform SpotLight uSpot;
uniform vec3 uCameraPos;
uniform sampler2D uDiffuseMap;
uniform sampler2D uSpecularMap;
uniform float uShininess;
vec3 blinnPhong(vec3 N, vec3 L, vec3 V, vec3 lightColor, vec3 albedo, float specMask) {
vec3 H = normalize(L + V);
float diff = max(dot(N, L), 0.0);
float spec = pow(max(dot(N, H), 0.0), uShininess) * float(diff > 0.0);
return lightColor * (diff * albedo + spec * specMask);
}
void main() {
vec3 N = normalize(vNormal); // re-normalize after interpolation!
vec3 V = normalize(uCameraPos - vWorldPos);
vec3 albedo = texture(uDiffuseMap, vUV).rgb;
float specMask = texture(uSpecularMap, vUV).r;
vec3 color = 0.05 * albedo; //
…17.3 Shading frequency
Flat shading: one normal per face (flat varyings or derivative normals).
Gouraud shading: lighting computed per vertex, colors interpolated (cheap, poor highlights).
Phong shading: normals interpolated, lighting per fragment (standard).
17.4 Stylized lighting
// Toon / cel shading: quantize the diffuse term
float diff = max(dot(N, L), 0.0);
float bands = floor(diff * 4.0) / 4.0;
// Rim lighting
float rim = pow(1.0 - max(dot(N, V), 0.0), 3.0);
// Half-Lambert (softer falloff, Valve)
float halfLambert = pow(dot(N, L) * 0.5 + 0.5, 2.0);
// Hemisphere ambient
vec3 ambient = mix(groundColor, skyColor, N.y * 0.5 + 0.5);
// Fresnel (Schlick) for any reflective look
float fresnel = F0 + (1.0 - F0) * pow(1.0 - max(dot(N, V), 0.0), 5.0);