Shader Examples for DirectX Computational Graphics
This section provides a collection of shader examples and tutorials for developing computational graphics applications using DirectX. Shaders are essential for modern graphics pipelines, allowing for highly customizable visual effects and complex computations on the GPU.
Getting Started with DirectX Shaders
Before diving into specific examples, it's recommended to understand the basics of shader programming. DirectX typically uses High-Level Shading Language (HLSL) for writing shaders. Familiarize yourself with:
- Introduction to HLSL
- Understanding Shader Stages (Vertex, Pixel, Compute)
- Compiling and Linking Shaders
Basic Shader Examples
Vertex Shader - Simple Transformation
A fundamental vertex shader that performs basic transformations (world, view, projection) on vertex data.
// Input vertex structure
struct VS_INPUT {
float4 Position : POSITION;
float4 Color : COLOR0;
};
// Output vertex structure
struct VS_OUTPUT {
float4 Position : SV_POSITION;
float4 Color : COLOR0;
};
// Constant buffer for transformation matrices
cbuffer ModelViewProjection : register(b0) {
matrix WorldViewProjection;
};
VS_OUTPUT main(VS_INPUT input) {
VS_OUTPUT output = (VS_OUTPUT)0;
// Transform vertex position
output.Position = mul(input.Position, WorldViewProjection);
output.Color = input.Color;
return output;
}
Pixel Shader - Solid Color Output
A basic pixel shader that outputs a solid color, often used for debugging or as a fallback.
// Output structure for pixel shader
struct PS_OUTPUT {
float4 Color : SV_TARGET;
};
PS_OUTPUT main() {
PS_OUTPUT output;
output.Color = float4(1.0f, 0.0f, 1.0f, 1.0f); // Magenta color
return output;
}
Advanced Shader Techniques
Pixel Shader - Phong Illumination Model
Implements the Phong illumination model for realistic lighting, including ambient, diffuse, and specular components.
struct VS_OUTPUT {
float4 Position : SV_POSITION;
float3 Normal : NORMAL;
float3 WorldPos : TEXCOORD0;
};
struct PS_OUTPUT {
float4 Color : SV_TARGET;
};
cbuffer LightProperties : register(b1) {
float3 LightDirection;
float3 CameraPosition;
float3 LightColor;
float AmbientStrength;
float SpecularStrength;
};
float4 main(VS_OUTPUT input) : SV_TARGET {
float3 normal = normalize(input.Normal);
float3 worldPos = input.WorldPos;
float3 lightDir = normalize(LightDirection);
float3 viewDir = normalize(CameraPosition - worldPos);
// Ambient
float3 ambient = LightColor * AmbientStrength;
// Diffuse
float diff = max(dot(normal, lightDir), 0.0f);
float3 diffuse = LightColor * diff;
// Specular
float3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0f), 32); // Shininess factor
float3 specular = LightColor * spec * SpecularStrength;
float3 finalColor = ambient + diffuse + specular;
return float4(finalColor, 1.0f);
}
Geometry Shader - Instancing
Demonstrates how to use a geometry shader to output multiple primitives (e.g., instances of a mesh) from a single input primitive.
Note: Geometry shaders are less common in modern rendering but useful for specific effects.
struct VS_OUTPUT {
float4 Position : SV_POSITION;
};
struct GS_OUTPUT {
float4 Position : SV_POSITION;
};
[maxvertexcount(3)] // Output up to 3 vertices (a triangle)
void main(triangle VS_OUTPUT input[3], inout PrimitiveStream output) {
GS_OUTPUT p;
// Pass through the first vertex of the input triangle
p.Position = input[0].Position;
output.Append(p);
output.RestartStrip(); // Start a new primitive strip
// Example: outputting points instead of a triangle
p.Position = input[0].Position; output.Append(p);
p.Position = input[1].Position; output.Append(p);
p.Position = input[2].Position; output.Append(p);
output.RestartStrip();
}
Compute Shader - Image Processing (Blur)
A compute shader example for performing image processing tasks, such as applying a Gaussian blur filter.
// Texture representing the input image
RWTexture2D SourceTexture : register(u0);
// Texture to store the blurred output
RWTexture2D DestTexture : register(u1);
// Sampler for texture access
SamplerState SamplerLinear : register(s0);
// Thread group size
static const uint3 ThreadGroupSize = uint3(8, 8, 1);
[numthreads(ThreadGroupSize.x, ThreadGroupSize.y, ThreadGroupSize.z)]
void CSMain(uint3 DTid : SV_DispatchThreadID) {
// Pixel coordinates
uint2 texelCoord = DTid.xy;
uint2 textureSize;
SourceTexture.GetDimensions(textureSize.x, textureSize.y);
if (texelCoord.x >= textureSize.x || texelCoord.y >= textureSize.y) {
return; // Out of bounds
}
// Simple box blur (replace with Gaussian for better results)
float4 sum = 0.0f;
for (int x = -1; x <= 1; ++x) {
for (int y = -1; y <= 1; ++y) {
uint2 sampleCoord = texelCoord + uint2(x, y);
// Clamp coordinates to stay within texture bounds
sampleCoord.x = clamp(sampleCoord.x, 0, textureSize.x - 1);
sampleCoord.y = clamp(sampleCoord.y, 0, textureSize.y - 1);
sum += SourceTexture[sampleCoord];
}
}
// Average the samples and write to the destination texture
DestTexture[texelCoord] = sum / 9.0f;
}
Further Resources
Explore the following links for more in-depth information and advanced techniques: