Створення зображень з використанням OpenGL, VR-online – безкоштовний електронний журнал для всіх
Робота з графікою – одне з найулюбленіших моїх занять, тому коли мені запропонували виконати лабораторну роботу та описати, я погодився, незважаючи на те, що з OpenGL не працював більше 3 років. Сьогодні ми розглянемо одну з лаб, яку пропонують на третьому курсі факультету прикладної математики в МАІ, спецкурс «Комп'ютерна графіка». Я трохи ускладнив завдання, тож читай, буде цікаво.
Тема: Створення зображень за допомогою бібліотеки OpenGL. Завдання: Апроксимувати задану поверхню полігональною сіткою та засобами OpenGL забезпечити для неї
- можливість зображення в паралельній та перспективній проекції · можливість видалення невидимих ліній та поверхонь · можливість реалістичного освітлення · можливість каркасного зображення · можливість просторових афінних перетворень
Вихідні дані, що визначають поверхню, повинні зчитуватись з текстового файлу. Формат подання вихідних даних розробляється студентам самостійно. Залежно від номера студента надаються на вибір такі поверхні: Сфера з вирізами, Конус з вирізами, Циліндр з вирізами і т.д.
Що таке апроксимувати поверхню? Якщо подивитися на фігури, які нам пропонують, то видно, що вони мають форму з вигинами. Неможливо створити в комп'ютерній графіки сферу, можна лише малювати точки та лінії, а кола створюються за допомогою великої кількості ліній (тривимірні об'єкти з трикутників). Чим більше ліній, тим округлішою буде виходити форма об'єкта. Апроксимувати означає створити об'єкт максимально наближений до реального. А наскільки наближеним потрібно робити у цьому завданні? Гаразд, виберемо ступінь відповідності на своєрозсуд.
Дані необхідно завантажувати з файлу, але це ж серйозний недогляд. Відображення має відбуватися полігональним методом, тому яка різниця, які дані у файлі – сфера, циліндр чи пишні форми Памели Андерсон? Достатньо одному студенту виконати завдання, а всі інші повинні лише трохи змінити формат файлу та змінні у вихіднику, щоб ідентичність коду не впадала в очі. Після цього потрібно створити необхідну фігуру в 3DS Max, зберегти її у файлі і можна вважати, що завдання виконане. Ми ускладнимо завдання і генеруватимемо фігуру програмно. Вже третьому курсі факультету математики повинні вже вміти математично створити сферу чи циліндр.
Отже, давай напишемо програму, яка динамічно формуватиме сферу. Для початку створимо порожню Win32 програму і відразу ж додамо необхідні заголовні файли, а саме:
Перший заголовний файл підключає функції OpenGL, які ми повинні використовувати для відображення сфери. Другий рядок включає математичні функції. Так як сфера буде генеруватися, нам знадобляться тригонометричні функції math.h. Тепер йдемо у властивості проекту та властивості лінкеру в рядку Additional Dependencies додаємо бібліотеку opengl32.lib. Бібліотеку розширених функцій glu використовувати не будемо. Так, вона спростила б створення сфери, циліндра і т.д., але, судячи з завдання, ми не маємо права звертатися до неї.
Серед глобальних змінних нам знадобиться змінна hrc типу HGLRC, у якій зберігатимемо контекст малювання OpenGL.
Тепер рухаємося у функцію InitInstance, де створюється вікно. Після його створення додаємо код із лістингу 1. Давай розглянемо цей код, тому що його розуміння необхідне для виконання завдання.
Після отримання контексту малюваннявікна, ми повинні встановити формат пікселя. У даному випадку я використовую формат RGBA в 24 біти. Крім цього, підвищення швидкості включається подвійна буферизация (включений прапор PFD_DOUBLEBUFFER). У вихіднику, який ти знайдеш на компакт-диску, завдання формату пікселя я виніс в окрему функцію SetPixelFormat.
Далі йде створення контексту малювання OpenGL за допомогою функції wglCreateContext і робимо його поточним wglMakeCurrent. Це стандартна операція під час ініціалізації OpenGL. Тепер іде найцікавіше. У завданні є необхідність видалення невидимих ліній та поверхонь. Але як це зробити? Для OpenGL нічого складного тут немає, потрібно лише увімкнути тест глибини. Без нього виводитися всі поспіль і об'єкти, що знаходяться далі, можуть опинитися в одній позиції з найближчими об'єктами. Щоб увімкнути тест глибини пишемо: glEnable(GL_DEPTH_TEST);
Існує безліч варіантів тестів глибини і їх можна встановити з допомогою функції glDepthFunc. У цьому прикладі я використовую тест GL_LESS. Цей тест використовується за умовчанням, а які ще існують тести можеш дізнатися з файлу довідки функції glDepthFunc.
Висвітлення – це окрема пісня. У завданні сказано, що ми маємо забезпечити реалістичне висвітлення. Але як це зробити? Реалістичне висвітлення – це ціла наука. Існує безліч алгоритмів і методів, і OpenGL може практично все. Найреалістичніше, на мій погляд, освітлення можна отримати тільки з використанням вершинних шейдерів, але я сподіваюся, що укладачі завдання не пішли так далеко і не зіпсували нам життя, тому в даному прикладі я використовую освітлення, яке надається функціями OpenGL.
Отже, щоб у нашій сцені з'явилося джерело освітлення, необхідно створити джерело освітлення, вибрати модель та вказати його положення. УOpenGL перший пункт досить простий, тому що вже доступні джерела з іменами GL_LIGHTi, де i змінюється від 0 до GL_MAX_LIGHTS. У моєму заголовному файлі ця константа дорівнює 0x0D31. Я думаю, цього цілком достатньо.
Ми будемо використовувати одне джерело освітлення GL_LIGHT0. Щоб задати його положення, використовуємо наступний код:
У першому рядку задаємо масив із чотирьох чисел типу GLfloat, які визначать позицію джерела освітлення у нашому світі. У другому рядку викликаємо функцію glLightfv. Вона має три параметри: 1. Джерело освітлення; 2. Параметр, який потрібно змінити. Ми встановлюватимемо позицію світла, тому вказуємо константу GL_POSITION; 3. Вектор (масив із чотирьох значень), що задає позицію. Ця позиція буде трансформована у матрицю modelview нашого світу.
Якщо не встановити положення джерела світла, то за умовчанням буде використовуватися вектор (0,0,1,0). Можна ще задати тип лампочки - розсіяне світло, прожектор і т.д., але це вже справа смаку та кольору, а в завданні нам не сказали, якого саме потрібне світло.
Позицію лампочки задали, тепер необхідно увімкнути світло. Ні, для цього не потрібно викликати електрика, потрібно просто написати два наступні рядки:
У першому рядку ми дозволяємо освітлення взагалі, а у другому включаємо джерело освітлення GL_LIGHT0. Як ми вже знаємо, в OpenGL існує тонна і ще маленький вагончик наперед визначених лампочок, але щоб вони працювали, потрібно включити ті, які необхідні.
Щоб освітлення нормально працювало, бажано включити нормалізацію:
Незважаючи на те, що при побудові сфери я розраховуватиму нормаль ручками, ці два прапори включені, щоб ти знав про їхнє існування. Перший прапор дозволяє нормалізацію, а другий дозволяє це робити автоматично.
УЗавдання є необхідність надати можливість відображення фігури у вигляді каркаса. Як це зробити? Дуже просто. Наш полігон будуватиметься із зафарбованих трикутників. Щоб забрати забарвлення, можна додати наступний рядок:
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
Тут ми включаємо відображення передніх та задніх поверхонь полігону лініями. Тепер це не зафарбовані трикутники, а лінії, а отже, OpenGL намалює лише каркас.
Для роботи з паралельною проекцією використовується функція glOrtho:
void glOrtho ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far )
Щоб встановити перспективну проекцію, потрібно використовувати функцію gluPerspective:
void gluPerspective ( GLdouble angley, GLdouble aspect, GLdouble znear, GLdouble zfar )
Перший параметр – це кут огляду по осі Y. Другий параметр – співвідношення сторін, у більшості випадків цей параметр дорівнюють відношенню ширини вікна до висоти. Далі йдуть відстані до ближньої площини відсікання та дальньої.
Цілком логічним буде задавати параметри проекції щодо зміни розмірів вікна, адже від цього залежать і параметри відображення. Ідемо у функцію WndProc і додаємо туди обробник події WM_SIZE, код якого можна побачити в лістингу 2. У цьому прикладі використовуватимемо паралельне проектування. Щоб перетворити його на перспективу, заміни виклик glOrtho на наступний рядок:
gluPerspective (45.0f, width/height, 1.0f, 100.0f);
Перед встановленням необхідної проекції необхідно вибрати режим матриці GL_PROJECTION, а потім завантажити одиничну матрицю за допомогою функції glLoadIdentity().
Для відображення сцени за подією WM_PAINT додаємо виклик функції DrawScene. Цюфункцію я наводити тут не буду, тому що в ній відбувається генерація сфери математичним методом і вийшов досить невеликий код. Якби я завантажував сферу з файлу, то вийшло б не набагато менше, але адаптувати приклад не важко, якщо ти вмієш працювати з файлами.
Коли вивчатимеш вихідник, зверни увагу, що для кожного трикутника я визначаю нормалі, без яких освітлення стане неможливим. Просто сформувати полігон мало, необхідно нормалізувати його, щоб OpenGL знав, як має поводитися джерело освітлення.
Крім цього, об'єкту призначається матеріал. Залежно від матеріалу поверхні, змінюється та освітлення. Глянцеві поверхні повинні відкидати відблиски, а матові легко рівномірно розсіюють світло. Розмір відблиску також може відрізнятися залежно від поверхні.
Висвітлення – це ціла наука, і її неможливо вивчити в одній статті, але сподіваюсь, що наданий приклад допоможе тобі.
Вихідний код готового прикладу - це звичайно добре, але потрібно вміти ще пояснити цей код, а краще все ж таки знати предмет. Існує безліч варіантів тестів глибини, алгоритмів освітлень і якщо ти не знаєш теми, то знаючий викладач зможе легко поставити тебе в глухий кут. Так що якщо ти прогуляв цілий семестр і не знаєш OpenGL, купи книгу і надолужи втрачене. Це необхідно не тільки для складання іспитів, а й просто для себе, адже ми вчимося не заради оцінок, а заради знань, без яких після інституту диплом не вартий нічого.
Незважаючи на те, що ми описали рішення однієї з лаб, ми не виконуємо лабораторні за гроші і тим більше безкоштовно, тому з подібними питаннями прохання в мило не стукати. Я можу виконати роботу за тебе, але в мене є своя робота, дружина та діти, а твої знання коштують дорожче. наНаслідок хочу перефразувати одну чудову мудрість на сучасний лад: "вчення - гроші, слава і гідне життя, а не вчення - бідність, алкоголізм, а з нинішніми цінами на житло можна стати бомжем".