ВИСВІТЛЕННЯ В OPENGL ES – РОЗРОБКА ІГОР ДЛЯ ОС ANDROID

&nbsp

Освітлення в OpenGL ES – це корисна особливість, яка може надати 3D-ігор приємний відтінок Щоб використовувати подібну функціональність, спочатку нам необхідно зрозуміти модель освітлення OpenGL ES

Як працює освітлення

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

Інтенсивність, з якою світло відбивається обєктом, залежить від безлічі факторів Найголовніший чинник, на який ми звертаємо увагу, – це кут, з яким світловий промінь падає на поверхню Чим ближче цей кут до прямого, тим більше інтенсивність, з якою світло відібється від обєкта Це проілюстровано на рис 111

Як тільки світловий промінь впаде на поверхню, він відібється в двох різних напрямках Велика частина світла відібється неуважно Це означає, що відображені світлові промені нерівномірно розсипані у випадковому порядку по поверхні обєкта Деякі промені відбиваються дзеркально Це означає, що світлові промені відібються назад так, ніби вони падають на ідеальне дзеркало На рис 112 показана різниця між розсіяним і дзеркальним відбитками

Рис 111 Чим ближче кут до прямого, тим більше інтенсивність відбитого світла

Рис 112 Розсіяне і дзеркальне відображення

Дзеркальне відображення буде проявлятися як відблиски на обєктах Чи буде світло відбиватися від обєкта дзеркально, залежить від матеріалу, з якого він зроблений Обєкти з нерівною або шорсткою, як шкіра, поверхнею, швидше за все, не матимуть дзеркальних відблисків Обєкти, що мають гладку поверхню на зразок скла або мармуру, демонструватимуть ці світлові артефакти Звичайно, скло або мармур не є ідеально гладкими, але в порівнянні з деревом або людською шкірою вони є такими

Коли світло падає на поверхню, його відображення також змінює свій колір залежно від хімічного складу освітлюваного обєкта Обєкти, які здаються нам червоними, відображають тільки червоні порції світла і поглинають всі інші частоти Чорний обєкт – це такий обєкт, який поглинає практично весь світ, який падає на нього

OpenGL ES дозволяє імітувати реальну поведінку, визначаючи джерела світла і матеріали обєктів

Джерела висвітлення

Нас оточує безліч різноманітних джерел освітлення Сонце постійно посилає свої фотони Монітори випромінюють світло, навколишній нас приємним світінням ночами Лампочки і фари допомагають нам уникати зіткнень з різними предметами в темряві OpenGL ES дозволяє створювати чотири типи джерел світла

Підсвічування Є сама по собі не джерелом світла, а результатом появи фотонів від інших світових джерел Разом ці випадкові фотони створюють певний постійний рівень освітлення, що не має напрямки та висвітлює всі обєкти однаково

Точкові джерела світла Мають позицію в просторі і випускають світло у всіх напрямках Наприклад, точковим джерелом світла є лампочка

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

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

Ми будемо розглядати тільки підсвічування, а також точкові і спрямовані джерела світла Світильники часто складно використовувати на обмежених GPU Android-пристроїв через спосіб розрахунку освітлення в OpenGL ES Скоро ви зрозумієте, чому це так

Крім позиції і напрямки джерела світла OpenGL ES дозволяє визначати колір або інтенсивність світла Ці характеристики виражаються за допомогою кольору RGBA Однак OpenGL ES вимагає визначати чотири різних кольори для одного джерела замість одного

Підсвітка – інтенсивність / колір, що вносить вклад у створення затінення обєкта Обєкт буде висвітлений однаково з усіх сторін, незалежно від його позиції або орієнтації щодо джерела світла

Розсіяний – інтенсивність / колір світла, яким буде освітлений обєкт після розрахунку розсіяного відображення Грані обєкта, які не дивляться на джерело світла, не будуть висвітлені, як і в реальному житті

Дзеркальний – інтенсивність / колір, схожий на розсіяний колір Однак він впливає тільки на ті точки обєкта, які мають певну орієнтацію по відношенню до джерела світла і сенсору

Еміссівний – дуже складний розрахунок кольору, що має надзвичайно обмежене застосування в додатках з фізикою реального світу, тому ми не будемо його розглядати

Найчастіше будемо застосовувати розсіяні і дзеркальні інтенсивності джерела світла, а двом іншим вкажемо значення за замовчуванням Крім того, більшу частину часу будемо використовувати однаковий колір RGBA як для розсіяною, так і для дзеркальної інтенсивності

