Shader (shader) — är ett program som körs på GPU för att bearbeta grafisk data: vertexar, fragment eller beräkningsuppgifter. Till skillnad från vanlig kod på CPU arbetar en shader parallellt på hundratals kärnor och bearbetar varje element oberoende. Enligt Khronos Group (2025) används shaders i 100% av moderna 3D-applikationer — från mobila spel till UI-effekter i systemapplikationer. Utvecklare skriver shaders i specialiserade språk: GLSL, HLSL, Metal Shading Language eller SPIR-V.
Huvudpunkter
Shader — är ett litet program, skrivet på ett specialiserat språk och kört på GPU. Varje shader bearbetar ett dataelement — en vertex, ett fragment (pixel) eller ett arbetselement — oberoende av andra. GPU:ns parallellism gör det möjligt att köra tusentals shader-instanser samtidigt.
Termen introducerades av Pixar på 1980-talet för att beskriva program som styr utseendet på ytor i RenderMan. I realtid dök shaders upp med NVIDIA GeForce 3 (2001) — det första grafikkortet med programmerbar pipeline. Före shaders konfigurerades grafik via fasta parametrar: färg, textur, blandningsläge.
Enligt Unity Technologies (2025) använder ett genomsnittligt mobilt spel 50–200 shaders. Varje shader har varianter (variants) för olika plattformar, kvalitetsnivåer och belysningskonfigurationer. Antalet varianter kan nå 10 000 per projekt.
Shaderarkitekturen delas in i indata (attribut, uniform-variabler, texturer), programkod och utdata (färg, position). Uniform-variabler ställs in från CPU innan shadern startas och är oförändrade för alla element i bildrutan. Attribut är data per element (vertexkoordinater, normal, UV-koordinater).
SIMT (Single Instruction, Multiple Threads) — exekveringsmodell för shaders på GPU. En instruktion laddas en gång men exekveras av hundratals trådar på olika data. Till exempel, Fragment Shader för 1000 pixlar startar 1000 trådar med samma kod, men varje tråd får sina egna UV-koordinater och interpolerade attribut.
En viktig konsekvens av SIMT: förgreningar i shaders är dyra. Om hälften av trådarna går en gren och hälften en annan inom ett if-block, kör GPU båda grenarna sekventiellt för alla trådar. Prestandan sjunker till 50%. Undvik dynamiska förgreningar i shaders eller minimera dem genom förberäkningar på CPU.
Moderna GPU:er stöder flera typer av shaders, var och en för sitt eget stadium i den grafiska eller beräkningsmässiga pipelinen. Låt oss titta på huvudtyperna: vertex-, fragment-, compute- och moderna mesh-shaders.
| Shadertyp | Pipeline-stadium | Bearbetar | Språk |
|---|---|---|---|
| Vertex Shader | Vertex | Varje vertex | GLSL, HLSL |
| Fragment Shader | Fragment | Varje pixel | GLSL, HLSL |
| Compute Shader | Beräknings | Godtycklig data | GLSL, HLSL |
| Geometry Shader | Geometrisk | Primitiver | GLSL, HLSL |
| Tessellation Shader | Tessellering | Patchar | GLSL, HLSL |
| Mesh Shader | Mesh-pipeline | Task + Mesh | HLSL, MSL |
Vertex Shader bearbetar varje vertex i geometrin: transformerar koordinater, beräknar belysning, skickar data till fragment shadern. Detta är ett obligatoriskt stadium i den grafiska pipelinen — utan det når inte vertexarna skärmen. Vertex Shader anropas för varje vertex i varje bildruta.
Fragment Shader (eller Pixel Shader) — det mest resurskrävande stadiet. Det beräknar slutfärgen för varje fragment med hjälp av texturer, belysning och material. Fragment Shader exekveras för varje pixel som täcks av geometri, med hänsyn till alla applicerade texturer och effekter. Optimering av denna shader ger den största prestandaökningen.
Compute Shader — universell shader för godtyckliga beräkningar på GPU. Till skillnad från grafiska shaders är den inte bunden till geometri eller pixlar. Compute Shader arbetar med godtyckliga databuffertar via arbetsgrupper. Används för fysik, partikelsimulering, post-effekter och slutledning av neurala nätverk.
Mesh Shader — den nyaste shadertypen, som dök upp i NVIDIA Turing (2018) och Metal 3. Mesh Shader ersätter vertex-, geometri- och tesselleringsshaders med en enda mesh-pipeline. Arbetar tillsammans med Task Shader: Task Shader bestämmer vilka mesh-grupper som ska renderas, Mesh Shader genererar geometri i farten.
Shaders skrivs på specialiserade språk som kompileras till GPU-maskinkod. Valet av språk beror på plattform och API. Låt oss titta på huvudspråken: GLSL, HLSL, Metal Shading Language och mellanformatet SPIR-V.
GLSL — shaderspråket för OpenGL, OpenGL ES och WebGL. Syntaxen är baserad på C med tillägg av vektortyper (vec2, vec4, mat4) och inbyggda funktioner (texture, normalize, reflect). GLSL ES — versionen för mobila enheter med begränsad precision (lowp, mediump, highp). Enligt Khronos (2025) förblir GLSL det mest spridda shaderspråket tack vare sin cross-plattformsförmåga.
#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);
}
En enkel fragment shader läser färg från en textur baserat på UV-koordinater. precision mediump instruerar GPU:n att använda halv precision för float — påskyndar exekvering på mobila enheter med 25–40%.
HLSL — Microsofts shaderspråk för DirectX. Syntaxen ligger närmare C++ med stöd för klasser, strukturer och mallar. HLSL används i Windows-applikationer och stöder via Shader Model 6.7+ Ray Tracing, Mesh Shaders och Sampler Feedback. För mobil utveckling används HLSL inte direkt, men kompileras till SPIR-V för Vulkan.
MSL (Metal Shading Language) — Apples shaderspråk, baserat på C++14. Till skillnad från GLSL och HLSL kompileras MSL tillsammans med applikationen, vilket eliminerar JIT-kompilering på enheten. MSL stöder pekare, mallar och C++ standardbibliotek. Används på alla Apple-enheter: 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 använder attributen [[position]], [[stage_in]] och [[texture(N)]] för att binda resurser. Apples kompilator genererar optimerad kod för den aktuella GPU:n med hänsyn till antalet register och cache.
SPIR-V — mellanliggande binärt format för shaders, standard för Vulkan. Shaders skrivs i GLSL eller HLSL, kompileras till SPIR-V och laddas i Vulkan-applikationen. SPIR-V är inte bundet till ett specifikt språk — det finns kompilatorer från Rust, Python, OpenCL C till SPIR-V.
Shaders på mobila plattformar har begränsningar jämfört med stationära: färre register, begränsad precision och brist på stöd för vissa instruktioner. Låt oss titta på specifikationen för shaders på Android (Vulkan/OpenGL ES) och iOS (Metal).
Android använder GLSL ES för OpenGL och SPIR-V för Vulkan. OpenGL ES 3.2 stöder shaders med standard mediump-precision. Vulkan kräver explicit kompilering av GLSL till SPIR-V via glslangValidator. På Android laddas shaders från textresurser eller från kompilerade SPIR-V-filer.
Qualcomm Adreno GPU optimerar shaders på drivrutinsnivå. Rekommendationer: använd mediump för färg och UV, highp endast för positioner; undvik textur sampling i vertex shaders; gruppera beräkningar i vektorer (vec4 istället för 4x float). Enligt Qualcomm (2025) ger dessa optimeringar en förbättring på 30–50%.
iOS använder uteslutande Metal Shading Language. Shaders kompileras till maskinkod tillsammans med applikationen via Xcode. Metal tillhandahåller Shader Debugger och GPU Capture för profilering av shaders. Apple GPU (TBDR) har specifika optimeringar: early fragment test, memoryless render targets och programmerbar blandning.
Enligt Apple WWDC 2024 är det kritiskt för iOS-shaders att använda half istället för float, begränsa registertrycket (max 64 register) och undvika beroende textur sampling. iOS-shaders bör vara kompakta — optimal storlek är 50–100 ALU-instruktioner.
Spelmotorerna Unity och Unreal Engine tillhandahåller visuella shader-redigerare (Shader Graph, Material Editor) och abstrakta språk (ShaderLab, USF). Utvecklaren skriver shadern på ett högnivåspråk och motorn kompilerar den för målplattformen. Unity använder HLSL som mellanspråk, Unreal — USF (Unreal Shader Format).
Optimering av shaders — ett nyckelstadium i grafikutveckling för mobila enheter. En dåligt skriven shader kan sänka FPS från 60 till 20. Låt oss titta på huvudreglerna och teknikerna för optimering.
Använd minimal tillräcklig precision: lowp för färg och UV, mediump för normaler och belysning, highp endast för positioner och matriser. Enligt ARM (2025) är mediump-operationer 2 gånger snabbare än highp på Mali GPU. I GLSL ES ställs detta in via precision-kvalificerare.
Textur sampling — den dyraste operationen i en shader (10–30 cykler jämfört med 1–2 för en ALU-instruktion). Minska antalet samplingar: kombinera texturer i atlaser, använd lagring i uniform-arrayer istället för texturer, cachla resultat från närliggande pixlar via derivatinstruktioner.
Dynamiska förgreningar (if med uniform-variabel) minskar prestandan för SIMT-pipelinen. Ersätt förgreningar med matematiska funktioner: mix(), step(), smoothstep() och clamp(). Dessa funktioner exekveras på 1–2 instruktioner, medan en förgrening kan kosta 8–16 instruktioner på grund av tråddivergens.
// Dåligt: dynamisk förgrening i shader
if (u_enableLight) {
color *= computeLighting(normal);
}
// Bra: matematisk ersättning av förgrening
color *= mix(1.0, computeLighting(normal),
float(u_enableLight));
mix() utför linjär interpolation mellan två värden. När u_enableLight=0 returneras 1.0 — färgen ändras inte. När u_enableLight=1 returneras belysningsresultatet. Ingen förgrening — alla trådar exekverar samma kod.
Låt oss titta på praktiska exempel på shaders för mobil utveckling — från enkel skuggning till procedurell texturgenerering. Varje exempel demonstrerar en specifik teknik.
Phong-modellen — klassisk belysningsmodell med diffus och spegelkomponent. Vertex Shader beräknar belysning för varje vertex, Fragment Shader interpolerar bara resultatet (Gouraud shading). Lämplig för mobila enheter på grund av låg kostnad.
#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;
}
Shadern beräknar diffus belysning: skalärprodukten av normalen och ljusriktningen. Resultatet blandas med ambient (0.3) och diffuse (0.7) komponenter. Utför 10–15 ALU-instruktioner per vertex — acceptabelt för 100 000 polygoner vid 60 FPS på en mobil GPU.
Procedurella texturer genereras i Fragment Shader utan att laddas från fil. Sparar videominne och förenklar animation. Exempel — ett schackbräde med variabel cellstorlek. Bara några ALU-instruktioner — minimal belastning på 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-instruktioner — idealisk shader för mobila enheter. u_cells anger antalet celler per axel. floor och mod — billiga operationer, utförda på 1 cykel på alla mobila GPU:er.
Vanliga frågor
Shader körs på GPU i SIMT-läge — en instruktion för hundratals trådar. Vanligt C++-program — på CPU i MIMD-läge. Shadern har inte tillgång till filsystem, in/utmatning och dynamiskt minne. Den tar emot data via buffertar och texturer och returnerar position eller färg.
GLSL — för OpenGL ES och WebGL, cross-platform. Metal Shading Language — för iOS. HLSL — grunden för Unity och Unreal Engine. Börja med GLSL — förståelse av grunderna överförs till vilket shaderspråk som helst.
Huvudorsaker: hög precision (highp istället för mediump), överdriven textur sampling, dynamiska förgreningar och överskridande av registergränsen. Använd RenderDoc eller Xcode GPU Frame Debugger för shader-analys.
Compute Shader — shader för godtyckliga beräkningar på GPU: partikelfysik, bildbehandling, tygsimulering. Till skillnad från Fragment Shader är den inte bunden till pixlar och kan skriva till godtyckliga buffertar.
Ja, genom visuella redigerare: Shader Graph i Unity, Material Editor i Unreal Engine eller Metal Shader Converter. De genererar shader-kod baserat på en nodgraf. För djupgående optimering krävs förståelse av shaderspråket.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också