GLSL: свой шейдер
Одна формула на видеокарте вместо десятка узлов. Заготовка шейдера с TDOutputSwizzle, входы, встроенные uniform, свои параметры и что делать с ошибкой компиляции.
GLSL TOP выполняет ваш код на видеокарте для каждого пикселя. Одна формула дешевле десятка узлов с той же математикой, а ещё так делают то, чего в готовых операторах нет.
Заготовка
Код хранится в Text DAT, на который указывает Pixel Shader, и при создании
узла такой DAT с заготовкой появляется рядом. Основной язык — GLSL 4.60;
3.30 и более ранние версии убрали с переходом на Vulkan, но написанное под
3.30 на новой версии обычно работает.
layout(location = 0) out vec4 fragColor;
void main()
{
vec2 uv = vUV.st; // координата пикселя 0..1
float d = length(uv - 0.5); // расстояние от центра
float c = smoothstep(0.5, 0.0, d);
fragColor = TDOutputSwizzle(vec4(c, c, c, 1.0));
}
vUV.st — координата пикселя, fragColor — выход, который вы объявили
сами. Каждый цвет Derivative велит пропускать через TDOutputSwizzle():
он раскладывает каналы под формат текстуры, и шейдер одинаково работает
на Windows и macOS.
Входы приходят массивами по типу текстуры, 2D — как sTD2DInputs[0],
sTD2DInputs[1]:
vec4 src = texture(sTD2DInputs[0], vUV.st);
vec4 mask = texture(sTD2DInputs[1], vUV.st);
fragColor = TDOutputSwizzle(src * mask.r);
Номер считается среди входов того же типа: если вторым проводом подключён
3D-TOP, следующий 2D-вход всё равно sTD2DInputs[1].
Что уже объявлено
| Переменная | Что это |
|---|---|
uTDOutputInfo |
Разрешение выхода: uTDOutputInfo.res |
uTD2DInfos[i] |
Разрешение каждого 2D-входа |
uTDPass |
Номер прохода, когда на Common задано несколько Passes |
sTDNoiseMap |
Текстура 256 × 256 со случайными значениями |
TD_NUM_2D_INPUTS |
Сколько 2D-входов подключено |
Своё разрешение руками передавать не надо. А вот времени среди встроенных
нет: заводят uniform uTime с выражением absTime.seconds.
Свои параметры
На страницах Vectors, Colors, Arrays, Matrices и Constants
объявляют то, что приходит из TouchDesigner, — имя и значение, в том числе
выражением или экспортом из CHOP. Цвета со
страницы Colors переводятся в рабочее цветовое пространство проекта.
В коде uniform объявляют с тем же именем и типом:
uniform float uSpeed;
uniform vec3 uColor;
layout(location = 0) out vec4 fragColor;
void main() {
float t = uSpeed * 0.1;
fragColor = TDOutputSwizzle(vec4(uColor * sin(t + vUV.s * 20.0), 1.0));
}
Load Uniform Names заполняет параметры именами из кода; неиспользуемые
uniform компилятор обычно выбрасывает, и их кнопка не найдёт.
Compute и соседи
Mode = Compute Shader превращает GLSL TOP в вычислительный шейдер:
vUV там нет, координаты берут из gl_GlobalInvocationID, а пишут
функцией TDImageStoreOutput() — так с ветки 2025.30000, прежнее
sTDComputeOutputs заменено. GLSL MAT пишет материал для рендера,
а у POP из той же ветки свои GLSL-операторы, и ради
геометрии вычислительные шейдеры теперь пишут куда реже.
Когда стоит
Шейдер окупается, когда одна формула заменяет шесть-десять полноэкранных узлов, когда нужного алгоритма нет в операторах — SDF, raymarching, свои проекции — и когда за один проход надо прочитать несколько текстур. Не окупается, если то же делают три стандартных узла, если писать будет один человек, а поддерживать другой, и если хочется «правильности»: сеть из операторов читается и правится быстрее.
Error Behavior решает, что показать при ошибке: шахматку, чёрное
или предыдущий удачный шейдер — на шоу последнее. Пока шейдер
компилируется, кадр по умолчанию ждёт, а Compile Behavior разрешает
компилировать в фоне, показывая чёрное, шахматку или прежний шейдер.
Текст ошибки — в DAT с информацией рядом с узлом и в Textport, причём
номер строки может не совпасть с вашим: TouchDesigner добавляет к коду
свои объявления.
Шейдеры оттуда переносятся почти дословно: iTime — свой uniform от
absTime.seconds, fragCoord / iResolution.xy — vUV.st, mainImage
становится main, результат уходит через TDOutputSwizzle() в fragColor.
Быстрее способа научиться нет.
Нашли в статье ошибку, устаревшее или непонятное место?