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);