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

Введение в GLSL. (4 стр)

Автор:

Использование GLSL-шейдеров

С введением GLSL в OpenGL появилось новое понятие - хэндл (handle), и, соответственно, новый префикс glCreate* к функциям, которые заведуют созданием хэндлов.

Итак, объявляем нужные переменные:

  var
    po: GLhandleARB;   // программный объект (program object)
    vs: GLhandleARB;   // вершинный шейдер
    fs: GLhandleARB;   // фрагментный шейдер
    u_angle: GLuint;   // адрес uniform-переменной 'u_angle'
    a_phase: GLuint;   // адрес attribute-переменной 'a_phase'
    u_texture: GLuint; // адрес uniform-переменной 'u_texture'

Для проверки ошибок и вывода лога создадим процедуру CheckGLSLError;

procedure CheckGLSLError(Handle: GLhandleARB; Param: GLenum);
var
  l, glsl_ok: GLint;
  s: PChar;
begin
  CheckGLError; // Проверяем ошибку OpenGL (см. демку к статье).
  glGetObjectParameterivARB(Handle, Param, @glsl_ok); // Получаем успешность операции 
                                                      // 'Param' для хэндла 'Handle'.
  s := StrAlloc(1000);
  glGetInfoLogARB(Handle, StrBufSize(s), @l, PGLcharARB(s)); // Получаем лог последней операции
                                                             // над хэндлом 'Handle'.
  Writeln(s); // Вывод лога на экран.
  Assert(glsl_ok = GL_TRUE, 'glSLang error');
end;

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

  po := glCreateProgramObjectARB;

  // Теперь создадим сами шейдеры.

  vs := glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
  CheckGLError; 
  fs := glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
  CheckGLError;

Укажем для шейдеров текст программ:

  procedure LoadSources;
  var
    src: PChar; // PGLcharARB = PChar;
    len: GLuint;
  begin
    // Загружаем текст вершинного шейдера.
    src := PChar(LoadSourceFromFile('Shaders\GLSL-demo.vert'));
    len := StrLen(src);
    glShaderSourceARB(vs, 1, @src, @len);
    CheckGLError;

    // Загружаем текст фрагментного шейдера.
    src := PChar(LoadSourceFromFile('Shaders\GLSL-demo.frag'));
    len := StrLen(src);
    glShaderSourceARB(fs, 1, @src, @len);
    CheckGLError;
  end;

Объявление glShaderSource выглядит так:

procedure glShaderSourceARB(shaderObj: GLhandleARB; count: GLsizei; 

                            const string_: PPGLcharARB; const length: PGLint);
где:
shaderObj - хэндл нужного шейдера
count  - число строк в массиве
string_ - указатель на массив строк
len - указатель на массив длин строк

Вызов glShaderSource принимает указатель на массив указателей на строку (GLcharARB **string_). Переменная string_ может быть null-terminated, в этом случае длину строки нужно ставить в -1. Чаще всего, используют одноэлементный массив, т.е. указатель на указатель на строку, соотв. так же и с длиной строки.

  // Компилируем шейдеры:
  
  glCompileShaderARB(vs);
  CheckGLSLError(vs, GL_OBJECT_COMPILE_STATUS_ARB);
  glCompileShaderARB(fs);
  CheckGLSLError(fs, GL_OBJECT_COMPILE_STATUS_ARB);

  // Затем присоединяем шейдеры к программному объекту:

  glAttachObjectARB(po, vs);
  glAttachObjectARB(po, fs);

Присоединять к программному объекту можно несколько шейдеров одного типа, т.е. функция, используемая в одном шейдере, может быть реализована в другом, однако, перед использованием этой функции нужно объявить ее прототип. Шейдерные функции связываются при линковке.

  // Линкуем шейдеры в программном объекте (при линковке varying в vs 
  // соединяются с varying в fs, объединяются общие uniform'ы).

  glLinkProgramARB(po);
  CheckGLSLError(po, GL_OBJECT_LINK_STATUS_ARB);

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

  glValidateProgramARB(po);
  CheckGLSLError(po, GL_OBJECT_VALIDATE_STATUS_ARB); 

Получаем адрес нужных uniform-, varying-, и attribute-переменных:

  u_angle := glGetUniformLocationARB(po, PGLcharARB(PChar('u_struct.angle')));
  CheckGLError;

  u_texture := glGetUniformLocationARB(po, PGLcharARB(PChar('u_texture')));
  CheckGLError;

  a_phase := glGetAttribLocationARB(po, PGLcharARB(PChar('a_phase')));
  CheckGLError;