Матеріали

Всі обєкти в нашому світі складаються з будь-якого матеріалу Кожен матеріал визначає, як світло, що падає на обєкт, відбиватиметься і змінювати колір відбитого світла OpenGL ES дозволяє визначати ті ж чотири кольори RGBA для матеріалу, що і для джерела світла

Підсвітка – колір, який обєднується з фоновим кольором будь-якого джерела світла на сцені

Розсіяний – колір, який обєднується з розсіяним кольором будь-якого джерела світла

Дзеркальний – колір, який обєднується з дзеркальним кольором будь-якого джерела світла Він використовується для створення відблисків на поверхні обєкту

Еміссівний – продовжуємо ігнорувати цей тип кольору, оскільки він практично не застосовується в нашому контексті

Малюнок 113 ілюструє перші три типи властивостей матеріалу / джерела світла: підсвічування, розсіяне і дзеркальний

Рис 113 Різні типи матеріалів / джерел світла: тільки підсвічування (ліворуч), тільки розсіяний (посередині), підсвічування і розсіяний колір з дзеркальними відблисками (праворуч)

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

Як OpenGL ES розраховує освітлення: нормалі вершин

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

Рис 114 Сфера та її нормалі вершин

Нормалі – це одиничні вектори, що вказують напрямок, до якого повернена поверхню У нашому випадку поверхня – це трикутник Замість визначення нормалі поверхні ми визначаємо нормаль вершини Різниця між цими нормалями полягає в тому, що нормаль вершини може не вказувати в ту ж сторону, що і нормаль поверхні Це чітко видно на рис 114, де кожна нормаль вершини є усередненою нормаллю всіх трикутників, до яких належить вершина Таке усереднення проводиться для створення гладкої затіненості обєкта

При отрісовке обєкта з використанням освітлення та нормалей вершин OpenGL ES визначить кут між кожною вершиною і джерелом світла Якщо він знає цей кут, то може розрахувати колір вершини, грунтуючись на властивостях матеріалу Кінцевим результатом є колір кожної вершини, який далі застосовується до кожного трикутнику в комбінації з розрахованими квітами інших вершин Цей використаний колір буде обєднаний з будь-якими текстурними перетвореннями, які ми застосуємо до обєкта

Це звучить досить лякаюче, але насправді не все так погано Нам потрібно дозволити використання освітлення та визначити джерела освітлення, матеріал отрісовиваємих обєкта і нормалі вершин (на додаток до параметрів вершин, які ми зазвичай визначаємо, наприклад позицію або координати текстур) Розглянемо, як це можна реалізувати за допомогою OpenGL ES

На практиці

Тепер виконаємо всі дії, необхідні для того, щоб працювати з освітленням за допомогою OpenGL ES Створимо кілька невеликих допоміжних класів, які трохи спростять роботу з джерелами світла, і помістимо їх в пакет combadlogi сandroi dgamesframeworkgl

Дозвіл і заборона освітлення

Як і для інших станів OpenGL ES, спочатку слід підключити названу функціональність Це можна зробити наступним чином:

Після цього освітлення буде застосовано до всіх отрісовиваємих обєктам Щоб отримати результат, необхідно визначити джерела світла і матеріали, а також нормалі вершин Як тільки ми закінчимо відмальовувати всі необхідні обєкти, освітлення можна відключити:

Визначення джерел освітлення

OpenGL ES надає 4 типи джерел освітлення: підсвічування, точковий, спрямований і світильник Розглянемо, як визначити перші три Щоб світильники були ефективними і добре виглядали, кожна модель повинна складатися з величезної кількості трикутників Для безлічі теперішніх мобільних пристроїв це неможливо

OpenGL ES дозволяє визначати максимум 8 джерел освітлення одночасно, а також один глобальний джерело підсвічування Кожен з 8 джерел освітлення має ідентифікатор, від GL10GL LIGHT0 до GL10GL LIGHT7 Якщо потрібно змінити властивості одного з цих джерел освітлення, це можна зробити, визначивши відповідний йому ID

Дозволити використання джерел освітлення можна за допомогою наступного синтаксису:

Далі OpenGL ES отримає властивості цього джерела освітлення і застосує їх до всіх отрісовиваємих обєктам Якщо нам потрібно заборонити використання джерела освітлення, ми можемо зробити це за допомогою наступного твердження:

