Урок 9. Шейдеры в OpenGL4

20.08.2012

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

Шейдеры

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

Сейчас шейдеры являются ключевым аспектом OpenGL приложения. Фактически, трудно что-нибудь сделать без их использования. Вся графика игр, многочисленные спецэффекты, и тот реализм, который на сегодняшний день достигла графическая часть игр – это всё получилось благодаря использованию шейдеров. Взглянем лишь на некоторые примеры использования шейдеров:

  • Обычное применение: матрицы камеры и освещениями в вершинномшейдере, текстуры и определение текущего цвета пикселя в пиксельном шейдере
  • Композиции из нескольких текстур, использования глубоких текстур, для тонкой детализации ландшафта и моделей
  • Эффекты освещения и теней
  • Системы частиц – туман, задымление а также различные виды вспышек

На самом деле, данный список можно продолжать, кажется бесконечно. Существует слишком много примеров применения шейдеров, чтобы даже их просто перечислить. Запустите любую новую игру и существующий список применения шейдеров может быть пополнен несколькими, а то и десятками новых применений шейдеров.

Особенности компиляции и загрузки шейдера

Хотя шейдер является очень низкоуровневой структурой – он должен выполняться очень быстро, но вы можете его писать на языке, подобном C++, этот язык называется GLSL. Это потому, что шейдер на ходу компилируется в машинный код видеоускорителя. Кроме того, не нужен специальный компилятор. Язык GLSL является стандартным и его компиляция осуществляется на ходу любой 3d системой. Таким образом, вы просто указываете на исходный файл формата .fx и он автоматически загружается, компилируется и выполняется непосредственно в вашем 3d приложении.

Язык GLSL

Взглянем на пример шейдера с точки зрения программирования. Посмотрим на типичный код шейдера:

//------------------------------------------------------------------------------------
// Constant Buffer Variables
//------------------------------------------------------------------------------------
#version 330
 
uniform mat4 mWorld;
uniform mat4 mView;
uniform mat4 mProjection;
 
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in vec2 inTex;
 
out vec3 theColor;
out vec2 texCoord;
 
//------------------------------------------------------------------------------------
// Vertex Shader
//------------------------------------------------------------------------------------
void main()
{
        gl_Position = mProjection*mView*mWorld*vec4(inPosition, 1.0);
        theColor = vec3(0.25,0.5,1);
        texCoord = inTex;
} 
 
//------------------------------------------------------------------------------------
// Pixel Shader Variables
//------------------------------------------------------------------------------------
#version 330
 
in vec3 theColor;
in vec2 texCoord;
out vec4 outputColor;
 
uniform sampler2D gSampler;
 
//------------------------------------------------------------------------------------
// Pixel Shader
//------------------------------------------------------------------------------------
 
void main()
{
    outputColor = texture2D(gSampler, texCoord)* vec4(theColor,1);
}

Вы видите, что весь код шейдера может быть разбит на четыре ключевых раздела. Это: объявления глобальных переменных – констант шейдера для вершинного шейдера, код функции вершинного шейдера, объявления констант для пиксельного шейдера и, наконец код функции пиксельного шейдера. В дальнейших разделах мы поэтапно рассмотрим структуру языка GLSL. Обратите внимание, что в OpenGL принято помещать вершинный шейдер с константами в один файл, а пиксельный шейдер с его переменными – в другой.

Типы данных GLSL

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

Тип данных Примечание
Array Массив
Scalar Одно компонентная скалярная величина
Vector,Matrix Несколько компонентный вектор или матрица
Sampler Встроенный тип данных – сэмплер
Struct, User Defined Пользовательский тип данных

Рассмотрим эти типы данных. Тип данных Scalar определяет скалярную величину и может быть одним из следующих:

bool
int
uint
float

Тип данных Vectorобозначается добавлением количества компонент к типу данных scalar. Например vec4 определяет четырехкомпонентный вектор. Матрицы Matrixв OpenGL обозначаются mat4. Пример определения вектора и матрицы:

vec3 fVector = { 0.2f, 0.3f, 0.4f };
mat2 fMatrix = { 0.0f, 0.1, 
                 2.1f, 2.2f  };

