Текстури - Підручник з OpenGL - Каталог статей - Сайт команди Ghost Studio

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

  • вибрати зображення та перетворити його до потрібного формату
  • завантажити зображення на згадку
  • визначити, як текстура наноситиметься на об'єкт і як вона з ним взаємодіятиме.

Розглянемо кожен із цих етапів.

Прийнятий в OpenGL формат зберігання зображень відрізняється від стандартного формату Windows DIB тільки тим, що компоненти (R, G, B) для кожної точки зберігаються у прямому порядку, а не у зворотному і вирівнювання задається програмістом. Зчитування графічних даних із файлу та їх перетворення можна проводити і вручну, проте зручніше скористатися функцією, що входить до складу бібліотеки GLAUX (для її використання треба додатково підключити glaux.lib), яка сама проводить необхідні операції. Це функція

AUX_RGBImageRec*auxDIBImageLoad(string file)

де file-назва файлу з розширенням *.bmp або *.dib. Як результат, функція повертає покажчик на область пам'яті, де зберігаються перетворені дані.

При створенні текстури в пам'яті слід враховувати наступні вимоги.

По-перше, розміри текстури як по горизонталі, так і по вертикалі повинні бути ступенем двійки. Ця вимога накладається для компактного розміщення текстури у пам'яті та сприяє її ефективному використанню. Використовувати тільки текстури з такими розмірами, звичайно, незручно, тому перед завантаженням їх треба перетворити. Зміна розмірів текстурипроводиться за допомогою команди

voidgluScaleImage(GLenum формат, GLint widthin, GL heightin, GLenum typein, const void *datain, GLint widthout, GLint heightout, GLenum typeout, void *dataout)

GL_UNSIGNED_BYTE, GL_SHORT, GL_INT і так далі. Результат своєї роботи функція заносить у область пам'яті, яку вказує параметр dataout.

По-друге, треба передбачити випадок, коли об'єкт за розмірами значно менше текстури, що наноситься на нього. Чим менше об'єкт, тим менше повинна бути текстура, що наноситься на нього, і тому вводиться поняття рівнів деталізації текстури. Кожен рівень деталізації задає деяке зображення, яке є зазвичай зменшеною вдвічі копією оригіналу. Такий підхід дозволяє покращити якість нанесення текстури на об'єкт. Наприклад, зображення розміром 2 m x2 n можна побудувати max(m,n)+1 зменшених зображень, відповідних різним рівням деталізації.

Ці два етапи створення образу текстури у пам'яті можна провести за допомогою команди

voidgluBuild2DMipmaps(GLenum target, GLint components, GLint width, GLint height, GLenum format, GLenum type, const void *data)

де параметр target повинен дорівнювати GL_TEXTURE_2D, components визначає кількість колірних компонентів текстури, які будуть використовуватися при її накладенні і може приймати значення від 1 до 4 (1-тільки червоний,2-червоний і alpha, 3-червоний, синій, зелений, 4 -Всі компоненти).

Параметри width, height, data визначають розміри та розташування текстури відповідно, а format і type мають аналогічний зміст, що у команді gluScaleImage().

В OpenGL допускається використання одновимірних текстур, тобто розміру 1xN, проте це завжди треба вказувати, використовуючи як значення target константуGL_TEXTURE_1D. Існує одновимірний аналог аналізованої команди-gluBuild1DMipmaps(), який відрізняється від двовимірного відсутністю параметра height.

При використанні в сцені декількох текстур OpenGL застосовується підхід, що нагадує створення списків зображень. Спочатку за допомогою команди

voidglGenTextures(GLsizei n, GLuint*textures)

треба створити n ідентифікаторів для текстур, що використовуються, які будуть записані в масив textures. Перед початком визначення властивостей чергової текстури слід викликати команду

voidglBindTexture(GLenum target, GLuint texture)

де target може приймати значення GL_TEXTURE_1D або GL_TEXTURE_2D, а параметр texture повинен дорівнювати ідентифікатору тієї текстури, до якої будуть відноситися наступні команди. Для того, щоб у процесі малювання зробити поточну текстуру з деяким ідентифікатором, достатньо знову викликати команду glBindTexture() з відповідним значенням target та texture. Таким чином, команда glBindTexture() включає режим створення текстури з ідентифікатором texture, якщо така текстура ще не створена, або режим її використання, тобто робить цю поточну текстуру.

Методи накладання текстури

При накладенні текстури, як згадувалося, треба враховувати випадок, коли розміри текстури від розмірів об'єкта, який вона накладається. При цьому можливе як розтягування, так і стиснення зображення, і те, як будуть проводитися ці перетворення, може серйозно вплинути на якість побудованого зображення. Для визначення положення точки на текстурі використовується параметрична система координат (s, t), причому значення s та t знаходяться у відрізку [0,1]. Для зміни різних параметрів текстури використовуються команди:

  • voidglTexParameter[i f](GLenum target, GLenum pname, GLenum param)
  • voidglTexParameter[i f]v(GLenum target, GLenum pname, GLenum *params)

