In this section, we will delve into advanced lighting techniques that can significantly enhance the visual quality of your DirectX applications. These techniques include various methods to simulate realistic lighting effects, which are crucial for creating immersive 3D environments.

Key Concepts

  1. Phong Shading
  2. Blinn-Phong Shading
  3. Normal Mapping
  4. Parallax Mapping
  5. Ambient Occlusion
  6. Global Illumination

  1. Phong Shading

Phong Shading is a technique that interpolates surface normals across rasterized polygons and computes pixel colors based on the interpolated normals and a lighting model.

Code Example

// Vertex Shader
struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float3 Normal : NORMAL;
};

VS_OUTPUT VS(float4 Pos : POSITION, float3 Normal : NORMAL)
{
    VS_OUTPUT output;
    output.Pos = mul(Pos, WorldViewProj);
    output.Normal = mul(Normal, (float3x3)World);
    return output;
}

// Pixel Shader
float4 PS(VS_OUTPUT input) : SV_Target
{
    float3 lightDir = normalize(LightPos - input.Pos.xyz);
    float3 viewDir = normalize(CameraPos - input.Pos.xyz);
    float3 reflectDir = reflect(-lightDir, input.Normal);

    // Ambient
    float3 ambient = AmbientColor * AmbientIntensity;

    // Diffuse
    float diff = max(dot(input.Normal, lightDir), 0.0);
    float3 diffuse = diff * DiffuseColor * DiffuseIntensity;

    // Specular
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), SpecularPower);
    float3 specular = spec * SpecularColor * SpecularIntensity;

    return float4(ambient + diffuse + specular, 1.0);
}

Explanation

  • Vertex Shader: Transforms the vertex position and normal to world space.
  • Pixel Shader: Computes ambient, diffuse, and specular components based on the Phong reflection model.

  1. Blinn-Phong Shading

Blinn-Phong Shading is a modification of Phong Shading that uses the halfway vector between the light direction and the view direction to calculate the specular reflection.

Code Example

// Pixel Shader
float4 PS(VS_OUTPUT input) : SV_Target
{
    float3 lightDir = normalize(LightPos - input.Pos.xyz);
    float3 viewDir = normalize(CameraPos - input.Pos.xyz);
    float3 halfDir = normalize(lightDir + viewDir);

    // Ambient
    float3 ambient = AmbientColor * AmbientIntensity;

    // Diffuse
    float diff = max(dot(input.Normal, lightDir), 0.0);
    float3 diffuse = diff * DiffuseColor * DiffuseIntensity;

    // Specular
    float spec = pow(max(dot(input.Normal, halfDir), 0.0), SpecularPower);
    float3 specular = spec * SpecularColor * SpecularIntensity;

    return float4(ambient + diffuse + specular, 1.0);
}

Explanation

  • Halfway Vector: The halfway vector is used instead of the reflection vector, which can be more efficient and produce similar visual results.

  1. Normal Mapping

Normal Mapping is a technique used to simulate the fine details of a surface by perturbing the surface normals.

Code Example

// Vertex Shader
struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float3 Normal : NORMAL;
    float2 TexCoord : TEXCOORD0;
    float3 Tangent : TANGENT;
};

VS_OUTPUT VS(float4 Pos : POSITION, float3 Normal : NORMAL, float2 TexCoord : TEXCOORD0, float3 Tangent : TANGENT)
{
    VS_OUTPUT output;
    output.Pos = mul(Pos, WorldViewProj);
    output.Normal = mul(Normal, (float3x3)World);
    output.TexCoord = TexCoord;
    output.Tangent = mul(Tangent, (float3x3)World);
    return output;
}

// Pixel Shader
float4 PS(VS_OUTPUT input) : SV_Target
{
    float3 normalMap = tex2D(NormalMapSampler, input.TexCoord).xyz * 2.0 - 1.0;
    float3x3 TBN = float3x3(input.Tangent, cross(input.Normal, input.Tangent), input.Normal);
    float3 perturbedNormal = normalize(mul(normalMap, TBN));

    // Lighting calculations using perturbedNormal
    // ...

    return float4(finalColor, 1.0);
}

