DirectX Computational Graphics

Understanding Lighting in DirectX Graphics

Lighting is a fundamental aspect of creating realistic and visually appealing 3D scenes. It simulates how light interacts with surfaces to determine their appearance, influencing color, intensity, and shadows. This tutorial series will guide you through the core concepts and practical implementation of lighting in DirectX.

1. Introduction to Light Properties

Every light source in a 3D scene has several key properties:

  • Color/Intensity: The color and brightness of the light emitted.
  • Type: Different light types simulate various real-world light sources:
    • Directional Light: Simulates distant sources like the sun, with parallel rays.
    • Point Light: Emits light in all directions from a single point, like a light bulb.
    • Spotlight: Emits light in a cone, with a defined direction and falloff.
  • Position/Direction: Where the light is located or in which direction it's shining.
  • Attenuation: How the light's intensity decreases with distance (for point and spotlights).

We'll start by implementing simple diffuse lighting. Diffuse lighting models how light reflects equally in all directions from a matte surface. The intensity of diffuse reflection depends on the angle between the surface normal and the light direction. A common model is the Lambertian model, where the light intensity is proportional to the cosine of the angle between the surface normal and the light vector.

Key Concept: The surface normal is a vector perpendicular to the surface at a given point, crucial for calculating light interaction.

2. The Phong Lighting Model

The Phong lighting model is a widely used empirical model that approximates the way light reflects off a surface. It combines three components:

  • Ambient: A base level of light that illuminates all surfaces equally, preventing areas from being completely black. It represents indirect lighting and general scene illumination.
  • Diffuse: As described above, this accounts for light scattering evenly from a surface.
  • Specular: This component simulates the shiny highlights seen on glossy surfaces. It depends on the angle between the view direction and the reflection direction of the light.

The overall color of a surface pixel is typically calculated as:

FinalColor = Ambient + Diffuse + Specular

Let's look at the mathematical components:

  • Ambient: AmbientLight * MaterialAmbient
  • Diffuse: LightColor * MaterialDiffuse * max(0, dot(Normal, LightDirection))
  • Specular: LightColor * MaterialSpecular * pow(max(0, dot(ReflectionDirection, ViewDirection)), Shininess)

Where:

  • Normal is the surface normal vector.
  • LightDirection is the vector from the surface point to the light source.
  • LightColor is the color of the light source.
  • MaterialAmbient/Diffuse/Specular are properties of the surface material.
  • ReflectionDirection is the direction the light reflects off the surface.
  • ViewDirection is the vector from the surface point to the camera.
  • Shininess controls the size and intensity of the specular highlight.

3. Implementing Basic Lighting in HLSL

DirectX uses High-Level Shading Language (HLSL) to define how graphics are rendered. Here's a simplified example of a pixel shader that applies basic Phong lighting:


struct VertexShaderOutput {
    float4 Position : SV_POSITION;
    float3 Normal : NORMAL;
    float3 WorldPosition : POSITION;
};

float4 AmbientColor : AMBIENT;
float4 DiffuseColor : DIFFUSE;
float4 SpecularColor : SPECULAR;
float Shininess : SHININESS;

float3 LightDirection : LIGHTDIRECTION;
float3 LightColor : LIGHTCOLOR;

float4 main(VertexShaderOutput input) : SV_TARGET {
    float3 normal = normalize(input.Normal);
    float3 worldPos = input.WorldPosition;
    float3 lightDir = normalize(LightDirection); // Assuming directional light for simplicity

    // Ambient component
    float4 ambient = AmbientColor;

    // Diffuse component
    float diffuseFactor = max(0.0f, dot(normal, lightDir));
    float4 diffuse = DiffuseColor * LightColor * diffuseFactor;

    // Specular component (simplified reflection vector)
    float3 reflectionDir = reflect(-lightDir, normal);
    float3 viewDir = normalize(worldPos - /* Camera Position */); // Needs camera position
    float specularFactor = pow(max(0.0f, dot(reflectionDir, viewDir)), Shininess);
    float4 specular = SpecularColor * LightColor * specularFactor;

    // Combine components
    float4 finalColor = ambient + diffuse + specular;
    return finalColor;
}
                
Note on Camera Position: In a real application, the camera's world position would be passed to the shader as a constant buffer parameter.

This shader calculates the color for each pixel based on material properties, light characteristics, and surface orientation. You would typically pass world matrices, light parameters, and camera position from your C++ application to these shader constants.

4. Beyond Basic Lighting: Advanced Techniques

Once you've mastered the fundamentals, you can explore more advanced lighting techniques:

  • Multiple Lights: Applying lighting from several sources simultaneously.
  • Shadows: Simulating occlusions by casting shadows from objects. This often involves techniques like shadow mapping.
  • Normal Mapping: Using texture maps to simulate surface detail and complex bumpiness without increasing geometric complexity.
  • Physically Based Rendering (PBR): More accurate models that simulate light transport based on physical properties of materials, leading to more photorealistic results.
  • Global Illumination: Simulating indirect lighting, where light bounces off surfaces and illuminates other objects in the scene.