Підсвітка – це особливий випадок, оскільки у неї немає ідентифікатора На сцені OpenGL ES може існувати тільки одна підсвічування Розглянемо цей джерело освітлення докладніше

Підсвічування

Підсвітка – це особливий тип освітлення У нього немає позиції або напряму, тільки колір, який застосовується до всіх освітлюваним обєктам однаково OpenGL ES дозволяє визначати глобальну підсвічування наступним чином:

Масив ambi entCol or містить значення RGBA кольору підсвічування, представлені як числа з плаваючою крапкою в діапазоні від 0 до 1 Метод gl LightModel fv приймає в якості першого параметра константу, визначальну, що ми хочемо встановити колір джерела фонового освітлення, масив чисел з плаваючою точкою, який містить колір джерела, і зсув для масиву чисел з плаваючою точкою, з якого метод почне зчитувати значення RGBA Помістимо код, вирішальний цю задачу, в невеликий клас Його код приведений в лістингу 112

Лістинг 112 Клас AmbientLightjava проста абстракція глобальної підсвічування ODenGL ES

Все, що ми робимо, – зберігаємо колір підсвічування в масиві чисел з плаваючою точкою і надаємо два методи: один з них використовується для установки кольору, а інший, щоб вказати OpenGL ES, що використовувати слід саме цей колір За замовчуванням застосовується сірий колір

Точкові джерела освітлення

Точкові джерела освітлення мають позицію, а також фонові, розсіяні і дзеркальні колір / інтенсивність (ми не розглядаємо еміссівние колір / інтенсивність) Визначити різні типи квітів можна таким чином:

Перший параметр – це ідентифікатор джерела світла У цьому випадку ми використовуємо четвертий джерело Наступний параметр визначає атрибут, який ми хочемо змінити Третій параметр – це масив чисел з плаваючою точкою, що містить значення RGBA, а останній – це зміщення в даному масиві Визначити позицію джерела так само просто:

Ми знову визначаємо атрибут, який хочемо змінити (в даному випадку позицію), масив з чотирьох елементів містить х-, у-і z-координату джерела світла в створюваному світі Зверніть увагу, четвертий елемент масиву має дорівнювати одиниці, якщо джерело світла має позицію Помістимо це у допоміжний клас Його код міститься в лістингу 113

Лістинг 113 Клас PointLightjava, проста абстракція точкових джерел світла OpenGL ES

Наш допоміжний клас містить фонові, розсіяні і дзеркальні колірні компоненти світла, а також позицію (четвертий елемент дорівнює одиниці) На додаток ми зберігаємо останній ідентифікатор, використовуваний для даного джерела, тому стає можливо створити метод disableO, який відключить світло при необхідності Також у нас є метод enableO, який приймає екземпляр класу GL10 та ідентифікатор джерела світла (наприклад GL10GL LIGHT6) Він дозволяє використання освітлення, встановлює його атрибути і зберігає використаний ідентифікатор Метод disableO просто забороняє використання освітлення, використовуючи член класу 1astLightId, встановлений в методі enablе

Ми використовуємо розумні значення за замовчуванням для фонового, розсіяного і дзеркального кольорів при ініціалізації масивів-членів класу Світло буде білим і не створюватиме жодних відблисків, оскільки його дзеркальна складова чорна

Спрямовані джерела світла

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

Ми можемо перетворити його в вектор:

Решта атрибути, начебто фонового або розсіяного кольору, ідентичні аналогічним атрибутам точкового джерела світла У лістингу 114 показаний код невеликого допоміжного класу, що використовується для створення спрямованих джерел світла

Лістинг 114 Клас Directi onLi ghtjava, проста абстракція спрямованих джерел світла в OpenGL ES

Цей допоміжний клас практично ідентичний класу PointLight Єдина відмінність полягає в тому, що в масиві directi on четвертий елемент дорівнює одиниці Крім того, замість методу setPosition зявився метод setDirecti on Він дозволяє визначати напрямок, наприклад так: (-1, 0, 0), в цьому випадку світло буде виходити з правого боку Усередині методу всі компоненти вектора змінюють свій знак, таким чином ми перетворюємо напрямок до формату, очікуваному OpenGL ES

Визначаємо матеріали

