Overview
DirectX shaders are small programs that run on the GPU to implement rendering effects, compute operations, and more. They are written in the High Level Shader Language (HLSL) and compiled into bytecode that Direct3D consumes.
Shader Types
- Vertex Shader: Processes vertex attributes and outputs transformed vertices.
- Pixel (Fragment) Shader: Determines the color of each pixel.
- Geometry Shader: Generates geometry primitives from existing ones.
- Hull & Domain Shaders: Used for tessellation.
- Compute Shader: General‑purpose GPU computing.
- Raytracing Shaders: Includes ray generation, miss, hit, and callable shaders.
HLSL Basics
HLSL syntax is similar to C. Below is a minimal vertex shader example:
// SimpleVertexShader.hlsl
struct VSInput {
float3 pos : POSITION;
float4 color : COLOR;
};
struct VSOutput {
float4 pos : SV_POSITION;
float4 color : COLOR;
};
VSOutput main(VSInput input) {
VSOutput output;
output.pos = float4(input.pos, 1.0);
output.color = input.color;
return output;
}
Compilation & Pipeline Integration
Shaders are compiled with D3DCompile
or the newer DXC
compiler. The resulting bytecode is then bound to the device context.
// Example using D3DCompile
ID3DBlob* shaderBlob = nullptr;
ID3DBlob* errorBlob = nullptr;
HRESULT hr = D3DCompileFromFile(
L"SimpleVertexShader.hlsl",
nullptr,
nullptr,
"main",
"vs_5_0",
0,
0,
&shaderBlob,
&errorBlob);
if (FAILED(hr)) {
OutputDebugStringA((char*)errorBlob->GetBufferPointer());
}
device->CreateVertexShader(shaderBlob->GetBufferPointer(),
shaderBlob->GetBufferSize(),
nullptr,
&vertexShader);
Sample Code
Below is a complete minimal Direct3D 11 initialization that loads a vertex and pixel shader and draws a colored triangle.
#include <d3d11.h>
#pragma comment(lib, "d3d11.lib")
#include <d3dcompiler.h>
#pragma comment(lib, "d3dcompiler.lib")
// Vertex structure and data
struct Vertex { float3 pos; float4 color; };
Vertex vertices[] = {
{ { 0.0f, 0.5f, 0.0f }, {1,0,0,1} },
{ { 0.5f, -0.5f, 0.0f }, {0,1,0,1} },
{ { -0.5f,-0.5f, 0.0f }, {0,0,1,1} }
};
int main() {
// (Window creation code omitted for brevity)
// Initialize D3D device and swap chain
D3D_FEATURE_LEVEL featureLevel;
ID3D11Device* device = nullptr;
ID3D11DeviceContext* context = nullptr;
IDXGISwapChain* swapChain = nullptr;
DXGI_SWAP_CHAIN_DESC scDesc = {};
// ... fill scDesc and create device/swapChain ...
// Compile shaders
ID3DBlob* vsBlob; ID3DBlob* psBlob;
D3DCompileFromFile(L"SimpleVertexShader.hlsl", nullptr, nullptr,
"main", "vs_5_0", 0, 0, &vsBlob, nullptr);
D3DCompileFromFile(L"SimplePixelShader.hlsl", nullptr, nullptr,
"main", "ps_5_0", 0, 0, &psBlob, nullptr);
// Create shaders
ID3D11VertexShader* vs; ID3D11PixelShader* ps;
device->CreateVertexShader(vsBlob->GetBufferPointer(),
vsBlob->GetBufferSize(), nullptr, &vs);
device->CreatePixelShader(psBlob->GetBufferPointer(),
psBlob->GetBufferSize(), nullptr, &ps);
context->VSSetShader(vs, nullptr, 0);
context->PSSetShader(ps, nullptr, 0);
// Input layout
D3D11_INPUT_ELEMENT_DESC layoutDesc[] = {
{ "POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,
D3D11_INPUT_PER_VERTEX_DATA,0 },
{ "COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,12,
D3D11_INPUT_PER_VERTEX_DATA,0 }
};
ID3D11InputLayout* layout;
device->CreateInputLayout(layoutDesc, 2, vsBlob->GetBufferPointer(),
vsBlob->GetBufferSize(), &layout);
context->IASetInputLayout(layout);
// Vertex buffer
D3D11_BUFFER_DESC vbDesc = { sizeof(vertices), D3D11_USAGE_DEFAULT,
D3D11_BIND_VERTEX_BUFFER, 0, 0, 0 };
D3D11_SUBRESOURCE_DATA initData = { vertices, 0, 0 };
ID3D11Buffer* vertexBuffer;
device->CreateBuffer(&vbDesc, &initData, &vertexBuffer);
UINT stride = sizeof(Vertex);
UINT offset = 0;
context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Main loop (simplified)
while (true) {
// Clear screen
float clearColor[4] = {0.1f,0.1f,0.1f,1.0f};
ID3D11RenderTargetView* rtv; // obtained from swap chain
context->ClearRenderTargetView(rtv, clearColor);
// Draw
context->Draw(3, 0);
// Present
swapChain->Present(1,0);
}
}