Типы Sampler используется для указания слота источника текстуры. Пример использования типа данных Sampler:

uniform sampler2D gSampler;
...
outputColor = texture2D(gSampler, texCoord)* vec4(theColor,1);
...

Операции ветвления кода в GLSL

В большинстве случаев код GLSL выполняется построчно, то есть одна строка, или инструкция потом следующая и так далее. Но в некоторых случаях, можно добавлять циклы и условные переходы. Обратите внимание, что так как GLSL должен работать быстро, то ветвления в коде GLSL должны быть скорее исключением, чем практикой.

Операция ветвления Примечание
break Операция прерывания цикла
continue Продолжение цикла
discard Специфическое ветвление для пиксельногошейдера
do Ветвление
for Цикл
if Условный переход
switch Операция выбора
while Ветвление

Операции ветвления в GLSLимеют тот же синтаксис, что и в языке C++.

Определения функций и блоки в GLSL

Остальная структура языка может быть сведена к следующей таблице:

Элемент Примечание
Функции Определение функций
Операции ветвления См. предыдущий раздел
Блоки Блок определяется символами {}
Выражение return Операция возвращения из блока функции
Выражения Любое математическое выражение а также оператор присваивания.
Операторы Пример символов операторов: +,-,*,/,=,+=,++,<<,>>,==,!=,>=,!
Встроенные функции Intrinsic Functions

Определение функций имеет некоторые характерные черты в GLSL. Об этом далее.

Константы шейдера

Для того, чтобы иметь возможность изменять определенные глобальные переменные шейдера существуют константы шейдера. В GLSL все константы объявляются ключевым символом uniform, предшествующим объявлению глобальной переменной шейдера.

uniform mat4 mWorld;
uniform mat4 mView;
uniform mat4 mProjection;

Вы можете догадаться, что константы могут быть и других типов, не только типа mat4, как во фрагменте кода, приведенном выше.

Типы данных для функций вершинного и пиксельного шейдера

Для вершинного и пиксельного шейдера существуют определенные типы данных, присущие им в качестве входящих данных и выходящих функций. Итак, вершинныйшейдер принимает в себя входящие данные, которые должны полностью соответствовать формату вершинного буфера. Пример формата входящих данных для вершинного шейдера:

layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in vec2 inTex;

Данный фрагмент кода назначает входящими дынными для вершинного шейдера координату, нормаль и текстурную координату. Входящий формат данных вершинного шейдера может быть различным и отличаться от этого примера. Выходящими данные вершинный шейдер возвращает операцией присвоения переменной, имеющей тип out соответствующего значения. Перед тем как вершинныйшейдер что-то сможет возвратить, переменные out должны быть обявлены

out vec3 theColor;
out vec2 texCoord;

Выходящие данные вершинногошейдера являются входящими данными пиксельного шейдера и имеют идентичный формат:

in vec3 theColor;
in vec2 texCoord;

Пиксельный шейдер может возвратить единственную переменную формата vec4, определяющую цвет точки.

out vec4 outputColor;
...
 
outputColor = texture2D(gSampler, texCoord)* vec4(theColor,1);
...

Встроенные функции GLSL

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

abs
sin
cos
dot
cross
fmod
log
mul
max
min
round
sqrt
tan
texture1d
texture2d
textureCUBE

В этом отрывке кода из некоторого вершинного шейдера

vec3  lightVec             = normalize(LightPosition - ecPosition);
float diffuse              = max(dot(lightVec, tnorm), 0.0);

легко найти места, в которых вызывается несколько встроенных функций OpenGL, таких как: операция нормализации вектора, определение максимума и операция скалярного умножения векторов.

Заключение

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

Заполнен: OpenGL4
Присвоен тэг:

avatar

Об Авторе ()

Планета назначения, JO - 8703 - IV, обозначается светлой точкой на объемном экране корабля. К тому времени, как Робоид нанялся на юпитерианский круговой рейс, ему следовало бы стать главным инженером, но после Суховодного пионерского его вышибли, внеся в черный список, и высадили в Луна-Сити за то, что вместо слежения за приборами он провел время, программируя синтезатор пищи корабля строжайше запрещенной корабельными инструкциями последовательностью синтеза пива.

Комментирование закрыто.

Наверх