Explanation

  • Vertex Shader: Outputs the tangent vector along with the normal and texture coordinates.
  • Pixel Shader: Uses the normal map to perturb the surface normals and applies lighting calculations based on the perturbed normals.

  1. Parallax Mapping

Parallax Mapping is an enhancement of normal mapping that adds depth to textures by offsetting texture coordinates based on the view angle.

Code Example

// Pixel Shader
float4 PS(VS_OUTPUT input) : SV_Target
{
    float3 viewDir = normalize(CameraPos - input.Pos.xyz);
    float height = tex2D(HeightMapSampler, input.TexCoord).r;
    float2 parallaxTexCoords = input.TexCoord + (viewDir.xy * (height * ParallaxScale));

    float3 normalMap = tex2D(NormalMapSampler, parallaxTexCoords).xyz * 2.0 - 1.0;
    float3x3 TBN = float3x3(input.Tangent, cross(input.Normal, input.Tangent), input.Normal);
    float3 perturbedNormal = normalize(mul(normalMap, TBN));

    // Lighting calculations using perturbedNormal
    // ...

    return float4(finalColor, 1.0);
}

Explanation

  • Height Map: A texture that stores height information used to offset texture coordinates.
  • Parallax Offset: The texture coordinates are offset based on the view direction and height value to create a depth illusion.

  1. Ambient Occlusion

Ambient Occlusion (AO) simulates the soft shadows that occur in creases, holes, and surfaces that are close to each other.

Code Example

// Pixel Shader
float4 PS(VS_OUTPUT input) : SV_Target
{
    float ao = ComputeAmbientOcclusion(input.Pos, input.Normal);
    float3 ambient = AmbientColor * AmbientIntensity * ao;

    // Other lighting calculations
    // ...

    return float4(finalColor, 1.0);
}

Explanation

  • ComputeAmbientOcclusion: A function that calculates the ambient occlusion factor based on the geometry and surface normals.

  1. Global Illumination

Global Illumination (GI) simulates the indirect lighting that occurs when light bounces off surfaces and illuminates other surfaces.

Code Example

// Pixel Shader
float4 PS(VS_OUTPUT input) : SV_Target
{
    float3 indirectLight = ComputeGlobalIllumination(input.Pos, input.Normal);

    // Direct lighting calculations
    // ...

    float3 finalColor = directLight + indirectLight;
    return float4(finalColor, 1.0);
}

Explanation

  • ComputeGlobalIllumination: A function that calculates the indirect lighting contribution based on the scene geometry and lighting conditions.

Practical Exercise

Exercise

  1. Implement Phong Shading in a DirectX application.
  2. Modify the application to use Blinn-Phong Shading.
  3. Add normal mapping to the application.
  4. Implement parallax mapping.
  5. Integrate ambient occlusion.
  6. Add global illumination to the scene.

Solution

  1. Phong Shading: Follow the provided code example for Phong Shading.
  2. Blinn-Phong Shading: Modify the pixel shader to use the halfway vector.
  3. Normal Mapping: Add a normal map texture and modify the shaders to use it.
  4. Parallax Mapping: Add a height map texture and implement the parallax offset in the pixel shader.
  5. Ambient Occlusion: Implement a function to compute ambient occlusion and integrate it into the lighting calculations.
  6. Global Illumination: Implement a function to compute global illumination and add it to the final lighting calculations.

Conclusion

In this section, we explored several advanced lighting techniques that can significantly enhance the realism of your DirectX applications. By understanding and implementing these techniques, you can create more visually appealing and immersive 3D environments. In the next module, we will focus on working with 3D models and animations, further expanding your DirectX programming skills.

© Copyright 2024. All rights reserved