Войти
ПрограммированиеСтатьиГрафика

Введение в GLSL.

Автор:

Введение
Синтаксис
Вершинный шейдер
Фрагментный шейдер
Использование GLSL-шейдеров
Заключение

Введение

Данная статья ни в коей мере не претендует на полноту изложения материала о GLSL и предназначена лишь для ознакомления с GLSL-синтаксисом и GLSL-framework'ом. Вся информация взята из спецификации GLSL v1.051 [1], GL2SDK [2] и спецификаций расширений [3,4,5].

Ну, так что же такое GLSL?

GLSL (OpenGL Shading Language) - glSLang - новый язык высокого уровня для создания фрагментных и вершинных шейдеров. В отличии от HLSL/Cg он создавался в расчете на будущее железо, поэтому теоретически он намного мощнее. В частности, GLSL много взял от RenderMan Shading Language.

Требуемые расширения OpenGL.

GL_ARB_shader_objects                        - основной framework для работы с GLSL;
GL_ARB_vertex_shader, GL_ARB_fragment_shader - поддержка вершинных/фрагментных шейдеров соотв.;
GL_ARB_shading_language_100                  - поддержка самого языка описания шейдеров версии 1.0.0;

Статья состоит из 4-х частей. В 1-й мы рассмотрим основные возможности языка GLSL, напишем шейдеры (2 и 3 части), которые подключим к программе во 4-й части статьи.

Используемые сокращения:
 

vs  - vertex shader;
fs  - fragment shader;
tiu - texture image unit;
tc  - texture coordinates;

Синтаксис

Так что же представляет из себя GLSL-шейдер? Он очень похож на программу на C, однако, из-за его специфической направленности, язык расширен за счет векторной и матричной арифметики, дополнительных типов и функций. Присутствует и препроцессор.

На самом деле, GLSL - это шейдерный язык общего направления, а его привязка к OpenGL делается через ключевые слова, начинающиеся с "gl_". Более того, все слова, начинающиеся с "gl_" считаются зарезервированными для будущего использования.

Объявление переменных аналогично С, т.е. могут объявляться в любом месте программы, и их видимость зависит от области, где они были объявлены.

Кроме этого, в GLSL введены специальные типы переменных для связи шейдера с внешним миром (uniform), фрагментного шейдера с вершинным шейдером (varying) и переменные-атрибуты вершина (attribute). Такие переменные должны иметь глобальную область видимости.

В GLSL есть циклы - for, while, do..while; операторы перехода -  continue, break, return, discard (запрещение вывода фрагмента, только в fs). Синтаксис аналогичен C.

Объявление обычных (не специальных) переменных происходит следующим образом:

anyType variableName;
где anyType:
float - тип числа с плавающей точкой одинарной точности;
int - целочисленный тип;
bool - логический тип (принимает значения true, false);
vec2, vec3, vec4 - float-вектор (размерность 2, 3, 4 соотв.);
ivec2, ivec3, ivec4 - int-вектор;
bvec2, bvec3, bvec4 - bool-вектор;
mat2, mat3, mat4 - float-матрица (размерность 2x2, 3x3, 4x4 соотв.);

Кроме того, anyType можно использовать и как функцию преобразования типов (конструктор):

  float f = 0.9;
  vec4 v4 = vec4(f, 0.5, f, 1); // v4 = [0.9, 0.5, 0.9, 1]
  vec3 v3 = vec3(v4); // v3 = [0.9, 0.5, 0.9]

Также типом переменной может быть структура:

struct structName { ... } variableName1, variableName2, ... ;

Эта декларация объявляет новый тип structName, которым можно пользоваться так же, как и встроенными типами.

Можно задавать константы и функции:

  const float ex1 = 1.5;
  const vec4 ex2 = vec4(1, 0, 0, 0);
  const vec4 ex2 = vec4(1, 2, vec2(0, 1));

Параметры функции объявляются как

