- •Что такое OpenGl?
- •Первое знакомство с программой OpenGl
- •Синтаксис OpenGl
- •Конвейер визуализации OpenGl
- •Подготовка данных для отправки в OpenGl
- •Вершинные шейдеры
- •Мозаичные шейдеры
- •Наша первая программа: Подробное обсуждение Ввод main()
- •Запуск OpenGl
- •Распределение объектов буфера вершин
- •Инициализация вершинных и фрагментарных шейдеров
- •Наш первый рендеринг в OpenGl
- •Рисование в OpenGl
- •Включение и выключение команд в OpenGl
- •Шейдеры и OpenGl
- •Настраиваемый конвейер OpenGl
- •Обзор OpenGl Shading Language
- •Создание шейдера при помощи glsl Точка отправления
- •Задача переменных
- •Охват переменных
- •Инициализация переменных
- •Сложные типы
- •Получение доступа к элементам матриц и векторов
- •Структуры
- •Квалификаторы хранилищ
- •Заявления
- •Вычислительная инвариантность
- •Глобальные функции компиляции шейдеров
- •Блоки интерфейса
- •Унифицированные блоки
- •Указание унифицированных блоков в шейдере
- •Доступ к унифицированным блокам из приложения
- •Блоки In/Out
- •Компилирование шейдеров
- •Наша функция LoadShaders()
- •Подпрограммы шейдеров
- •Установка подпрограмм glsl
- •Раздельные объекты шейдеров
Настраиваемый конвейер OpenGl
Глава 1 вкратце описала процесс конвейера визуализации OpenGL. В это главе мы более подробно опишем разные стадии и что делает каждая команда. Ковейер визуализации версии 4.3 содержит 4 стадии обработки данных плюс стадию вычисления, каждую из которых вы выполняете с помощью шейдера.
Вершинный шейдер. Стадия, на которой вершинные данные, указанные вами в вершинном объекте буфера, обрабатывают каждую вершину в отдельности. Эта стадия обязательна для всех программ на OpenGL, и этот шейдер должен быть привязан к программе обязательно. Применение вершинного шейдера мы пишем в Главе 3, “Рисование в OpenGL”.
Мозаичный шейдер. Это необязательная стадия, которая создает дополнительные геометрические данные внутри конвейера OpenGL, а не оставляет на программу определение каждого отдельного геометрического примитива. Если задействована, эта фаза принимает данные вывода со стадии вершинного шейдера и обрабатывает вершины дальше. Мы опишем стадию мозаичного шейдера в Главе 9, “Мозаичные шейдеры”.
Геометрический шейдер. Еще одна необязательная стадия, которая может модифицировать все имеющиеся геометрические данные конвейера OpenGL. Эта стадия работает с геометрическими примитивами по отдельности, позволяя модифицировать их. На этой стадии доступно создание большего количества геометрических данных, основанных на имеющихся примитивах, также возможно изменение типа примитивов (напр., конвертирование треугольников в линии), или даже сброс всей геометрии. Если задействована, эта фаза получает на обработку данные или после того, как вершинный шейдер закончил создание геометрических примитивов и вершин, или после обработки этих данных мозаичным шейдером, если тот был также задействован.
И наконец, последняя стадия обработки в конвейере OpenGL - это фрагментарный шейдер. Эта стадия обрабатывает отдельные фрагменты (или образцы, если задействована модель шейдера образцов), которые генерировал растеризатор OpenGL. Для нее тоже необходима привязка шейдера. На этой стадии вычисляются цвет и глубина объекта, а потом эти данные перенаправляются на дальнейшую обработку фрагментарным тестированием и смешением на конвейре. О фрагментарной стадии шейдинга будет говориться много раз по ходу книги.
Вычислительный шейдер. Сам по себе этот шейдер не является частью конвейера, а скорее представляет отдельностоящую программу. Вычислительный шейдер обрабатывает данные общих рабочих элементов, внутри заданного программой диапазона, а не из графическихданных, как вершины и фрагменты. Вычислительные шейдеры могут обрабатывать буферы созданные и занятые другими процессами вашего приложения. Туда входят кадровый буфер и буфер пост-обработки, но по сути, всё, что вам угодно. О вычислительных шейдерах читайте Главу 12, “Вычислительные шейдеры”.
Важно понимать, как в целом данные переходят с одной стадии обработки на другую. Шейдеры, как вы могли понять из Главы 1, похожи на запрос процедуры: данные поступают в него, обрабатываются, и идут на выход. В “С”, например, это делается или с помощью глобальных переменных, или независимой переменной к функции. GLSL немного отличается в этом. Каждый шейдер похож законченную программу на языке “С” тем, что начинается с процедуры main(). Однако в отличае от программы на “С”, main() на GLSL не принимает независимые переменные, а вместо этого обрабатывает данные, поступающие из и в шейдеры, в виде глобальных переменных (не спутайте их с глобальными переменными приложения, т.к. глобальные переменные шейдеров имеют совершенно отдельные значения от тех, что вы вписываете в код самой программы). Например, взглянем на Пример 2.1.
Пример 2.1 A Simple Vertex Shader
#version 330 core
in vec4 vPosition;
in vec4 vColor;
out vec4 color;
uniform mat4 ModelViewProjectionMatrix;
void
main()
{
color = vColor;
gl_Position = ModelViewProjectionMatrix * vPosition;
}
Хотя это очень компактный шейдер, здесь есть, что запомнить. Вне зависимости от того, для какой стадии шейдинга вы пишите, все шейдеры будут иметь примерно одинаковую структуру с этим примером. В том числе даже заявка версии в заголовке с #version.
Для начала, обратите внимание на глобальные переменные. Это данные ввода и вывода для шейдера OpenGL. Помимо каждой переменной, данные с заданным типом (напр., vec4, о котором скоро) копируются в шейдер из OpenGL с помощью in-переменных, и точно также копируются из шейдра с помощью out-переменных. Данные в этих переменных обновляются каждый раз при исполнении шейдера (напр., если OpenGL обрабатывает вершины, то новые данные пропускаются через эти переменные для каждой вершины; при работе с фрагментами - для каждого фрагмента). Еще одна категория переменных, которые возможно получить из приложения OpenGL, это унифицированные переменные. Унифицированные (unifrom) переменные не сменяются от вершины к вершине или фрагменту, а сохраняют значение для всех геометрических примитивов, пока приложение не обновит их.
