Настраиваемый конвейер OpenGL
Сдам Сам

ПОЛЕЗНОЕ


КАТЕГОРИИ







Настраиваемый конвейер OpenGL





Глава 1 вкратце описала процесс конвейера визуализации OpenGL. В это главе мы более подробно опишем разные стадии и что делает каждая команда. Ковейер визуализации версии 4.3 содержит 4 стадии обработки данных плюс стадию вычисления, каждую из которых вы выполняете с помощью шейдера.

1. Вершинный шейдер. Стадия, на которой вершинные данные, указанные вами в вершинном объекте буфера, обрабатывают каждую вершину в отдельности. Эта стадия обязательна для всех программ на OpenGL, и этот шейдер должен быть привязан к программе обязательно. Применение вершинного шейдера мы пишем в Главе 3, “Рисование в OpenGL”.

2. Мозаичный шейдер. Это необязательная стадия, которая создает дополнительные геометрические данные внутри конвейера OpenGL, а не оставляет на программу определение каждого отдельного геометрического примитива. Если задействована, эта фаза принимает данные вывода со стадии вершинного шейдера и обрабатывает вершины дальше. Мы опишем стадию мозаичного шейдера в Главе 9, “Мозаичные шейдеры”.

3. Геометрический шейдер. Еще одна необязательная стадия, которая может модифицировать все имеющиеся геометрические данные конвейера OpenGL. Эта стадия работает с геометрическими примитивами по отдельности, позволяя модифицировать их. На этой стадии доступно создание большего количества геометрических данных, основанных на имеющихся примитивах, также возможно изменение типа примитивов (напр., конвертирование треугольников в линии), или даже сброс всей геометрии. Если задействована, эта фаза получает на обработку данные или после того, как вершинный шейдер закончил создание геометрических примитивов и вершин, или после обработки этих данных мозаичным шейдером, если тот был также задействован.



4. И наконец, последняя стадия обработки в конвейере OpenGL - это фрагментарный шейдер. Эта стадия обрабатывает отдельные фрагменты (или образцы, если задействована модель шейдера образцов), которые генерировал растеризатор OpenGL. Для нее тоже необходима привязка шейдера. На этой стадии вычисляются цвет и глубина объекта, а потом эти данные перенаправляются на дальнейшую обработку фрагментарным тестированием и смешением на конвейре. О фрагментарной стадии шейдинга будет говориться много раз по ходу книги.

5. Вычислительный шейдер. Сам по себе этот шейдер не является частью конвейера, а скорее представляет отдельностоящую программу. Вычислительный шейдер обрабатывает данные общих рабочих элементов, внутри заданного программой диапазона, а не из графическихданных, как вершины и фрагменты. Вычислительные шейдеры могут обрабатывать буферы созданные и занятые другими процессами вашего приложения. Туда входят кадровый буфер и буфер пост-обработки, но по сути, всё, что вам угодно. О вычислительных шейдерах читайте Главу 12, “Вычислительные шейдеры”.

 

Важно понимать, как в целом данные переходят с одной стадии обработки на другую. Шейдеры, как вы могли понять из Главы 1, похожи на запрос процедуры: данные поступают в него, обрабатываются, и идут на выход. В “С”, например, это делается или с помощью глобальных переменных, или независимой переменной к функции. GLSL немного отличается в этом. Каждый шейдер похож законченную программу на языке “С” тем, что начинается с процедуры main(). Однако в отличае от программы на “С”, main() на GLSL не принимает независимые переменные, а вместо этого обрабатывает данные, поступающие из и в шейдеры, в виде глобальных переменных (не спутайте их с глобальными переменными приложения, т.к. глобальные переменные шейдеров имеют совершенно отдельные значения от тех, что вы вписываете в код самой программы). Например, взглянем на Пример 2.1.

Пример 2.1A Simple Vertex Shader

#version330 core

in vec4vPosition;

in vec4vColor;