При цьому target має аналогічний зміст, що й раніше, pname визначає, яку властивість змінюватимемо, а за допомогою param або params встановлюється нове значення. Можливі значення pname:

GL_TEXTURE_MIN_FILTERParam визначає функцію, яка буде використовуватися для стиснення текстури. При значенні GL_NEAREST буде використовуватися один (найближчий), а при значенні GL_LINEAR чотири найближчі елементи текстури.

Значення за промовчанням: GL_LINEAR.

GL_TEXTURE_MAG_FILTERпараметр param визначає функцію, яка використовуватиметься для збільшення (розтягування) текстури. При значенні GL_NEAREST буде використовуватися один (найближчий), а при значенні GL_LINEAR чотири найближчі елементи текстури.

Значення за промовчанням: GL_LINEAR.

GL_TEXTURE_WRAP_Sпараметр param встановлює значення координати s, якщо воно не входить у відрізок [0,1]. При значенні GL_REPEAT ціла частина s відкидається і в результаті зображення розмножується по поверхні. При значенні GL_CLAMP використовуються крайові значення: 0 або 1, що зручно використовувати, якщо об'єкт накладається один образ.

Значення за промовчанням: GL_REPEAT.

GL_TEXTURE_WRAP_Tаналогічно до попереднього значення, тільки для координати t.

Використання режиму GL_NEAREST значно підвищує швидкість накладання текстури, проте знижується якість, оскільки на відміну від GL_LINEAR інтерполяція не проводиться.

Для того, щоб визначити, як текстура буде взаємодіяти з матеріалом, з якого зроблено об'єкт, використовуються команди

  • voidglTexEnv[if](GLenum target, GLenum pname, GLtype param)
  • voidglTexEnv[i f]v(GLenum target, GLenum pname, GLtype *params)

Параметр target повинен дорівнювати GL_TEXTURE_ENV, а як pname розглянемо тільки одне значення GL_TEXTURE_ENV_MODE, яке найчастіше застосовується. Параметр якщо param може дорівнювати:

  • GL_MODULATEкінцевий колір знаходиться як добуток кольору точки на поверхні та кольору відповідної їй точки на текстурі.
  • GL_REPLACEяк кінцевий колір використовується колір точки на текстурі.
  • GL_BLENDкінцевий колір знаходиться як сума кольору точки на поверхні і кольору відповідної точки на текстурі з урахуванням їх яскравості.
Координати текстури

Перед нанесенням текстури на об'єкт залишилося встановити відповідність між точками на поверхні об'єкта та на самій текстурі. Задавати цю відповідність можна двома методами: окремо кожної вершини чи відразу всіх вершин, задаючи параметри спеціальної функції відображення.

Перший метод реалізується за допомогою команд

  • voidglTexCoord[1 2 3 4][s i f d](type coord)
  • voidglTexCoord[1 2 3 4][s i f d]v(type *coord)

Найчастіше використовують команди виду glTexCoord2..(type s, type t), що задають поточні координати текстури. Взагалі, поняття поточних координат текстури аналогічне поняттям поточного кольору та поточної нормалі і є атрибутом вершини. Однак навіть для куба знаходження відповідних координат текстури є досить трудомістким заняттям, тому в бібліотеці GLU крім команд, які проводять побудову таких примітивів, як сфера, циліндр і диск, передбачено також накладання текстур. Для цього достатньо викликати команду

voidgluQuadricTexture(GLUquadricObj*quadObject, GLboolean textureCoords)

з параметром textureCoords рівним GL_TRUE, і тоді поточна текстура автоматично накладатиметься на примітив.

Другий метод реалізується за допомогою команд

  • voidglTexGen[i f d](GLenum coord, GLenum pname, GLtype param)
  • voidglTexGen[i f d]v(GLenum coord, GLenum pname, const GLtype *params)

Параметр coord визначає для якої координати задається формула і може набувати значення GL_S, GL_T; pname визначає тип формули і може дорівнювати GL_TEXTURE_GEN_MODE, GL_OBJECT_PLANE, GL_EYE_PLANE. За допомогою params задаються необхідні параметри, а param може дорівнювати GL_OBJECT_LINEAR, GL_EYE_LINEAR, GL_SPHERE_MAP. Розгляд усіх можливих комбінацій значень аргументів цієї команди зайняв би занадто багато місця, тому як приклад розглянемо, як можна задати дзеркальну текстуру. При такому накладенні текстури зображення буде відбиватися від поверхні об'єкта, викликаючи цікавий оптичний ефект. Для цього спочатку треба створити два цілих масиву коефіцієнтів s_coeffs і t_coeffs зі значеннями (1,0,0,1) і (0,1,0,1) відповідно, а потім викликати команди: