Uniform-переменные
В предыдущей главе мы увидели каким образом GPU управляет большим количеством параллельных потоков, каждый из которых отвечает за назначение цвета небольшой части изображения. Каждый из параллельных потоков не знает о состоянии остальных, но всё же нам бывает нужно передавать входные данные от CPU ко всем потокам одновременно. Из-за особенностей архитектуры графических карт, все такие входные данные будут одинаковыми для всех потоков (однородными, uniform) и доступными только для чтения. Другими словами, каждый поток принимает на вход одни и те же данные, которые он может прочитать и не может перезаписать.
Эти входы называются однородными (uniform) и могут иметь практически любой из поддерживаемых типов: float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D и samplerCube. Uniform-переменные с указанием своих типов объявляются вначале шейдера сразу после указания точности по умолчанию.
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution; // Размер изображения (ширина, высота)
uniform vec2 u_mouse; // Положение курсора мыши в пикселях
uniform float u_time; // Время в секундах с момента загрузки
Uniform-переменные можно представлять себе как маленькие мостики между CPU и GPU. Имена могут изменяться от примера к примеру, но в этой серии примеров я всегда передаю следующее: u_time (время в секундах с момента запуска шейдера), u_resolution (размер изображения) и u_mouse (положение мыши на изображении, выраженное в пикселях). Я буду писать u_ вначале имён uniform-переменных, чтобы их происхождение было обозначено явно, но на практике вы столкнётесь с самыми разными именами uniform-переменных. Например, ShaderToy.com объявляет эти же переменные со следующими именами:
uniform vec3 iResolution; // разрешение области изображения (в пикселях)
uniform vec4 iMouse; // координаты мыши в пикселях. xy - текущие, zw - клик
uniform float iGlobalTime; // время работы шейдера (секунды)
Хватит слов, давайте посмотрим на юниформы в действии. В следующем коде мы используем u_time (количество секунд с момента запуска шейдера) в комбинации с функцией синуса чтобы анимировать изменение количества красного цвета на экране.
Как видите, GLSL содержит ещё много сюрпризов. GPU аппаратно ускоряет угловые, тригонометрические и экспоненциальные функции. Вот некоторые из них: sin(), cos(), tan(), asin(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), sign(), floor(), ceil(), fract(), mod(), min(), max() и clamp().
Настало время поиграть с кодом выше.
-
Уменьшите частоту так, чтобы изменение цвета стало почти незаметным.
-
Увеличивайте её до тех пор, пока не увидите сплошной цвет без мерцания.
- Поиграйтесь с каналами RGB на различных частотах, чтобы добиться какого-нибудь интересного поведения.
gl_FragCoord
Подобно тому, как GLSL объявляет выходное значение vec4 gl_FragColor по умолчанию, он так же предоставляет вход vec4 gl_FragCoord, содержащий координаты пикселя или фрагмента экрана, над которым работает данный поток. С помощью vec4 gl_FragCoord мы можем узнать где именно поток работает внутри изображения. В данном случае мы не называем входное значение однородным, потому что оно меняется от потока к потоку и называется изменяющимся (varying).
В коде выше мы нормализуем координаты фрагмента, разделяя их на разрешение изображения. В результате значения переходят в диапазон между 0.0 и 1.0, что упрощает отображение значений X и Y на красный и зелёный каналы.
В мире шейдеров у нас нет нормальных отладочных инструментов, поэтому приходится задавать переменным яркие цвета и пытаться извлечь из них смысл. Вы увидите, что иногда программирование на GLSL похоже на засовывание корабля в бутылку. Оно столь же сложно, сколь красиво и захватывающе.

Настало время проверить наше понимание приведённого выше кода.
-
Укажите где находятся координаты
(0.0, 0.0)на изображении. -
Как насчёт
(1.0, 0.0),(0.0, 1.0),(0.5, 0.5)и(1.0, 1.0)? -
Догадайтесь как использовать
u_mouse, зная, что координаты даны в пикселях и НЕ нормализованы. Можете ли вы изменять цвета с помощью этой переменной? - Придумайте какой-нибудь интересный способ изменения цветов с помощью
u_timeиu_mouse.
После выполнения этих упражнения у вас скорее всего возникнет вопрос: где ещё можно применить мощь шейдеров? В следующей главе вы научитесь создавать шейдерные инструменты на three.js, Processing, и openFrameworks.