Фрагментный шейдер
Фрагментный шейдер — последняя творческая стадия: он решает, какого цвета будет каждый пиксель на экране.
Фрагментный шейдер (он же пиксельный) запускается для каждого фрагмента, покрытого треугольником, и вычисляет его итоговый цвет RGBA.
Зачем это знать
Всё визуальное богатство — освещение, текстуры, отражения, туман, стилизация — живёт здесь. Это самая нагруженная стадия (тысячи запусков на треугольник), поэтому каждый лишний расчёт во фрагментном шейдере умножается на миллионы пикселей.
Вход и выход
| Вход | Выход |
| интерполированные varying (UV, нормаль, цвет) | цвет фрагмента (vec4 RGBA) |
| uniform: текстуры, свет, время | (опц.) запись глубины |
Минимальный фрагментный шейдер
// Сплошной оранжевый цвет
precision mediump float;
void main() {
gl_FragColor = vec4(1.0, 0.5, 0.0, 1.0); // R,G,B,A
}
Чуть интереснее — раскрасить по интерполированному UV, превратив координаты в цвет:
precision mediump float;
varying vec2 vUV; // пришло из вершинного шейдера, проинтерполировано
void main() {
gl_FragColor = vec4(vUV.x, vUV.y, 0.0, 1.0);
}
Смешивание цветов — модель
Частая операция во фрагментном шейдере — линейная интерполяция (mix) между двумя цветами по параметру t от 0 до 1. Смоделируем переход от синего к красному.
def mix(a, b, t):
return tuple(round(x + (y - x) * t, 2) for x, y in zip(a, b))
blue = (0.0, 0.0, 1.0)
red = (1.0, 0.0, 0.0)
for t in [0.0, 0.25, 0.5, 0.75, 1.0]:
print(f"t={t}: {mix(blue, red, t)}")
Вывод:
t=0.0: (0.0, 0.0, 1.0) t=0.25: (0.25, 0.0, 0.75) t=0.5: (0.5, 0.0, 0.5) t=0.75: (0.75, 0.0, 0.25) t=1.0: (1.0, 0.0, 0.0)
При t=0 чистый синий, при t=1 чистый красный, в середине — фиолетовый. В GLSL это встроенная функция mix(a, b, t) — основа градиентов и плавных переходов.
Как работает под капотом
Фрагмент — это ещё не пиксель: после фрагментного шейдера он проходит тест глубины и блендинг, и только пройдя их, попадает в фреймбуфер как пиксель. Несколько фрагментов могут претендовать на один пиксель (перекрывающиеся треугольники) — кто «ближе», решит z-buffer. Современные GPU также делают early-Z: отбрасывают заведомо закрытые фрагменты ещё до запуска шейдера, экономя работу.
Частые ошибки
- Тяжёлые вычисления во фрагментном шейдере — они умножаются на число пикселей.
- Забыть альфа-канал (4-й компонент) — объект может оказаться невидимым или непрозрачным не так, как ждали.
- Считать фрагмент и пиксель одним и тем же — фрагмент ещё может не дойти до экрана.
Итоги
- Фрагментный шейдер вычисляет цвет каждого покрытого фрагмента.
- Вход — интерполированные varying и uniform; выход — vec4 RGBA.
- mix(a,b,t) — основа плавных цветовых переходов.
- Это самая нагруженная стадия; фрагмент ≠ пиксель до тестов глубины и блендинга.