Матеріал визначається декількома атрибутами Як і у випадку з будь-якими іншими обєктами OpenGL ES, матеріал – це стан, який буде активно доти, поки ми не змінимо його знову або поки не загубиться контекст OpenGL ES Щоб встановити поточні атрибути матеріалів, ми можемо зробити наступне:

Як і зазвичай, нам необхідно визначити фоновий, розсіяний і дзеркальний RGBA-кольору Це можна зробити так само, як і раніше, – за допомогою масивів чисел з плаваючою точкою, що складаються з чотирьох елементів

Обєднати ці дії в один допоміжний клас дуже просто Результат ви можете побачити в лістингу 115

Лістинг 115 Клас Material Java, проста абстракція матеріалів OpenGL ES

Тут теж немає нічого дивного Ми просто зберігаємо три компоненти, що описують матеріал, а також надаємо функції для установки їх значень і метод enabl е, які передають їх OpenGL ES

У OpenGL ES є ще один козир у рукаві, коли мова йде про матеріали Зазвичай він замість методу glMaterialfvO використовує дещо, зване кольором матеріалу Це означає, що замість фонового і розсіяного кольорів, що визначаються методом glMateri al fv, OpenGL ES прийме колір вершин наших моделей в якості фонового і розсіяного квітів матеріалу Щоб дозволити використання цієї особливості, необхідно просто викликати її:

Зазвичай я саме так і роблю, тому що фоновий і розсіяний кольору часто однакові Оскільки я не використовую дзеркальні відблиски в більшості моїх ігор і демонстрацій, я цілком можу застосовувати такий спосіб і зовсім не викликати метод glMaterial fv Який спосіб задіяти вам – вирішуєте тільки ви

Визначаємо нормалі

Щоб у OpenGL ES працювало освітлення, необхідно визначити нормалі вершин для кожної вершини моделі Нормаль вершини повинна являти собою одиничний вектор, який вказує (звичайно) в ту сторону, в яку повернена поверхню, до якої належить вершина На рис 115 проілюстровані нормалі вершин для нашого куба

Рис 115 Нормалі вершин для кожної вершини нашого куба

Нормаль вершини – це ще один атрибут вершини, такий же, як позиція чи колір Щоб скористатися нормалями вершин, нам необхідно ще раз змінити клас Verti ces3 Для того щоб вказати OpenGL ES, де він може знайти нормалі для кожної вершини, ми будемо використовувати метод gl Normal PointerO, точно так само, як ми раніше застосовували методи gl VertexPointer або gl Col or Pointer У лістингу 116 показана фінальна версія класу Verti ces3

Лістинг 116 Клас Vertices3Java, фінальна версія, що підтримує нормалі

У класі зявився новий член hasNormals, що відслідковує, чи мають вершини нормалі

Конструктор тепер приймає також параметр hasNormals Нам ще необхідно модифікувати розрахунок члена vertexSize, додавши три числа з плаваючою точкою на кожну вершину там, де це можливо

Як ви можете бачити, методи setVertices і setlndices залишаються без змін

У щойно продемонстрованому методі bind Про використовуємо ті ж прийоми з буфером ByteBuffer, що і раніше, але цього разу додаємо нормалі за допомогою методу gl Normal Pointer Для обчислення зміщення покажчика нормалі необхідно прийняти до уваги те, чи задані координати текстур і кольору

Як ви можете бачити, метод draw також не змінився все дійство відбувається в методі bind О

Нарешті, ми кілька змінюємо метод unbindO Забороняємо використання покажчиків нормалі, якщо такі були, відповідно очищаючи стан OpenGL ES

Застосувати змінений клас Verti ces3 так само просто, як і раніше Розглянемо невеликий приклад:

Створюємо масив чисел з плаваючою точкою для зберігання трьох вершин, кожна з яких має позицію (перші три числа в кожному рядку) і нормаль (останні три числа в кожному рядку) У цьому випадку ми задаємо трикутник у площині ху, його нормалі вказують у напрямку позитивної частини осі z

Все, що нам залишається, – створити екземпляр класу Verti ces3 і встановити значення вершин Досить легко, чи не так

Вся робота по привязці, малювання і відвязку виконується точно так само, як і в попередній версії класу Як і раніше, ми можемо додати кольору вершин і координати текстур

Збираємо все воєдино

Зберемо все разом Нам необхідно намалювати сцену, що має глобальну підсвічування, точкові і спрямовані джерела світла Вони будуть висвітлювати куб, розташований на початку координат Нам також потрібно викликати метод gl uLookAt, щоб розташувати камеру На рис 116 показаний зовнішній вигляд нашого світу

