Els shaders són programes que s'executen en la GPU (Unitat de Processament Gràfic) i són essencials per a la renderització moderna en temps real. En aquest mòdul, aprendrem què són els shaders, els diferents tipus de shaders i com s'utilitzen en DirectX.
Què són els Shaders?
Els shaders són petits programes escrits en llenguatges de programació especialitzats com HLSL (High-Level Shading Language) per a DirectX. Aquests programes s'executen en la GPU i s'utilitzen per a diverses tasques de renderització, com ara la transformació de geometria, l'aplicació de textures i la il·luminació.
Tipus de Shaders
- Vertex Shaders: Processen cada vèrtex individualment. S'utilitzen per transformar les coordenades dels vèrtexs, aplicar animacions i calcular propietats com normals i coordenades de textura.
- Pixel Shaders: Processen cada píxel individualment. S'utilitzen per determinar el color final de cada píxel, aplicant textures, il·luminació i altres efectes visuals.
- Geometry Shaders: Processen primitives (com triangles) i poden generar noves primitives. S'utilitzen per a efectes avançats com la tessellació i la generació de geometria dinàmica.
- Compute Shaders: S'utilitzen per a càlculs generals en la GPU que no estan directament relacionats amb la renderització. Poden ser utilitzats per a simulacions físiques, processament d'imatges, etc.
Estructura d'un Shader
Un shader típic en HLSL té la següent estructura:
// Vertex Shader struct VS_INPUT { float4 Pos : POSITION; float4 Color : COLOR; }; struct VS_OUTPUT { float4 Pos : SV_POSITION; float4 Color : COLOR; }; VS_OUTPUT VSMain(VS_INPUT input) { VS_OUTPUT output; output.Pos = input.Pos; output.Color = input.Color; return output; } // Pixel Shader float4 PSMain(VS_OUTPUT input) : SV_TARGET { return input.Color; }
Explicació del Codi
- VS_INPUT: Estructura que defineix les dades d'entrada per al vertex shader. En aquest cas, inclou la posició i el color del vèrtex.
- VS_OUTPUT: Estructura que defineix les dades de sortida del vertex shader, que seran utilitzades pel pixel shader.
- VSMain: Funció principal del vertex shader. Transforma les dades d'entrada i les passa a la sortida.
- PSMain: Funció principal del pixel shader. Rep les dades del vertex shader i calcula el color final del píxel.
Compilació i Utilització de Shaders
Els shaders han de ser compilats abans de ser utilitzats en una aplicació DirectX. La compilació converteix el codi HLSL en codi de màquina que pot ser executat per la GPU.
Exemple de Compilació de Shaders en DirectX
ID3DBlob* vertexShaderBlob = nullptr; ID3DBlob* pixelShaderBlob = nullptr; ID3DBlob* errorBlob = nullptr; // Compilar el vertex shader HRESULT hr = D3DCompileFromFile(L"shader.hlsl", nullptr, nullptr, "VSMain", "vs_5_0", 0, 0, &vertexShaderBlob, &errorBlob); if (FAILED(hr)) { if (errorBlob) { OutputDebugStringA((char*)errorBlob->GetBufferPointer()); errorBlob->Release(); } // Manejar l'error } // Compilar el pixel shader hr = D3DCompileFromFile(L"shader.hlsl", nullptr, nullptr, "PSMain", "ps_5_0", 0, 0, &pixelShaderBlob, &errorBlob); if (FAILED(hr)) { if (errorBlob) { OutputDebugStringA((char*)errorBlob->GetBufferPointer()); errorBlob->Release(); } // Manejar l'error } // Crear el vertex shader ID3D11VertexShader* vertexShader; hr = device->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), nullptr, &vertexShader); if (FAILED(hr)) { // Manejar l'error } // Crear el pixel shader ID3D11PixelShader* pixelShader; hr = device->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), nullptr, &pixelShader); if (FAILED(hr)) { // Manejar l'error }
Explicació del Codi
- D3DCompileFromFile: Funció que compila el shader des d'un fitxer HLSL. Els paràmetres inclouen el nom del fitxer, el punt d'entrada (funció principal) i el perfil del shader (vs_5_0 per a vertex shader, ps_5_0 per a pixel shader).
- CreateVertexShader i CreatePixelShader: Funcions que creen els shaders compilats i els preparen per ser utilitzats en la pipeline de renderització.
Exercici Pràctic
Objectiu
Crear un shader simple que canviï el color dels vèrtexs a un color fix.
Passos
- Escriu un vertex shader que passi les coordenades dels vèrtexs sense canvis.
- Escriu un pixel shader que estableixi el color de tots els píxels a vermell.
- Compila i utilitza els shaders en una aplicació DirectX.
Solució
shader.hlsl
// Vertex Shader struct VS_INPUT { float4 Pos : POSITION; }; struct VS_OUTPUT { float4 Pos : SV_POSITION; }; VS_OUTPUT VSMain(VS_INPUT input) { VS_OUTPUT output; output.Pos = input.Pos; return output; } // Pixel Shader float4 PSMain(VS_OUTPUT input) : SV_TARGET { return float4(1.0, 0.0, 0.0, 1.0); // Color vermell }
Codi C++ per Compilar i Utilitzar els Shaders
// Compilar i crear els shaders (mateix codi que l'exemple anterior) // Establir els shaders en la pipeline de renderització context->VSSetShader(vertexShader, nullptr, 0); context->PSSetShader(pixelShader, nullptr, 0);
Conclusió
En aquesta secció, hem après què són els shaders, els diferents tipus de shaders i com s'escriuen i utilitzen en DirectX. Els shaders són una part fonamental de la programació gràfica moderna i ens permeten crear efectes visuals complexos i personalitzats. En els següents temes, aprofundirem en la creació de vertex shaders i pixel shaders més avançats.
Curs de Programació DirectX
Mòdul 1: Introducció a DirectX
- Què és DirectX?
- Configuració de l'Entorn de Desenvolupament
- Comprendre l'API de DirectX
- Crear la Teva Primera Aplicació DirectX
Mòdul 2: Conceptes Bàsics de Direct3D
- Introducció a Direct3D
- Inicialitzar Direct3D
- Renderitzar un Triangle
- Gestionar el Bucle de Renderització
Mòdul 3: Treballar amb Shaders
Mòdul 4: Tècniques Avançades de Renderització
Mòdul 5: Models 3D i Animació
Mòdul 6: Optimització del Rendiment
- Perfilat i Depuració
- Optimitzar el Rendiment de la Renderització
- Gestió de Memòria
- Multifil en DirectX