Фрагментный шейдер

Фрагментный шейдер — последняя творческая стадия: он решает, какого цвета будет каждый пиксель на экране.

Фрагментный шейдер (он же пиксельный) запускается для каждого фрагмента, покрытого треугольником, и вычисляет его итоговый цвет 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) — основа плавных цветовых переходов.
  • Это самая нагруженная стадия; фрагмент ≠ пиксель до тестов глубины и блендинга.
Проверьте себя
1. Что вычисляет фрагментный шейдер?
AПозицию вершины
BЦвет каждого фрагмента (пикселя)
CМатрицу проекции
DЧисло кадров
2. Что делает функция mix(a, b, t) в GLSL?
AПеремножает векторы
BЛинейно смешивает a и b по параметру t от 0 до 1
CНормализует вектор
DСчитает глубину
3. Почему фрагментный шейдер критичен для производительности?
AОн работает на CPU
BОн запускается тысячи раз на треугольник, и расчёты умножаются на пиксели
CОн медленнее вершинного по своей природе
DОн хранит всю сцену