Як і для всіх інших прикладів, створимо клас, який буде називатися LightTest, як зазвичай розширює клас GLGame Він буде повертати екземпляр класу LightScreen за допомогою методу getStartScreenO Клас LightScreen успадковує від класу GLScreen (лістинг 117)

Рис 116 Наша перша освітлена сцена

Лістинг 117 Фрагменти класу LightTestjava створення освітлення за допомогою OpenGL ES

Почнемо з опису кількох членів класу Член angle зберігає інформацію про поточний куті повороту куба навколо осі у Член Verti ces3 зберігає вершини моделі куба, які ми скоро визначимо На додаток у нас є екземпляри класів AmbientLight, PointLight і Di rectional Light, а також екземпляр класу Material

Далі слід конструктор Тут створюються вершини моделі куба, а також завантажується текстура ящика Ми також инициализируем джерела освітлення і матеріали Колір підсвічування – світло-зелений Спрямований джерело – червоного кольору і розташовується в точці (3 3 0) нашого світу Спрямований джерело світла має синій розсіяний колір, світло падає зліва Для матеріалу використовуємо значення за замовчуванням (кілька фоновий, білий для розсіяної складової і чорний для дзеркальної)

У методі resume переконуємося, що наша текстура (пере) завантажується, якщо контекст буде втрачено

Метод createCube практично не змінився з попередніх прикладів Однак цього разу ми додаємо нормалі для кожної вершини, що показано на рис 115 Крім цього все залишається колишнім

У методі update просто збільшуємо кут повороту куба

Тут вже цікавіше Перші кілька рядків є шаблонним кодом, призначеним для очищення буфера квітів і глибини, дозволу тестування глибини і установки області видимості

Далі ми встановлюємо матрицю проекцій рівний перспективної матриці проекцій за допомогою методу gl uPerspecti ve, а також використовуємо метод gl uLookAt для модельно-видової матриці, завдяки чому камера працює так само, як на рис 116

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

Як звичайно, також дозволяємо текстурирование і привязуємо текстуру ящика Нарешті, викликаємо метод gl RotatefC) для повороту куба і потім отрісовиваємих його вершини за допомогою вдало розміщених викликів примірника класу Verti ces3

Наприкінці методу ми відключаємо точкові і спрямовані джерела освітлення (памятаєте, підсвічування – це глобальний стан), а також текстурирование і тестування глибини Це все, що стосується висвітлення в OpenGL ES

Інша частина класу порожня нам не потрібно виробляти будь-які дії у разі паузи На рис 117 показаний результат роботи програми

Рис 117 Сцена, зображена на рис 116, промальовані за допомогою OpenGL ES

Кілька приміток до висвітлення в OpenGL ES

Хоча використання освітлення може додати родзинку вашій грі, у нього є свої обмеження і пастки Є кілька моментів, про які ви повинні знати

Використання освітлення споживає занадто багато ресурсів, особливо це помітно на повільних пристроях Застосовуйте освітлення обережно Чим більше джерел освітлення ви опишете, тим більше обчислень буде потрібно, щоб отрісовать сцену

Визначати позицію / напрямок точкових / направлених джерел світла слід після того, як будуть завантажені матриці камери і до того, як модельно-видова матриця буде помножена на які-небудь інші матриці для переміщення і повороту обєктів Це критично Якщо не дотримуватися цих вказівок, можлива поява непояснених світлових артефактів

При використанні методу gl Seal ef для зміни розміру моделі її нормалі також будуть масштабовані Це погано, оскільки OpenGL ES очікує, що нормалі будуть мати параметри в заданих одиницях виміру Щоб обійти цю проблему, ви можете використовувати команду glEnable (GL10GL NORMALIZE) або за деяких обставин gl Enable (GL10 GL RESCALE N0RMAL) Вважаю, слід використовувати першу команду, оскільки застосування другий має обмеження і підводні камені Проблема полягає в тому, що нормалізація або повторне масштабування нормалей вимагає великої обчислювальної потужності Краще рішення з точки зору продуктивності – НЕ масштабувати освітлені обєкти

Джерело: Mario Zechner / Маріо Цехнер, «Програмування ігор під Android», пров Єгор Сидорович, Євген зазноби, Видавництво «Пітер»

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*