Lighting

Light = ambient + diffuse (Lambert: max(N·L, 0)) + specular (Phong: max(R·V, 0)^shininess, where R = reflect(-L, N)).

19.1 The Phong reflection model

Light = ambient + diffuse (Lambert: max(N·L, 0)) + specular (Phong: max(R·V, 0)^shininess, where R = reflect(-L, N)). Blinn–Phong uses the half vector H = normalize(L + V) and max(N·H, 0)^shininess — cheaper, better at grazing angles, and the basis for microfacet models.

19.2 Blinn–Phong pixel shader with multiple lights

Check: LightData is 64 bytes (4 registers), so the cbuffer array stride is exactly 64 — matching a C++ array of the same struct.

#define MAX_LIGHTS 16
#define LIGHT_DIRECTIONAL 0
#define LIGHT_POINT       1
#define LIGHT_SPOT        2

struct LightData
{
    float3 position;  uint   type;
    float3 direction; float  range;
    float3 color;     float  intensity;
    float  spotInner; float  spotOuter; float2 pad;     // cosines of cone angles
};

cbuffer Lights : register(b3)
{
    LightData gLights[MAX_LIGHTS];
    uint      gLightCount;
    float3    gAmbient;
};

Texture2D    gAlbedoTex : register(t0);
SamplerState gAniso     : register(s0);

float3 BlinnPhong(LightData L, float3 P, float3 N, float3 V, float3 albedo, float shininess)
{
    float3 toLight; float atten = 1.0;
    if (L.type == LIGHT_DIRECTIONAL)
    {
        toLight = -L.direction;
    }
    else
    {
        float3 d = L.position - P;
        float dist = length(d);
        toLight = d / dist;
        float falloff = saturate(1.0 - pow(dist / L.range, 4));    // windowed inverse-square
        atten = falloff * falloff / (dist * dist + 1.0);
        if (L.type == LIGHT_SPOT)
        {
            float cd = dot(-toLight, L.direction);
            atten *= smoothstep(L.spotOuter, L.spotInner, cd);
        }
    }
    float3 H    = normal
…

19.3 Shading frequency

Per-vertex (Gouraud): lighting in the VS, interpolated — cheap, misses highlights.

Per-pixel: lighting in the PS — standard.

Per-sample: input SV_SampleIndex or sample interpolation — MSAA quality, expensive.

19.4 Stylized lighting

For wrap lighting use saturate((dot(N, L) + w) / (1 + w)); for a hemispheric ambient use lerp(groundColor, skyColor, N.y * 0.5 + 0.5).

float3 Toon(float3 N, float3 L, float3 V, float3 baseColor)
{
    float ndl  = dot(N, L);
    float band = ndl > 0.6 ? 1.0 : ndl > 0.2 ? 0.6 : 0.3;      // quantized diffuse
    float rim  = pow(1.0 - saturate(dot(N, V)), 4.0);           // rim light
    float spec = step(0.95, dot(N, normalize(L + V)));           // hard specular
    return baseColor * band + rim * 0.3 + spec;
}