Shader (shader) — is een programma dat op de GPU draait voor het verwerken van grafische gegevens: vertices, fragmenten of rekentaken. In tegenstelling tot gewone code op de CPU, werkt een shader parallel op honderden kernen en verwerkt elk element onafhankelijk. Volgens gegevens van Khronos Group (2025) worden shaders gebruikt in 100% van moderne 3D-toepassingen — van mobiele games tot UI-effecten in systeemtoepassingen. Ontwikkelaars schrijven shaders in gespecialiseerde talen: GLSL, HLSL, Metal Shading Language of SPIR-V.
Belangrijkste
Shader — is een klein programma, geschreven in een gespecialiseerde taal en uitgevoerd op de GPU. Elke shader verwerkt één gegevenselement — een vertex, fragment (pixel) of werkelement — onafhankelijk van anderen. De parallelle verwerking van de GPU maakt het mogelijk duizenden shader-instanties tegelijkertijd uit te voeren.
De term werd geïntroduceerd door Pixar in de jaren 1980 om programma's te beschrijven die het uiterlijk van oppervlakken in RenderMan beheren. In real-time verschenen shaders met de NVIDIA GeForce 3 (2001) — de eerste videokaart met een programmeerbare pijplijn. Vóór shaders werd grafiek geconfigureerd via vaste parameters: kleur, textuur, mengmodus.
Volgens gegevens van Unity Technologies (2025) gebruikt een gemiddelde mobiele game 50–200 shaders. Elke shader heeft varianten (variants) voor verschillende platforms, kwaliteitsniveaus en belichtingsconfiguraties. Het aantal varianten kan oplopen tot 10.000 per project.
De architectuur van een shader is verdeeld in invoergegevens (attributen, uniform-variabelen, texturen), programmacode en uitvoergegevens (kleur, positie). Uniform-variabelen worden vanaf de CPU ingesteld voordat de shader wordt gestart en blijven onveranderd voor alle elementen van het frame. Attributen zijn gegevens per element (vertexcoördinaten, normaal, UV-coördinaten).
SIMT (Single Instruction, Multiple Threads) — het uitvoeringsmodel van shaders op de GPU. Eén instructie wordt eenmaal geladen maar uitgevoerd door honderden threads op verschillende gegevens. Bijvoorbeeld, Fragment Shader voor 1000 pixels start 1000 threads met dezelfde code, maar elke thread ontvangt zijn eigen UV-coördinaten en geïnterpoleerde attributen.
Een belangrijk gevolg van SIMT: vertakkingen in shaders zijn duur. Als binnen een if-blok de helft van de threads de ene tak en de helft de andere tak volgt, voert de GPU beide takken sequentieel uit voor alle threads. De prestaties dalen tot 50%. Vermijd dynamische vertakkingen in shaders of minimaliseer ze door voorafgaande berekeningen op de CPU.
Moderne GPU's ondersteunen verschillende soorten shaders, elk voor zijn eigen fase van de grafische of rekenpijplijn. Laten we de belangrijkste soorten bekijken: vertex, fragment, compute en moderne mesh-shaders.
| Soort shader | Pijplijnfase | Verwerkt | Taal |
|---|---|---|---|
| Vertex Shader | Vertex | Elke vertex | GLSL, HLSL |
| Fragment Shader | Fragment | Elke pixel | GLSL, HLSL |
| Compute Shader | Rekenkundig | Willekeurige gegevens | GLSL, HLSL |
| Geometry Shader | Geometrisch | Primitieven | GLSL, HLSL |
| Tessellation Shader | Tessellatie | Patches | GLSL, HLSL |
| Mesh Shader | Mesh-pijplijn | Task + Mesh | HLSL, MSL |
Vertex Shader verwerkt elke vertex van de geometrie: transformeert coördinaten, berekent belichting, stuurt gegevens door naar de fragment shader. Dit is een verplichte fase van de grafische pijplijn — zonder deze komen vertices niet op het scherm. Vertex Shader wordt aangeroepen voor elke vertex van elk frame.
Fragment Shader (of Pixel Shader) — de meest resource-intensieve fase. Het berekent de uiteindelijke kleur van elk fragment met behulp van texturen, belichting en materiaal. Fragment Shader wordt uitgevoerd voor elke pixel die door geometrie wordt bedekt, rekening houdend met alle toegepaste texturen en effecten. Optimalisatie van deze shader levert de grootste prestatieverbetering op.
Compute Shader — universele shader voor willekeurige berekeningen op de GPU. In tegenstelling tot grafische shaders is deze niet gebonden aan geometrie of pixels. Compute Shader werkt met willekeurige databuffers via werkgroepen. Het wordt gebruikt voor fysica, deeltjessimulatie, post-effecten en inferentie van neurale netwerken.
Mesh Shader — het nieuwste type shader, verschenen in NVIDIA Turing (2018) en Metal 3. Mesh Shader vervangt de vertex-, geometrie- en tessellatieshaders door één mesh-pijplijn. Het werkt samen met Task Shader: Task Shader beslist welke mesh-groepen worden gerenderd, Mesh Shader genereert geometrie ter plekke.
Shaders worden geschreven in gespecialiseerde talen die worden gecompileerd naar machinecode van de GPU. De keuze van de taal hangt af van het platform en de API. Laten we de belangrijkste talen bekijken: GLSL, HLSL, Metal Shading Language en het tussenformaat SPIR-V.
GLSL — de shadertaal voor OpenGL, OpenGL ES en WebGL. De syntaxis is gebaseerd op C met toevoeging van vectortypen (vec2, vec4, mat4) en ingebouwde functies (texture, normalize, reflect). GLSL ES — de versie voor mobiele apparaten met beperkte precisie (lowp, mediump, highp). Volgens gegevens van Khronos (2025) blijft GLSL de meest wijdverbreide shadertaal vanwege de cross-platform mogelijkheden.
#version 300 es
precision mediump float;
in vec2 v_texCoord;
uniform sampler2D u_texture;
out vec4 fragColor;
void main() {
fragColor = texture(u_texture, v_texCoord);
}
Een eenvoudige fragment shader leest de kleur uit een textuur via UV-coördinaten. precision mediump geeft de GPU de opdracht halve precisie te gebruiken voor float — versnelt de uitvoering op mobiele apparaten met 25–40%.
HLSL — de shadertaal van Microsoft voor DirectX. De syntaxis ligt dichter bij C++ met ondersteuning voor klassen, structuren en sjablonen. HLSL wordt gebruikt in Windows-toepassingen en ondersteunt via Shader Model 6.7+ Ray Tracing, Mesh Shaders en Sampler Feedback. Voor mobiele ontwikkeling wordt HLSL niet direct gebruikt, maar het wordt gecompileerd naar SPIR-V voor Vulkan.
MSL (Metal Shading Language) — de shadertaal van Apple, gebaseerd op C++14. In tegenstelling tot GLSL en HLSL wordt MSL samen met de applicatie gecompileerd, waardoor JIT-compilatie op het apparaat wordt geëlimineerd. MSL ondersteunt pointers, sjablonen en de C++-standaardbibliotheek. Het wordt gebruikt op alle Apple-apparaten: iPhone, iPad, Mac, Apple TV.
#include <metal_stdlib>
using namespace metal;
struct VertexOut {
float4 position [[position]];
float2 texCoord;
};
fragment float4
myFragment(VertexOut in [[stage_in]],
texture2d<float> tex [[texture(0)]]) {
constexpr sampler s = sampler(filter::linear);
return tex.sample(s, in.texCoord);
}
MSL gebruikt attributen [[position]], [[stage_in]] en [[texture(N)]] voor het binden van resources. De Apple-compiler genereert geoptimaliseerde code voor de huidige GPU, rekening houdend met het aantal registers en cache.
SPIR-V — binair tussenformaat voor shaders, de standaard voor Vulkan. Shaders worden geschreven in GLSL of HLSL, gecompileerd naar SPIR-V en geladen in een Vulkan-applicatie. SPIR-V is niet gebonden aan een specifieke taal — er bestaan compilers van Rust, Python, OpenCL C naar SPIR-V.
Shaders op mobiele platforms hebben beperkingen vergeleken met desktop: minder registers, beperkte precisie en het ontbreken van ondersteuning voor sommige instructies. Laten we de specificiteit van shaders op Android (Vulkan/OpenGL ES) en iOS (Metal) bekijken.
Android gebruikt GLSL ES voor OpenGL en SPIR-V voor Vulkan. OpenGL ES 3.2 ondersteunt shaders met standaard mediump-precisie. Vulkan vereist expliciete compilatie van GLSL naar SPIR-V via glslangValidator. Op Android worden shaders geladen uit tekstuele resources of uit gecompileerde SPIR-V-bestanden.
Qualcomm Adreno GPU optimaliseert shaders op stuurprogrammaniveau. Aanbevelingen: gebruik mediump voor kleur en UV, highp alleen voor posities; vermijd textuursampling in vertex shaders; groepeer berekeningen in vectoren (vec4 in plaats van 4x float). Volgens gegevens van Qualcomm (2025) leveren deze optimalisaties een verbetering van 30–50% op.
iOS gebruikt uitsluitend Metal Shading Language. Shaders worden samen met de applicatie gecompileerd naar machinecode via Xcode. Metal biedt Shader Debugger en GPU Capture voor het profileren van shaders. Apple GPU (TBDR) heeft specifieke optimalisaties: early fragment test, memoryless render targets en programmeerbaar mengen.
Volgens gegevens van Apple WWDC 2024 is het voor iOS-shaders essentieel om half te gebruiken in plaats van float, de registerdruk te beperken (max 64 registers) en afhankelijke textuursampling te vermijden. iOS-shaders moeten compact zijn — optimale grootte is 50–100 ALU-instructies.
De game-engines Unity en Unreal Engine bieden visuele shader-editors (Shader Graph, Material Editor) en abstracte talen (ShaderLab, USF). De ontwikkelaar schrijft de shader in een hoogwaardige taal en de engine compileert deze voor het doelplatform. Unity gebruikt HLSL als tussentaal, Unreal — USF (Unreal Shader Format).
Optimalisatie van shaders — een cruciale fase in de grafische ontwikkeling voor mobiele apparaten. Een slecht geschreven shader kan de FPS verlagen van 60 naar 20. Laten we de belangrijkste regels en technieken voor optimalisatie bekijken.
Gebruik de minimale voldoende precisie: lowp voor kleur en UV, mediump voor normalen en belichting, highp alleen voor posities en matrices. Volgens gegevens van ARM (2025) zijn mediump-bewerkingen 2 keer sneller dan highp op Mali GPU. In GLSL ES wordt dit ingesteld via precision-kwalificatoren.
Textuursampling — de duurste bewerking in een shader (10–30 cycli tegenover 1–2 voor een ALU-instructie). Verminder het aantal samples: combineer texturen in atlassen, gebruik opslag in uniform-arrays in plaats van texturen, cache resultaten van naburige pixels via derivative-instructies.
Dynamische vertakkingen (if met een uniform-variabele) verminderen de prestaties van de SIMT-pijplijn. Vervang vertakkingen door wiskundige functies: mix(), step(), smoothstep() en clamp(). Deze functies worden uitgevoerd in 1–2 instructies, terwijl een vertakking 8–16 instructies kan kosten vanwege thread-divergentie.
// Slecht: dynamische vertakking in shader
if (u_enableLight) {
color *= computeLighting(normal);
}
// Goed: wiskundige vervanging van vertakking
color *= mix(1.0, computeLighting(normal),
float(u_enableLight));
mix() voert lineaire interpolatie uit tussen twee waarden. Wanneer u_enableLight=0 is, wordt 1.0 geretourneerd — de kleur verandert niet. Wanneer u_enableLight=1 is, wordt het belichtingsresultaat geretourneerd. Geen vertakking — alle threads voeren dezelfde code uit.
Laten we praktische voorbeelden van shaders voor mobiele ontwikkeling bekijken — van eenvoudige arcering tot procedurele texturegeneratie. Elk voorbeeld demonstreert een specifieke techniek.
Het Phong-model — het klassieke belichtingsmodel met diffuse en spiegelende componenten. Vertex Shader berekent de belichting voor elke vertex, Fragment Shader interpoleert alleen het resultaat (Gouraud shading). Geschikt voor mobiele apparaten vanwege de lage kosten.
#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec3 a_normal;
uniform mat4 u_mvp;
uniform mat4 u_modelView;
out vec3 v_color;
void main() {
vec3 normal = normalize(mat3(u_modelView) * a_normal);
vec3 lightDir = normalize(vec3(0.0, 1.0, 1.0));
float diff = max(dot(normal, lightDir), 0.0);
v_color = vec3(0.8, 0.2, 0.2) * (0.3 + diff * 0.7);
gl_Position = u_mvp * a_position;
}
De shader berekent diffuse belichting: het inwendige product van de normaal en de lichtrichting. Het resultaat wordt gemengd met ambient (0.3) en diffuse (0.7) componenten. Het voert 10–15 ALU-instructies per vertex uit — acceptabel voor 100.000 polygonen bij 60 FPS op een mobiele GPU.
Procedurele texturen worden gegenereerd in de Fragment Shader zonder laden uit een bestand. Bespaart videogeheugen en vereenvoudigt animatie. Voorbeeld — een schaakbord met variabele celgrootte. Slechts enkele ALU-instructies — minimale belasting van de GPU.
#version 300 es
precision mediump float;
in vec2 v_uv;
uniform float u_cells;
out vec4 fragColor;
void main() {
vec2 cell = floor(v_uv * u_cells);
float isWhite = mod(cell.x + cell.y, 2.0);
fragColor = mix(vec4(0.1, 0.1, 0.1, 1.0),
vec4(0.9, 0.9, 0.9, 1.0),
isWhite);
}
8 ALU-instructies — de ideale shader voor mobiele apparaten. u_cells bepaalt het aantal cellen per as. floor en mod — goedkope bewerkingen, uitgevoerd in 1 cyclus op alle mobiele GPU's.
Veelgestelde vragen
Shader wordt uitgevoerd op de GPU in SIMT-modus — één instructie voor honderden threads. Een gewoon C++-programma — op de CPU in MIMD-modus. De shader heeft geen toegang tot het bestandssysteem, invoer/uitvoer en dynamisch geheugen. Het ontvangt gegevens via buffers en texturen en retourneert positie of kleur.
GLSL — voor OpenGL ES en WebGL, cross-platform. Metal Shading Language — voor iOS. HLSL — de basis voor Unity en Unreal Engine. Begin met GLSL — het begrip van de basisprincipes is overdraagbaar naar elke shadertaal.
Belangrijkste oorzaken: hoge precisie (highp in plaats van mediump), overmatige textuursampling, dynamische vertakkingen en overschrijding van de registerlimiet. Gebruik RenderDoc of Xcode GPU Frame Debugger voor shader-analyse.
Compute Shader — shader voor willekeurige berekeningen op de GPU: deeltjesfysica, beeldverwerking, stofsimulatie. In tegenstelling tot Fragment Shader is het niet gebonden aan pixels en kan het naar willekeurige buffers schrijven.
Ja, via visuele editors: Shader Graph in Unity, Material Editor in Unreal Engine of Metal Shader Converter. Ze genereren shadercode op basis van een nodediagram. Voor diepgaande optimalisatie is begrip van de shadertaal vereist.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook