5.4 Створюємо текстуру в пам'яті
Одного виведення зображень недостатньо для створення повноцінних тривимірних сцен. Часто виникає потреба накладати зображення на тривимірні об'єкти і повертати / зрушувати їх. Для цих цілей існую текстури. Також текстури допоможуть вам покрити весь об'єкт у вигляді мозаїки. Скажімо, коли у вас є цегляна стіна, то вам не треба завантажувати зображення з купою цегли. Досить завантажити одну цеглину і вказати, що цю текстуру потрібно розмножити по всій площині.
Спочатку ми розберемо створення і накладення текстур на площину. Потім розглянемо накладення текстур на об'єкти, описані в секції 4.1. І нарешті, на всі інші, створені з багатокутників; зокрема, тор і чайник.
Перше ви вже навчилися робити в попередньому розділі. Створіть проект з ім'ям Texture. Оголосіть наступні глобальні змінні:
Змінні photo_tex і space_tex служитимуть ідентифікаторами текстур. А в photo_image і space_image ми завантажимо bmp-файли. Тут потрібно відзначити, що текстури в OpenGL повинні мати розмір 2 n x 2 m. де n і m цілі числа. Це зроблено для прискорення роботи, тому що стискати або розтягувати такі текстури швидше і зручніше. Ви, звичайно, можете завантажити зображення будь-якого іншого розміру, але його доведеться масштабувати. На мій погляд, це неправильно. Результат масшабірованія вас може і не влаштувати. Так що, я використовую графічні файли з розміром, кратним ступеня двійки. Мені зручніше відредагувати зображення в будь-якому графічному пакеті, урізати його або, навпаки, доповнити, ніж потім з'ясовувати, чому воно спотворюється. Втім, тут багато що залежить від конкретного випадку. Художнику, який робить текстури, все одно, якого розміру її робити, тому легше попросити його зробити зображення з відповідними розмірами. Вставте цей код в функцію main.
Картинки візьміть з моєї програми - Texture. Фотографію можете взяти свою. ;-) Тільки розмір її бажано залишити 512x512.
Тепер ви повинні створити ім'я-ідентифікатор текстури. Його потрібно створювати, коли у вас в додатку використовується більше однієї текстури, щоб була можливість якось їх розрізняти. В іншому випадку, коли текстура лише одна, ідентифікатор їй не потрібен. У наступному прикладі, при накладення текстури на сферу, у нас буде рівно одна текстура, і я покажу, виклики яких функцій необов'язкові. А поки, я припускаю, що вам потрібно використовувати декілька текстур. До речі, я переглядав багато прикладів при написанні книги. Серед них були приклади з широко відомої Red Book, приклади з MSDN, з Інтернет і інших джерел, але все, що стосувалося текстур, працювало тільки з однієї текстурою. Для елементарної програми-прикладу, звичайно, підійде і одна текстура, а ось для серйозних додатків, навряд чи. Ми хочемо написати серйозні додатки, тому нам доведеться використовувати декількох текстур. Функція glGenTextures приймає на вхід два параметра. Перший вказує кількість імен-ідентифікаторів текстур, які потрібно створити. Другий параметр - покажчик на масив елементів типу unsigned int. Кількість елементів в масиві має збігатися з числом, зазначеним в якості першого параметра. Наприклад, наступний код створює десять імен текстур.
Зберігати ідентифікатори текстур в масиві не завжди зручно. Такий спосіб підходить для зберігання задніх фонів або типів стін: цегляна, кам'яна і т.п. Загалом, в масиві зберігають ті елементи, між якими є щось спільне. У нашому випадку, два зображення пов'язані, тому що Використовується в одному додатку, тому я створив два різних ідентифікатора. Так що, додайте наступний код в функцію main.
Тепер ми прив'язуємося до текстурі фотографії, тобто робимо її активною. Для цього служить функція glBindTexture. Перший параметр повинен бути GL_TEXTURE_2D або GL_TEXTURE_1D. Він показує, з одновимірним або двовимірним зображенням будемо працювати. Всі приклади тут стосуються двовимірних текстур. Для одновимірної текстури я просто не знайшов гарного прикладу. Втім, в Red Book є приклад з одновимірної текстурою. Там чайник прикрашають червоною стрічкою. Де взяти ці приклади і багато іншого дивіться у додатку 'A'. Другий параметр glBindTexture - ідентифікатор, який ми створили вище за допомогою glGenTextures. Тепер додайте виклик цієї функції в main.
Тепер ми повинні створити саму текстуру в пам'яті. Масив байт в структурі AUX_RGBImageRec не є ще текстурою, тому що у текстури багато різних параметрів. Створивши текстуру, ми наділимо її певними властивостями. Серед параметрів текстури ви вказуєте рівень деталізації, спосіб масшабірованія і зв'язування текстури з об'єктом. Рівень деталізації потрібен для накладення текстури на менші об'єкти, тобто коли площа на екрані менше розмірів зображення. Нульовий рівень деталізації відповідає вихідного зображення розміром 2 n x2 m. перший рівень - 2 n-1 x2 m-1. k-ий рівень - 2 n-k x2 m-k. Число рівнів відповідає min (n, m). Для створення текстури є дві функції glTexImage [1/2] D і gluBuild [1/2] DMipmaps.
Основна відмінність в тому, що перша функція створює текстуру одного певного рівня деталізації і сприймає тільки зображення. розмір яких кратний ступеня двійки. Друга функція більш гнучка. Вона генерує текстури всіх рівнів деталізації. Також ця функція не вимагає, щоб розмір зображення був кратний ступеня двійки. Вона сама стисне / розтягне зображення відповідним чином, хоча може виявитися, що і не цілком відповідним. Я скористаюся функцією glTexImage2D. Перший параметр цієї функції повинен бути GL_TEXTURE_2D. Другий - рівень деталізації. Нам потрібно вихідне зображення, тому рівень деталізації - нуль. Третій параметр вказує кількість компонентів кольору. У нас зображення зберігається в форматі RGB. Тому значення цього параметра дорівнює трьом. Четвертий і п'ятий параметри - ширина і висота зображення. Шостий - ширина кордону; у нас гарніци не буде, тому значення цього параметра - нуль. Далі, сьомий параметр - формат зберігання пікселів в масиві - GL_RGB і тип - GL_UNSIGNED_BYTE. І нарешті, восьмий параметр - покажчик на масив даних. Ще ви повинні викликати функцію glPixelStorei і задати, що вирівнювання в масиві даних йде по байту. Додайте наступний код в функцію main.
Аналогічний результат можна отримати, вставивши виклик gluBuild2DMipmaps з параметрами, зазначеними нижче.
Тепер потрібно встановити параметри текстури. Для цього служить функція
Перший параметр приймає значення GL_TEXTURE_1D або GL_TEXTURE_2D. Другий - pname - визначає параметр текстури, який ви будете змінювати. І третій параметр - це встановлюється значення. Якщо ви скористалися gluBuild2DMipmaps замість glTexImage2D, то вам не треба встановлювати такі параметри, тому що вже сформовані текстури всіх рівнів деталізації, і OpenGL зможе підібрати текстуру потрібного рівня, якщо площа об'єкта не збігається з площею текстури. В іншому випадку, ви повинні додати наступні рядки:
Ви вказали, що для зменшення і збільшення текстури використовується алгоритм GL_NEAREST. Це означає, що кольором пікселя об'єкта, на який накладається текстура, стає колір найближчого пікселя елемента текстури. Замість GL_NEAREST можна вказати GL_LINEAR, тобто колір елемента об'єкта буде обчислюватися як середнє арифметичне чотирьох елементів текстури. Є ще чотири алгоритму обчислення кольору елемента об'єкта. Їх можна встановлювати, коли ви створили текстуру з усіма рівнями деталізації, тому що застосовують алгоритми GL_NEAREST і GL_LINEAR до одного або двох найближчих рівнями деталізації.
Ще ви можете встановити взаємодію текстури з об'єктом. Тут є два режими при використанні трьох компонентів кольору. Перший режим, встановлений за замовчуванням, коли у вас враховується колір об'єкта і колір текстури. Результуючий колір виходить перемножением компонентів кольору текстури на компоненти кольору об'єкта. Скажімо, якщо колір текстури - (r, g, b), а колір об'єкта, на який вона накладається, - (r0, g0, b0), то результуючим кольором буде - (r * r0, g * g0, b * b0) . У разі, якщо колір об'єкта чорний - (0,0,0), то ви не побачите на ньому текстуру, так як вона вся буде чорною. Другий режим взаємодії, коли колір об'єкта не враховується. Результуючим кольором буде колір текстури. Ці параметри можна встановити наступним чином.
За замовчуванням, як я вже сказав, є режим GL_MODULATE. Тепер зробіть активної текстуру space_tex. І повторіть для неї те ж саме. На цьому закінчується створення текстури. Залишилося пов'язати координати текстури з координатами об'єкта. Відредагуйте функцію display так:
Як ви, напевно, здогадалися, glTexCoord2d зіставляє координати текстури вершин чотирикутника. Скажу тільки, що нижній лівий кут текстури має координати (0,0), а верхній правий - (1,1).
Вихідний файл дивіться тут. Виконуваний файл тут.
Моє фото тут. Зоряне небо тут.