Shader (шейдер) — это программа, которая выполняется на GPU для обработки графических данных: вершин, фрагментов или вычислительных задач. В отличие от обычного кода на CPU, шейдер работает параллельно на сотнях ядер, обрабатывая каждый элемент независимо. По данным Khronos Group (2025), шейдеры используются в 100% современных 3D-приложений — от мобильных игр до UI-эффектов в системных приложениях. Разработчики пишут шейдеры на специализированных языках: GLSL, HLSL, Metal Shading Language или SPIR-V.
Главное
Shader — это небольшая программа, написанная на специализированном языке и выполняемая на GPU. Каждый шейдер обрабатывает один элемент данных — вершину, фрагмент (пиксель) или рабочий элемент — независимо от других. Параллелизм GPU позволяет запускать тысячи экземпляров шейдера одновременно.
Термин ввёл Pixar в 1980-х для описания программ, управляющих внешним видом поверхностей в 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 и programmable blending.
По данным 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 с переменной-униформой) снижают производительность SIMT-конвейера. Заменяйте ветвления на математические функции: mix(), step(), smoothstep() и clamp(). Эти функции выполняются за 1–2 инструкции, тогда как ветвление может стоить 8–16 инструкций из-за дивергенции потоков.
// Bad: dynamic branching in shader
if (u_enableLight) {
color *= computeLighting(normal);
}
// Good: mathematical branch replacement
color *= mix(1.0, computeLighting(normal),
float(u_enableLight));
mix() выполняет линейную интерполяцию между двумя значениями. Когда u_enableLight=0, возвращается 1.0 — цвет не меняется. Когда u_enableLight=1, возвращается результат освещения. Нет ветвления — все потоки выполняют одинаковый код.
Рассмотрим практические примеры шейдеров для мобильной разработки — от простой тонировки до процедурной генерации текстур. Каждый пример демонстрирует конкретный приём.
Модель Фонга — классическая модель освещения с диффузной и зеркальной составляющими. 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-освещение: dot-произведение нормали и направления света. Результат смешивается с 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.
Часто задаваемые вопросы
Шейдер выполняется на 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 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также