[const]convention paramName
где convention:
in - создается локальная копия параметра для чтения-записи (выбрано по умолчанию), если присутствует const, то запись запрещена;
inout - локальная копия не создается, чтение/запись происходит с внешней переменной (как по ссылке);
out - так же, как и inout, только чтение параметра запрещено;

  vec4 myFunc1(const in vec4 v1) { ... };
  void myFunc2(in vec4 v1, out float f1) { ... };

Uniform-переменные можно объявлять и в vs, и в fs. При объявлении uniform'ов с одинаковыми именами в паре vs/fs они считаются общими для пары vs/fs.

Типами uniform-переменных могут быть anyType или samplerType (samplerType может использоваться только во фрагментном шейдере):

uniform (type | samperType) uniformName;
где samplerType:
sampler1D, sampler2D, sampler3D, samplerCube, sampler1DShadow, sampler2DShadow - сэмплеры, используются для чтения из текстуры;

Для чтения из текстуры существует несколько функций:
 
texture[123]D[Proj, Lod, ProjLod] - чтение из текстуры. при постфиксе Lod нужно указывать mip-уровень, при Proj tc делятся на их последнюю компоненту;
textureCube[Lod] - чтение из cubemap-текстуры;
shadow[12]D - чтение из shadowmap-текстуры, сравнение происходит автоматически при чтении;

Например:

 
  vec4 t0 = texture2D(uniform sampler2D mySampler2D, gl_TexCoords[0]);
  vec4 t1 = textureCube(uniform samplerCube mySamplerCUBE, gl_TexCoords[1]);

Писать в varying-переменную можно только в vs. Когда читаем varying в fs, выдается интерполированное между вершинами значение. Типом varying-переменной может быть только float-тип: число, вектор, матрица или массив.

Например:

  varying vec2 my_vec2;
  varying float my_f4[4];

Attribute-переменные существуют лишь у вершинного шейдера, это аналоги ATTRIB-переменных в ARB_vertex_program, однако, есть и различие - если в ARBvp мы жестко задаем номер generic-атрибута, по которому будут располагаться данные, то в GLSL компилятор сам решает, какие generic-атрибуты для чего использовать. Типом attribute-переменной могут быть только float, vec2, vec3, vec4, mat2, mat3 и mat4. Массивы и структуры не допускаются.

Например:

  attribute vec4 my_vec4;
  attribute mat4 my_mat4;

Кроме того, в шейдерах присутствует большое кол-во встроенных атрибутов самых разных типов, которые представляют собой текущий OpenGL state (например, gl_Color).

В GLSL существует множество операторов: алгебраические, дереференсирование массива, структуры, битовые, логические... все они разбиты по приоритетам, подробнее смотрите в спецификации [1].

Большинство алгебраических операторов выполняются покомпонентно (component-wise), но есть и исключения - умножение вектора на матрицу, матрицу на вектор и матрицы на матрицу выполняются как в линейной алгебре.

Существуют также покомпонентные функции сравнения векторов (lessThan, lessThanEqual, notEqual, и т.п.), они, в отличие от стандартных операций сравнения <, <=, >=, >, ==, !=, которые возвращают boolean-значение, возвращают boolean-вектор с размерностью, равной размерности аргументов.

Например:

  vec4 x = vec4(1, 2, 3, 4);   
  vec4 y = vec4(4, 3, 2, 1);   
  bvec4 res = greaterThan(x, y) // теперь res = {false, false, true, true}

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

В GLSL есть даже встроенная функция шума –

noise[1234](genType x),
где genType - float, vec2, vec3, vec4; x выступает в роли seed'а. Эта функция возвращает значение в [-1,1] со средним значением в 0, однако, GLSL не гарантирует полной идентичности результатов этой функции у разных вендоров.

Ну, теперь мы знаем более чем достаточно, чтобы написать простой вершинный и фрагментный шейдеры ;)

Страницы: 1 2 3 4 Следующая »

#GLSL, #шейдеры

2 января 2004 (Обновление: 17 июня 2010)

Комментарии [39]