out vec4color;

uniform mat4ModelViewProjectionMatrix;

Void

main()

{

color = vColor;

gl_Position = ModelViewProjectionMatrix * vPosition;

}

 

Хотя это очень компактный шейдер, здесь есть, что запомнить. Вне зависимости от того, для какой стадии шейдинга вы пишите, все шейдеры будут иметь примерно одинаковую структуру с этим примером. В том числе даже заявка версии в заголовке с #version.

Для начала, обратите внимание на глобальные переменные. Это данные ввода и вывода для шейдера OpenGL. Помимо каждой переменной, данные с заданным типом (напр., vec4, о котором скоро) копируются в шейдер из OpenGL с помощью in-переменных, и точно также копируются из шейдра с помощью out-переменных. Данные в этих переменных обновляются каждый раз при исполнении шейдера (напр., если OpenGL обрабатывает вершины, то новые данные пропускаются через эти переменные для каждой вершины; при работе с фрагментами - для каждого фрагмента). Еще одна категория переменных, которые возможно получить из приложения OpenGL, это унифицированные переменные. Унифицированные (unifrom) переменные не сменяются от вершины к вершине или фрагменту, а сохраняют значение для всех геометрических примитивов, пока приложение не обновит их.

Обзор OpenGL Shading Language

В этом разделе вы найдете обзорную информацию по языку программирования, на котором написаны шейдеры OpenGL. У GLSL много общего с С++ и Java, и его используют для написания шейдеров всех стадий обработки, поддерживаемых в OpenGL, хотя некоторые функции доступны только определенным шейдерам. Для начала мы опишем требования GLSL, его типы и другие элементы языка, общие для всех стадий разных шейдеров. Затем поговорим об функциях уникальных для каждого шейдера.

 

Создание шейдера при помощи GLSL

Точка отправления

Как и программы языка “С”, программа GLSL начинает исполнения с формы main(). Все программы GLSL начинаются со следующего:

#version330 core

Void

main()

{

// Your code goes here

}

 

“//” обозначение является комментарием и заканчивается в конце данной строки, совсем как в “С”. Кроме того, мульти-линейные комментарии / * и */ языка “С” также поддерживаются. Однако, в отличие от ANSI “С”, main() не возвращает интегральные данные, они обнуляются. Кроме того, как в “С” и языках основанных на нём, операторы оканчиваются точкой с запятой. И хотя это полностью скомпилированная и рабочая программа GLSL, её функциональность оставляет желать лучшего. Чтобы сделать наши шейдеры интереснее для работы, мы опишем переменные и их свойства.

 

Задача переменных

GLSL это типизированный язык: каждая переменная должна быть прописана и протипированна в соответствующий тип. Наименование переменных подчиняется тем же правилам, что и в “С”: можно использовать буквы, числа, нижнее подчеркивание (_) для задания наименования переменной. Однако, ни цифра, ни нижнее подчеркивание не могут быть в начале имени. Также нельзя использовать подряд два подчеркивания - такие имена не принимаются в GLSL.

Таблица 2.1 показывает основные типы, допустимы в GLSL

Table 2.1Basic Data Types in GLSL

Наименование Описание
float IEEE 32-бит. значение с плавающей запятой
double IEEE 64-бит. значение с плавающей запятой
int "> signed two’s-complement 32-bit integer value
uint неподписанное 32-бит. интегральное значение
bool булево значение

 

Эти типы (и позже составные типы из этих) все прозрачны. То есть, их внутренняя форма обнаруживается шейдером, и код шейдера выбирает, как их отображать внутренне.

Дополнительные типы, непрозрачные, не обнаруживают внутреннюю форму. Сюда входят типы сэмлера, изображений и ядерного счетчика(????????). Они задают переменные для создания маркеров непрозрачности для доступа к картам текстур, изображениям, и ядерным счетчикам, как будет рассказано в Главе 4, “Цвет, Пиксели и Кадровые буферы”.

