Shader (шейдър) — е програма, която се изпълнява на GPU за обработка на графични данни: върхове, фрагменти или изчислителни задачи. За разлика от обикновения код на CPU, шейдърът работи паралелно на стотици ядра, обработвайки всеки елемент независимо. Според данни на Khronos Group (2025), шейдърите се използват в 100% от модерните 3D приложения — от мобилни игри до UI ефекти в системни приложения. Разработчиците пишат шейдъри на специализирани езици: GLSL, HLSL, Metal Shading Language или SPIR-V.
Основни
Shader — е малка програма, написана на специализиран език и изпълнявана на GPU. Всеки шейдър обработва един елемент данни — връх, фрагмент (пиксел) или работен елемент — независимо от другите. Паралелизмът на GPU позволява стартиране на хиляди инстанции на шейдър едновременно.
Терминът е въведен от Pixar през 80-те години на XX век за описване на програми, управляващи външния вид на повърхности в RenderMan. В реално време шейдърите се появяват с NVIDIA GeForce 3 (2001) — първата видеокарта с програмируем конвейер. Преди шейдърите графиката се конфигурираше чрез фиксирани параметри: цвят, текстура, режим на смесване.
Според данни на Unity Technologies (2025), средната мобилна игра използва 50–200 шейдъра. Всеки шейдър има варианти (variants) за различни платформи, нива на качество и конфигурации на осветление. Броят на вариантите може да достигне 10 000 на проект.
Архитектурата на шейдъра се дели на входни данни (атрибути, uniform променливи, текстури), програмен код и изходни данни (цвят, позиция). Uniform променливите се задават от CPU преди стартиране на шейдъра и са неизменни за всички елементи на кадъра. Атрибутите са данни на елемент (координати на връх, нормала, UV координати).
SIMT (Single Instruction, Multiple Threads) — модел на изпълнение на шейдъри на GPU. Една инструкция се зарежда веднъж, но се изпълнява от стотици нишки върху различни данни. Например Fragment Shader за 1000 пиксела стартира 1000 нишки с един и същ код, но всяка нишка получава своите UV координати и интерполирани атрибути.
Важно следствие от SIMT: разклоненията в шейдърите са скъпи. Ако вътре в if блок половината нишки отиват по едно разклонение, а половината по друго, GPU изпълнява и двете разклонения последователно за всички нишки. Производителността пада до 50%. Избягвайте динамични разклонения в шейдърите или ги минимизирайте чрез предварителни изчисления на CPU.
Съвременните GPU поддържат няколко типа шейдъри, всеки за своя етап от графичния или изчислителния конвейер. Нека разгледаме основните видове: върхови, фрагментни, compute и модерни mesh-шейдъри.
| Тип шейдър | Етап на конвейера | Обработва | Език |
|---|---|---|---|
| Vertex Shader | Върховен | Всеки връх | GLSL, HLSL |
| Fragment Shader | Фрагментен | Всеки пиксел | GLSL, HLSL |
| Compute Shader | Изчислителен | Произволни данни | GLSL, HLSL |
| Geometry Shader | Геометричен | Примитиви | GLSL, HLSL |
| Tessellation Shader | Теселация | Пачове | GLSL, HLSL |
| Mesh Shader | Mesh конвейер | Task + Mesh | HLSL, MSL |
Vertex Shader обработва всеки връх на геометрията: трансформира координати, изчислява осветление, предава данни на фрагментния шейдър. Това е задължителен етап на графичния конвейер — без него върховете не достигат до екрана. Vertex Shader се извиква за всеки връх на всеки кадър.
Fragment Shader (или Pixel Shader) — най-ресурсоемкият етап. Той изчислява крайния цвят на всеки фрагмент, използвайки текстури, осветление и материал. Fragment Shader се изпълнява за всеки пиксел, покрит от геометрия, като се вземат предвид всички наложени текстури и ефекти. Оптимизацията на този шейдър дава най-голямо увеличение на производителността.
Compute Shader — универсален шейдър за произволни изчисления на GPU. За разлика от графичните шейдъри, не е обвързан с геометрия или пиксели. Compute Shader работи с произволни буфери от данни чрез работни групи. Използва се за физика, симулация на частици, пост-ефекти и инференция на невронни мрежи.
Mesh Shader — най-новият тип шейдър, появил се в NVIDIA Turing (2018) и Metal 3. Mesh Shader замества върховия, геометричния и теселационния шейдър с един mesh конвейер. Работи в двойка с Task Shader: Task Shader решава кои mesh групи да се рендерират, Mesh Shader генерира геометрия в движение.
Шейдърите се пишат на специализирани езици, които се компилират до машинен код на GPU. Изборът на език зависи от платформата и API. Нека разгледаме основните езици: GLSL, HLSL, Metal Shading Language и междинния формат SPIR-V.
GLSL — език за шейдъри за OpenGL, OpenGL ES и WebGL. Синтаксисът е базиран на C с добавяне на векторни типове (vec2, vec4, mat4) и вградени функции (texture, normalize, reflect). GLSL ES — версия за мобилни устройства с ограничена точност (lowp, mediump, highp). Според данни на Khronos (2025), GLSL остава най-разпространеният език за шейдъри благодарение на крос-платформеността.
#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);
}
Прост фрагментен шейдър чете цвят от текстура по UV координати. precision mediump указва на GPU да използва половинна точност за float — ускорява изпълнението на мобилни устройства с 25–40%.
HLSL — език за шейдъри на Microsoft за DirectX. Синтаксисът е по-близък до C++ с поддръжка за класове, структури и шаблони. HLSL се използва в Windows приложения и чрез Shader Model 6.7+ поддържа Ray Tracing, Mesh Shaders и Sampler Feedback. За мобилна разработка HLSL не се използва директно, но се компилира до SPIR-V за Vulkan.
MSL (Metal Shading Language) — език за шейдъри на Apple, базиран на C++14. За разлика от GLSL и HLSL, MSL се компилира заедно с приложението, което елиминира JIT компилация на устройството. MSL поддържа указатели, шаблони и стандартната библиотека на C++. Използва се на всички Apple устройства: 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 използва атрибутите [[position]], [[stage_in]] и [[texture(N)]] за свързване на ресурси. Компилаторът на Apple генерира оптимизиран код за текущия GPU, като взема предвид броя на регистрите и кеша.
SPIR-V — междинен двоичен формат за шейдъри, стандарт за Vulkan. Шейдърите се пишат на GLSL или HLSL, компилират се до SPIR-V и се зареждат в Vulkan приложение. SPIR-V не е обвързан с конкретен език — съществуват компилатори от Rust, Python, OpenCL C до SPIR-V.
Шейдърите на мобилни платформи имат ограничения в сравнение с настолните: по-малко регистри, ограничена точност и липса на поддръжка за някои инструкции. Нека разгледаме спецификата на шейдърите на Android (Vulkan/OpenGL ES) и iOS (Metal).
Android използва GLSL ES за OpenGL и SPIR-V за Vulkan. OpenGL ES 3.2 поддържа шейдъри с подразбираща се mediump точност. Vulkan изисква изрична компилация на GLSL до SPIR-V чрез glslangValidator. На Android шейдърите се зареждат от текстови ресурси или от компилирани SPIR-V файлове.
Qualcomm Adreno GPU оптимизира шейдъри на ниво драйвер. Препоръки: използвайте mediump за цвят и UV, highp само за позиции; избягвайте текстурни проби във върхови шейдъри; групирайте изчисления във вектори (vec4 вместо 4x float). Според данни на Qualcomm (2025), тези оптимизации дават подобрение от 30–50%.
iOS използва изключително Metal Shading Language. Шейдърите се компилират до машинен код заедно с приложението чрез Xcode. Metal предоставя Shader Debugger и GPU Capture за профилиране на шейдъри. Apple GPU (TBDR) има специфични оптимизации: early fragment test, memoryless render targets и програмируемо смесване.
Според данни на Apple WWDC 2024, за iOS шейдъри е критично да се използва half вместо float, да се ограничи регистровото налягане (макс 64 регистъра) и да се избягват зависими текстурни проби. iOS шейдърите трябва да са компактни — оптимален размер 50–100 ALU инструкции.
Игровите двигатели Unity и Unreal Engine предоставят визуални редактори за шейдъри (Shader Graph, Material Editor) и абстрактни езици (ShaderLab, USF). Разработчикът пише шейдър на език от високо ниво, а двигателят го компилира за целевата платформа. Unity използва HLSL като междинен език, Unreal — USF (Unreal Shader Format).
Оптимизация на шейдъри — ключов етап от разработката на графика за мобилни устройства. Лошо написан шейдър може да намали FPS от 60 на 20. Нека разгледаме основните правила и техники за оптимизация.
Използвайте минимална достатъчна точност: lowp за цвят и UV, mediump за нормали и осветление, highp само за позиции и матрици. Според данни на ARM (2025), mediump операциите са 2 пъти по-бързи от highp на Mali GPU. В GLSL ES това се задава чрез precision квалификатори.
Текстурните проби — най-скъпата операция в шейдър (10–30 цикъла срещу 1–2 за ALU инструкция). Намалете броя на пробите: обединявайте текстури в атласи, използвайте съхранение в uniform масиви вместо текстури, кеширайте резултатите от съседни пиксели чрез derivative инструкции.
Динамични разклонения (if с uniform променлива) намаляват производителността на SIMT конвейера. Заменяйте разклоненията с математически функции: mix(), step(), smoothstep() и clamp(). Тези функции се изпълняват за 1–2 инструкции, докато разклонение може да струва 8–16 инструкции поради дивергенция на нишките.
// Лошо: динамично разклонение в шейдър
if (u_enableLight) {
color *= computeLighting(normal);
}
// Добре: математическо заместване на разклонение
color *= mix(1.0, computeLighting(normal),
float(u_enableLight));
mix() извършва линейна интерполация между две стойности. Когато u_enableLight=0, се връща 1.0 — цветът не се променя. Когато u_enableLight=1, се връща резултатът от осветлението. Без разклонение — всички нишки изпълняват един и същ код.
Нека разгледаме практически примери за шейдъри за мобилна разработка — от просто оцветяване до процедурно генериране на текстури. Всеки пример демонстрира конкретна техника.
Модел на Phong — класически модел на осветление с дифузна и огледална компонента. Vertex Shader изчислява осветлението за всеки връх, Fragment Shader само интерполира резултата (Gouraud shading). Подходящ за мобилни устройства поради ниската цена.
#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;
}
Шейдърът изчислява diffuse осветление: скаларно произведение на нормалата и посоката на светлината. Резултатът се смесва с ambient (0.3) и diffuse (0.7) компоненти. Изпълнява 10–15 ALU инструкции на връх — приемливо за 100 000 полигона при 60 FPS на мобилен GPU.
Процедурни текстури се генерират във Fragment Shader без зареждане от файл. Икономия на видео памет и опростяване на анимация. Пример — шахматна дъска с променлив размер на клетките. Само няколко ALU инструкции — минимално натоварване на 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 инструкции — идеален шейдър за мобилни устройства. u_cells задава броя на клетките на ос. floor и mod — евтини операции, изпълнявани за 1 цикъл на всички мобилни GPU.
Често задавани въпроси
Shader се изпълнява на GPU в SIMT режим — една инструкция за стотици нишки. Обикновена програма на C++ — на CPU в MIMD режим. Шейдърът няма достъп до файловата система, вход/изход и динамична памет. Той получава данни чрез буфери и текстури и връща позиция или цвят.
GLSL — за OpenGL ES и WebGL, крос-платформен. Metal Shading Language — за iOS. HLSL — основа за Unity и Unreal Engine. Започнете с GLSL — разбирането на основите се пренася на всеки език за шейдъри.
Основни причини: висока точност (highp вместо mediump), прекомерни текстурни проби, динамични разклонения и превишаване на регистровия лимит. Използвайте RenderDoc или Xcode GPU Frame Debugger за анализ на шейдъри.
Compute Shader — шейдър за произволни изчисления на GPU: физика на частици, обработка на изображения, симулация на тъкани. За разлика от Fragment Shader, не е обвързан с пиксели и може да записва в произволни буфери.
Да, чрез визуални редактори: Shader Graph в Unity, Material Editor в Unreal Engine или Metal Shader Converter. Те генерират шейдърен код на базата на нодова схема. За задълбочена оптимизация ще е необходимо разбиране на езика за шейдъри.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също