Opengl шейдери в сучасній інтерпретації

Opengl шейдери в сучасній інтерпретації

Нові версії OpenGL не змушують на себе чекати і весь час, з'являється інформація, що деякі функції вже не рекомендуються, а то і зовсім вилучені. А, що ж приходить на зміну традиційному, звичному функціоналу ?!
А нічого, все тепер можна з легкістю виконати на шейдерах. Про це і піде мова далі.

Тенденція така, що весь функціонал, будь то модель освітлення, якісь видові перетворення, інтерпретування даних в текстури та інше ... Все це йде в шейдери. Навіть у версії OpenGL 2.0 можна було робити багато всього, але старий функціонал залишився тільки заради сумісності і його поступово замінюють новими прийомами, видаляючи старі функції. Та й до того ж все, що робиться в шейдерах, апаратно прискорено.

Був час, коли я боявся йти від звичного і використовувати такі речі, як шейдери. Вони довгий час здавалися мені незрозумілими, хоча туторіали по сабжу виглядали захоплююче красиво, і цікаво! І ось одного разу, зібравшись з силами, я все ж їх подужав (справа зрушила з мертвої точки). І тепер вирішив поділиться простим прикладом їх використання.

теоретичний екскурс

Сьогодні мова піде піде про GLSL-шейдерах, але GLSL не єдиний шейдерний мову для роботи з OpenGL.
Можна наприклад використовувати шейдери на CG (C for Graphics). Для цього потрібні додаткова бібліотека CG від NVidia.

GLSL (OpenGL Shading Language) - мова високого рівня для програмування шейдеров. Синтаксис мови базується на мові програмування ANSI C, проте, через його специфічної спрямованості, з нього були виключені багато можливостей, для спрощення мови і підвищення продуктивності. У мову включені додаткові функції і типи даних, наприклад для роботи з векторами і матрицями. GLSL став повністю частиною OpenGL в версії 2.0.

Графічний конвеєр OpenGL 2.0

Opengl шейдери в сучасній інтерпретації

Верховий шейдер - замінює частину графічного конвеєра, що виконує перетворення пов'язані з даними вершин. Такі, як множення вершин (також нормалей) на матрицю проекції і моделювання, установка квітів вершин, установка матеріалів освітлення. Він відпрацює для кожної відмалювати вершин.
Обов'язковою роботою для вершинного шейдера є запис позиції вершини, в вбудовану змінну gl_Position.

Геометричний шейдер - шейдер здатний обробити не тільки одну вершину, а й цілий примітив. Він може або відкинути (від обробки) примітиви, або створити нові, тобто геометричний шейдер здатний генерувати примітиви. А також здатний змінювати тип вхідних примітивів. (Примітка: геометрично шейдер повністю увійшов в OpenGL, в версії 3.2)

Фрагментний шейдер - замінює частину графічного конвеєра (ГК), що обробляють кожен отриманий на попередніх стадіях ГК фрагмент (НЕ піксель). Обробка може включати такі стадії, як отримання даних з текстури, прорахунок освітлення, прорахунок змішування.
Обов'язковою роботою для фрагментного шейдера є запис кольору фрагмента, в вбудовану змінну gl_FragColor, або його відкидання спеціальної командою discard. У випадки відкидання фрагмента, ніякі розрахунки далі з ним проводиться не будуть, і фрагмент вже не потрапить в буфер кадру.

(Примітка: також в OpenGL є ще два типи тесселяціонних шейдеров, вони доступні в OpenGL 4.0 і вище)

Завантаження і компіляція

GLSL шейдери прийнято зберігати в вигляді вихідних кодів, але OpenGL 4.1 дозволяє завантажувати шейдери у вигляді бінарних даних для кращої переносимості шейдеров на різні апаратні і програмні платформи. Тексти програм компілюються драйвером. Вони можуть бути скомпільовані лише після створення діючого контексту OpenGL. Драйвер сам генерує всередині себе оптимальний двійковий код, який розуміє дане устаткування. Це гарантує, що один і той же шейдер буде правильно і ефективно працювати на різних платформах.

Вихідний код може бути представлений у вигляді ANSI рядків завершуються перенесенням рядка ( 'n') або без нього. У випадку якщо перенесення немає, потрібно передати масив довжин кожного рядка.

Кроки завантаження і компіляції:

  • Спочатку виділяються ідентифікатори у вигляді GLuint, під шейдери - glCreateShader і шейдерну програму glCreateProgram;
  • На ідентифікатор шейдера завантажується вихідний код, який передається драйверу glShaderSource;
  • Після шейдер компілюється glCompileShader;
  • Кілька шейдеров різних типів, прикріплюються до програми glAttachShader;
  • Останній крок лінкування прикріплених шейдеров в одну шейдерну програму glLinkProgram.

Прийшов час, розглянути невеликий приклад, який використовує OpenGL 2.0. Але при цьому в прикладі не використовується фіксований графічний конвеєр, щоб максимально наблизиться, до OpenGL 3.3. Це може допомогти більш плавного переходу на нові версії OpenGL, а також щоб було легше працювати з OpenGL ES 2.0 / 3.0, так як в OpenGL ES 2.0 / 3.0 також відсутній фіксований графічний конвеєр.

Ми будемо використовувати верховий і Фрагментний шейдери, так без них в сучасних версіях OpenGL нічого не намалюєш, а інші типи шейдерів ми поки розглядати не будемо, так їх немає в OpenGL 2.0, і вони не є обов'язковими в OpenGL 3.3 і вище.

верховий шейдер

Створюємо атрибут у вигляді двомірного вектора з ім'ям coord. Саме в ньому і будуть приходити дані про координати вершини.
Атрибут (attribute) - це дані передаються програмою вершинні шейдери (іншим шейдерам дані не доступні). Причому дані приходять шейдеру на кожну вершину. Ці дані доступні тільки для читання.
vec2 - це двовимірний вектор типу float.

gl_Position - це вбудована змінна для записи обробленої шейдером позиції вершини. Так, як вона має тип vec4, ми створюємо вектор з чотирьох компонент беручи x і y з атрибута, z ставимо в нуль, а w в 1.0. Далі наші дані про вершині йдуть далі по конвеєру.

Фрагментний шейдер

Оголошуємо Юніформ змінну типу чотирьох компонентного вектора. У ній ми передамо бажаний колір примітиву.

Uniform - це дані посилаються в шейдер додатком, на відміну від атрибута вони глобальні, і для шейдеров, і для вершин. Тобто якщо оголосити Юніформ змінну з однаковим ім'ям в верховому і фрагментом шейдера вони будуть загальними і для них. Також дані не залежать від того яка зараз вершина обробляється, вони залишаються незмінними поки їх не змінить додаток.

gl_FragColor - це вбудована змінна має тип vec4, в неї записується оброблений фрагментного шейдером колір фрагмента.

Для складання прикладу, Вам знадобиться бібліотеки GLEW і freeglut

Даний приклад дуже простий. І має на меті показати, лише, як завантажуються і підключаються шейдери. І як будуть виглядати найпростіші вершину і Фрагментний шейдери.

Вихідний код прикладу

висновок