А почему бы не обращаться по имени? Парсинг имени и поиск по строке каждый раз при обращении к uniform'у - это не самый лучший вариант по производительности, поэтому и было сделано через индексы.

  // Биндим программный объект:
        
  glUseProgramObjectARB(po);

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

  glUniform1fARB(u_angle, angle); // Пишем в 'u_angle' значение переменной angle.
  CheckGLError;
  angle :=angle + angleStep;      // angle принимает значения от 0 до 2*Pi,
                                  // с каждым кадром увеличиваясь на angleStep.

  if angle > 2*Pi then angle := 2*Pi - angle;
  
  glUniform1iARB(u_texture, 0); // Пишем в 'u_texture' номер используемого tiu, 
                                // в данном случае 0. Разумеется, до этого нужно установить 
                                // текстурный образ и параметры фильтрации, разрешать
                                // GL_TEXTURE_2D не обязательно. 
  CheckGLError;

  glBegin(GL_TRIANGLES);                
    glColor3f(1.0, 0.0, 0.0);                
    glTexCoord2f(0.0, 0.0);
    glVertexAttrib1fARB(a_phase, 0.0); // Пишем в 'a_phase' требуемую фазу для вершина,
                                       // в данном случае 0. 
    glVertex3f(-10.0, -10.0, -30.0);

    glColor3f(0.0, 1.0, 0.0);
    glTexCoord2f(1.0, 0.0);
    glVertexAttrib1fARB(a_phase, 1.0); // Пишем в 'a_phase' требуемую фазу для вершина,
                                       // в данном случае 1.0. 
    glVertex3f(10.0, -10.0, -30.0);

    glColor3f(0.0, 0.0, 1.0);
    glTexCoord2f(0.5, 1.0);
    glVertexAttrib1fARB(a_phase, 2.0); // Пишем в 'a_phase' требуемую фазу для вершина,
                                       // в данном случае 2.0. 
    glVertex3f(0.0, 10.0, -30.0);
  glEnd;

Все. Запускаем и наслаждаемся работающим GLSL-шейдером.

Заключение

Вполне ожидаемый вопрос - достаточно ли мощен и нов GLSL, и стоит ли переходить на него? Думаю, да. На самом деле, реальных альтернатив GLSL нет, так как Cg - это лишь полумера, некая надстройка над OpenGL в попытке объединить существующие шейдеры в язык высокого уровня, и уменьшить количество vendor-specific кода. Тем более, что GLSL является неотъемлемой частью грядущего OpenGL 2.0, призванного стать новым этапом в неувядающей гонке с D3D ;)

Кроме того:

1. Для GLSL придуман удобный, практичный, расширяемый шейдерный framework. Т.е. кроме vs/ps, туда легко вписать texture shaders, pixel shaders (т.е. шейдеры, работающие с передачей пиксельных данных, например, функцией glTexImage; и, с дальнейшим развитием железа, geometry shaders, или surface shaders (по типу Stanford Shading Language).

2. Компиляция GLSL-шейдеров возложена на драйвер, поэтому не требуются внешние библиотеки (как в Cg). Из-за высокоуровневости языка можем получить более оптимизированный код, чем при использовании ассемблероподобных шейдеров, так как у компилятора в этом случае больше простора для творчества ;)

Демка к статье (с исходным кодом на Delphi) - 20040102.rar. Требуемые dll для запуска демки (devil.dll, ilu.dll, ilut.dll, glut32.dll) - 20040102_1.rar.

Источники:

1. OpenGL Shader Language Specification v1.051
http://opengl.org/documentation/oglsl/ShaderSpecV1.051.pdf
2. 3Dlabs GL2SDK
http://www.3dlabs.com/support/developer/ogl2/downloads/
3. ARB_shader_objects specification.
http://oss.sgi.com/projects/ogl-sample/registry/ARB/shader_objects.txt
4. ARB_vertex_shader specification.
http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_shader.txt
5. ARB_fragment_shader specification.
http://oss.sgi.com/projects/ogl-sample/registry/ARB/fragment_shader.txt
6. 3Dlabs OpenGL 2.0 whitepapers:
http://www.3dlabs.com/support/developer/ogl2/whitepapers/

Примечания:

1. Чтение из текстуры в вершинном шейдере, функция шума и циклы не реализованы в текущих драйверах (ATI Catalyst 3.10).

2. При компиляции шейдера с комментариями на русском языке в кодировке Windows-1251 компилятор выдавал ошибку 'pre-mature EOF', поэтому я использовал DOS-кодировку.

Страницы: 1 2 3 4

#GLSL, #шейдеры

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

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