Разные типы сэмплеров и их применение описаны в Главе 6, “Текстуры”.

 

Охват переменных

Все переменные, которые нужно указать, можно указать до их непосредственного применения (точно как в “С”, они должны стоять первыми в блоке кода). Правила охвата в GLSL очень схожи с правилами “С” и состоят вот в чем:

● Переменные, заявленные за пределами функции, являются общеприменимыми и видимы всем следующим функциям шейдера;

● Пременные, заявленные внутри фигурных скобок (напр., определение функции, блок, следующий за циклом или аргументом “if” и т.д.), существуют только внутри этих скобок.

● Шаг цикла, как i в цикле

for (int i = 0; i < 10; ++i) {

// loop body

}

действителен только для этого цикла;

 

Инициализация переменных

Переменные также могут вступать в силу по заявлении. Например:

inti, numParticles = 1500;

floatforce, g = 9.8;

boolfalling = true;

doublepi = 3.1415926535897932384626LF;

 

Прямые интегральные переменные могут быть в восьмеричной, десятичной или шестнадцатеричной системе. Возможно использования отрицательного знака перед цифрой, чтобы игнорировать её в коде, или приписание u или U денотации неподписанного интегрального значения.

Прямые значения с плавающей запятой должны иметь десятиричный вид, если только не описываются научным языком, напр., 3E-7. (Однако, бывают случаи, когда прямые интегралы будут неявно конвертированы в значения с плавающей запятой.) Дополнительно, переменные могут иметь рефикс f или F (на языке “С”)для обозначения прямого значения “float”. Необходимо приписка суффикса "lF” или “LF”, чтобы задать прямому значению точность “double”.

Булевы значения могут иметь приписку или “true” или “false”, и могут быть инициализированы любым из этих значений, или быть результатом операции, вычисление которой представляется булевым (логическим) числом.

 

Составляющие

Как говорилось ранее, GLSL более строго типизирован, чем С++, и в нем меньше неявных конвертаций между значениями. Например, результатом

int f = false;

будет ошибка компиляции, так как значением интеграла не может быть логическое число. Типы будут неявно конвертированы как показано в таблице.

Table 2.2Неявные конвертации в GLSL

Нужный тип: Может неявно конвертироваться в:

Uint int

float int, uint

double int, uint, float

 

Вышеприведенные типы конвертаций действительны для скаляров, векторов и матриц данных типов. Конвертации никогда не изменяют матрицы на векторы и т.д., или количество их компонентов. Конвертации также не применяются к массивам и структурам.

Все другие конвертации значений требуют явной конвертации с использованием заданного конструктора конвертации. Конструктор, как и в других языках C++, это функция с тем же наименованием, что и тип, которая возвращает значение этого типа. Например,

floatf = 10.0;

intten = int(f);

использует конструктор конвертации int для применения конвертации. Похожим образом у других типов данных есть свои конструкторы конвертации: float, double, uint, bool, а также векторы и матрицы этих типов. Каждый из них принимает несколько других типов для явной конвертации. Эти функции также демонстрируют другую особенность GLSL: функцию перегрузки, когда каждая функция принимает значения разного типа, но все имеют одинаковое наименование. Дальше по тексту мы еще обсудим функции подробнее.

 

Сложные типы

Базовые типы данных GLSL могут быть объединены, чтобы лучше соответствовать основным типам данных OpenGL и облегчить задачу вычислений.

Сначала GLSL поддерживает двух-, трех- и четырех-компонентные векторы для каждого базового типа данных (bool, int, uint, float и double). Также доступны матрицы float и double-типа. В Таблице 2.3 перечислены разрешенные типы векторов и матриц.

Таблица 2.3GLSL Vector and Matrix Types









Не нашли то, что искали? Воспользуйтесь поиском гугл на сайте:


©2015- 2018 zdamsam.ru Размещенные материалы защищены законодательством РФ.