DirectX Computational Graphics

Programming Guides: Shaders

Pixel Shaders (Fragment Shaders)

Pixel shaders, often referred to as fragment shaders in other graphics APIs, are the stage in the DirectX graphics pipeline responsible for determining the final color of each pixel (or fragment) that makes up a rendered object. They operate on individual pixels after they have been processed by the vertex and geometry shaders and rasterized.

Role and Functionality

The primary role of a pixel shader is to calculate the color output for a pixel. This involves a wide range of operations, including:

Input and Output

Pixel shaders receive interpolated data from the rasterizer, which includes:

The output of a pixel shader is typically a color value (RGBA) that is written to one or more render targets. It can also output depth values or other custom data for subsequent pipeline stages.

Shader Language (HLSL)

Pixel shaders are commonly written in High-Level Shading Language (HLSL). Here's a simplified example of an HLSL pixel shader that applies a basic diffuse lighting model:


// Input structure from the vertex shader, after interpolation
struct PS_INPUT
{
    float4 Pos : SV_POSITION; // Screen-space position
    float3 Normal : NORMAL;   // Interpolated normal
    float2 Tex : TEXCOORD0;   // Interpolated texture coordinates
};

// Output structure (can be a single render target)
struct PS_OUTPUT
{
    float4 Color : SV_Target; // The final pixel color
};

// Texture sampler and resource
Texture2D g_Texture : register(t0);
SamplerState g_Sampler : register(s0);

// Lighting parameters
float3 g_LightDirection = float3(0.0f, 1.0f, 0.0f); // Directional light
float4 g_DiffuseColor = float4(1.0f, 1.0f, 1.0f, 1.0f); // Base diffuse color

PS_OUTPUT main(PS_INPUT input)
{
    PS_OUTPUT output;

    // Normalize the interpolated normal
    float3 normal = normalize(input.Normal);

    // Calculate the diffuse term
    // Dot product of light direction and surface normal
    // Clamp to ensure it's not negative
    float diffuse = max(0.0f, dot(normal, -g_LightDirection));

    // Sample the texture
    float4 texColor = g_Texture.Sample(g_Sampler, input.Tex);

    // Combine texture color, diffuse light, and base diffuse color
    output.Color = texColor * g_DiffuseColor * diffuse;

    // Ensure alpha is preserved
    output.Color.a = texColor.a;

    return output;
}
            

Key Concepts

Performance Considerations

Pixel shaders can be computationally intensive, as they execute for every pixel rendered. Optimizations include:

The pixel shader is where the visual fidelity of your rendered scene truly comes to life. Mastering its capabilities is crucial for creating realistic and visually appealing graphics.

Further Reading