try).
Определение языка1
В этой главе представлены основные возможности языка шейдеров OpenGL. Сначала будет приведен небольшой пример работы в паре вершинного и фрагмент-ного шейдеров, демонстрирующий их структуру и интерфейсы. Каждая особенность языка будет обсуждаться отдельно.
Синтаксис языка шейдеров OpenGL заимствован у семейства языков программирования С. Лексемы, идентификаторы, точки с запятыми, обозначение вложения блоков фигурными скобками, структурная схема и многие ключевые слова выглядят так же, как в языке С. Доступны оба вида комментариев,.// …и/* … */.Но многие особенности отличаются от используемых в С, и в этой главе все серьезные отличия будут описаны.
Каждый пример шейдера представлен в виде, в котором он может появиться в файле или на экране. Но на самом деле шейдеры передаются в OpenGL в виде етрок, а не файлов.
3.1. Примеры шейдеров
Обычно программа содержит два шейдера: вершинный и фрагментный. На самом деле шейдеров каждого типа может быть больше, но для всех шейдеров каждого типа может использоваться только одна функция mai п, поэтому обычно все-таки ограничиваются одним шейдером каждого типа.
Далее приведена простая пара вершинного и фрагментного шейдеров, которая может равномерно выразить температуру цветом. Амплитуда температурных значений и их цветов параметризована. Первый фрагмент относится к вершинному шейдеру. Он выполняется один раз для каждой вершины:
// uniform-переменные, изменяемые не чаще, чем при смене примитива uniform float CoolestTemp: uniform float TempRange;
// attribute-переменные, обычно меняются для каждой вершины attribute float VertexTemp;
// varying-переменные передают значения // из вершинного шейдера фрагментному varying float Temperature:
void mainO {
// вычисляется температура для интерполирования во фрагментах // в диапазоне [0.0. 1.0]
Temperature = (VertexTemp - CoolestTemp) / TempRange:
/*
Размещение вершины, установленное, функцией glVertexO, можно прочесть из встроенной переменной gIVertex. Растеризатору для определения положения вершины нужно знать и это значение, и текущую матрицу модели-вида.
*/
gl_Position = gl ModelViewProjectionMatrlx * g1_Vertex:
}
Это код вершинного шейдера. Затем выполняется сборка примитивов, после которой растеризатор получает достаточно информации для создания фрагментов. Растеризатор интерполирует значения Temperature в вершинах и создает значения для фрагмента. После этого каждый фрагмент передается фрагментному шейдеру;
// uni form-переменные меняются не чаще, чем для каждого примитива // vec3 объявляет вектор из трех чисел с плавающей запятой1 uniform vec3 CoolestColor; uniform vec3 HottestColor:
// Temperature содержит интерполированное для фрагментов значение // температуры, установленное вершинным шейдером varying float Temperature;
void mainO {
// получить цвет из промежутка между самый холодный и самым теплым
// с помощью встроенной функции mix()
vec3 color = mix(CoolestColor. HottestColor, Temperature):
// Создать вектор из 4 чисел с плавающей запятой, добавив значение // прозрачности, разное 1.0. и установить фрагменту этот цвет gl_FragColor = vec4.(color. 1.0);