Усім привіт.
У підтримку попередньої статті по бібліотекам FreeGLUT і OpenGL http://www.kytok.org.ua/post/freeglut-and-opengl-lehkyj-start (обов'язково відвідай дану статтю в якій описується як встановити необхідні пакунки щоб запустити проект) пропоную розглянути ще один невеликий приклад по відмальовуванню куба з текстурою.
Даний приклад розміщений у тому-ж репозиторію що і ресурси попередньої статті за адресою https://github.com/yuriysydor1991/freeglut-fast-start-article-resources
Сам приклад розміщений у декількох файлах, а саме для головної функції програми за https://github.com/yuriysydor1991/freeglut-fast-start-article-resources/blob/main/vertex-cube-window/main.cppПриклад
Ось головний код який розміщений у прикладі:
/* * Copyright (c) 2024 Yurii Sydor (yuriysydor1991@gmail.com) kytok.org.ua * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ // системні підключення #include <chrono> // головні заголовкові файли GLUT і OpenGL #include <GL/gl.h> #include <GL/glu.h> #include <GL/glut.h> // заголовкові файли проекту #include "cube_raw_texture.h" // деякі константи // ширини і висоти вікна за замовчуванням constexpr const unsigned int W_DEFAULT_WIDTH = 500; constexpr const unsigned int W_DEFAULT_HEIGHT = 500; long int getMillisecondsTime(); // глобальна змінна для обертання бублика // градуси обертання бублика long int spin = 0; long int spin2 = 0; // змінна для зберігання часу пройденого від // останнього намальованого кадру. long int timestart = getMillisecondsTime(); // ідентифікатор текстури куба unsigned int ctext{0}; // функція яка повертає поточні мілісекунди пройденого часу long int getMillisecondsTime() { // отримуємо поточний час auto currentTime = std::chrono::system_clock::now(); // Перетворюємо поточний час у мілісекунди від початку відліку auto currentTimeMillis = std::chrono::time_point_cast<std::chrono::milliseconds>currentTime); // отримуємо значення мілісекунд return currentTimeMillis.time_since_epoch().count(); } // процедура перераховування обертання фігури в залежності від часу void recalculateSpin() { // отримуємо поточний час у локальну змінну auto ctime = getMillisecondsTime(); // обраховуємо пройдений час від попереднього кадру auto deltaTime = ctime - timestart; // якщо змінити дільник тоді зміниться швидкість обертання spin += deltaTime / 10; spin2 += deltaTime / 10; // обрізаємо значення обертань у проміжок [0,360] if (spin > 360) { spin -= 360; } if (spin2 > 360) { spin2 -= 360; } // зберігаємо поточне значення часу timestart = ctime; } // функція ініціалізації параметрів OpenGL void init() { // встановлюємо колір очищення пікселів екрану // детальніше про функцію у http://www.kytok.org.ua/post/glclearcolor? glClearColor (0.0, 0.0, 0.0, 1.0); // Детальніше про функцію glEnable у статті http://www.kytok.org.ua/post/glenable // вмикаємо розпізнавання глибини glEnable(GL_DEPTH_TEST); // вмикаємо текстурування glEnable(GL_TEXTURE_2D); // Генеруємо ідентифікатор текстури в кількості 1 штуки // Детальніше про дану функцію у статті http://www.kytok.org.ua/post/glgentextures glGenTextures(1, &ctext) ; // Робимо її поточною // Детальніше про функцію glBindTexture за посиланням http://www.kytok.org.ua/post/glbindtexture glBindTexture(GL_TEXTURE_2D, ctext) ; // 2d texture (x and y size) // встановлюємо деякі корисні параметри текстури glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Вказуємо на безпосередні дані зображення текстури // 2D текстура, рівень деталізації 0 (нормальний), 4-ри компоненти (червоний, зелений, синій і прозорість) // ширина і висота зображення (1024х1024), границя 0, RGBA кольорові дані, unsigned char тип і // в кінці самі дані текстури. // Детальніше про дану функцію у статті http://www.kytok.org.ua/post/glteximage2d glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, rawTexture::rawTextureWidth, rawTexture::rawTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, rawTexture::rawTextureData); } // Процедура відображення OpenGL сцени void display() { // очищуємо буфер пікселів екрану // раніше встановленим за допомогою glClearColor // Детальніше про дану функцію у статті http://www.kytok.org.ua/post/glclear glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // зберігаємо поточну матрицю у стеку матриць // і дублюємо її для редагування // Детальніше про дану функцію http://www.kytok.org.ua/post/glpushmatrix-glpopmatrix glPushMatrix(); // вмикаємо нашу єдину текстуру куба // Детальніше про функцію glBindTexture за посиланням http://www.kytok.org.ua/post/glbindtexture glBindTexture (GL_TEXTURE_2D, ctext); // обертаємо поточну матрицю // Деталі за посиланням http://www.kytok.org.ua/post/glrotate glRotatef(spin, 0.0, 1.0, 0.0); // також обертаємо навколо X вісі glRotatef(spin2, 1.0, 0.0, 0.0); // Розпочинаємо малювання трихвимірного об'єкта // який складається з квадратів // Деталі за посиланням http://www.kytok.org.ua/post/glbegin glBegin(GL_QUADS); // використовуємо функцію OpenGL glVertex3f для відмальовування куба // кожна трійка чисел це координати граней куба у 3D просторі. // кожні 4 виклики glVertex3f це один квадрат, з якого складається куб. // Детальніше про glVertex3f за посиланням http://www.kytok.org.ua/post/glvertex // Функція glTexCoord2f вказує на координати точки початку на текстурі // Детальніше про glTexCoord2f у статті-перекладі http://www.kytok.org.ua/post/gltexcoord glTexCoord2f(0.0138,0.3196); glVertex3f(100, -100, -100); glTexCoord2f(0.3196,0.3196); glVertex3f(100, -100, 100); glTexCoord2f(0.3196,0.0138); glVertex3f( -100, -100, 100); glTexCoord2f(0.0138,0.0138); glVertex3f( -100, -100, -100); glTexCoord2f(0.6529,0.6529); glVertex3f(100, 100, -100); glTexCoord2f(0.6529,0.3471); glVertex3f( -100, 100, -100); glTexCoord2f(0.3471,0.3471); glVertex3f( -100, 100, 100); glTexCoord2f(0.3471,0.6529); glVertex3f(100, 100, 100); glTexCoord2f(0.6529,0.6804); glVertex3f(100, -100, -100); glTexCoord2f(0.3471,0.6804); glVertex3f(100, 100, -100); glTexCoord2f(0.3471,0.9862); glVertex3f(100, 100, 100); glTexCoord2f(0.6529,0.9862); glVertex3f(100, -100, 100); glTexCoord2f(0.0138,0.6804); glVertex3f(100, -100, 100); glTexCoord2f(0.0138,0.9862); glVertex3f(100, 100, 100); glTexCoord2f(0.3196,0.9862); glVertex3f( -100, 100, 100); glTexCoord2f(0.3196,0.6804); glVertex3f( -100, -100, 100); glTexCoord2f(0.9862,0.6804); glVertex3f( -100, -100, 100); glTexCoord2f(0.6804,0.6804); glVertex3f( -100, 100, 100); glTexCoord2f(0.6804,0.9862); glVertex3f( -100, 100, -100); glTexCoord2f(0.9862,0.9862); glVertex3f( -100, -100, -100); glTexCoord2f(0.0138,0.6529); glVertex3f(100, 100, -100); glTexCoord2f(0.3196,0.6529); glVertex3f(100, -100, -100); glTexCoord2f(0.3196,0.3471); glVertex3f( -100, -100, -100); glTexCoord2f(0.0138,0.3471); glVertex3f( -100, 100, -100); // завершуємо вказування координат трьохвимірного об'єкту // Деталі за посиланням http://www.kytok.org.ua/post/glbegin glEnd(); // витягуємо оригінальну матрицю зі стеку матриць // Детальніше про дану функцію http://www.kytok.org.ua/post/glpushmatrix-glpopmatrix glPopMatrix(); // підміняємо буфер для нових команд glutSwapBuffers(); // перераховуємо кут обертання фігури в залежності від часу // пройденого між кадрами recalculateSpin(); // примусово перемальовуємо екран glutPostRedisplay(); } // Процедура зміни розміру вікна void reshape(int w, int h) { // встановлюємо розмір вікна OpenGL // Детальніше про дану функцію за посиланням http://www.kytok.org.ua/post/glviewport glViewport (0, 0, (GLsizei) w, (GLsizei) h); // встановлюжмо режим матриці у GL_PROJECTION // Детальніше про дану функцію за посиланням http://www.kytok.org.ua/post/glmatrixmode glMatrixMode(GL_PROJECTION); // Встановлюємо матрицю ідентичності // Детальніше про дану функцію за посиланням http://www.kytok.org.ua/post/glloadidentity glLoadIdentity(); // Встановлюємо параметри проекції // Детальніше про дану функцію за посиланням http://www.kytok.org.ua/post/glortho glOrtho(-w/2.f, w/2.f, -h/2.f, h/2.f, -1000.0, 1000.0); // Встановлюємо режим огляду моделі glMatrixMode(GL_MODELVIEW); // завантажуємо матрицю ідентичності // Детальніше про дану функцію за посиланням http://www.kytok.org.ua/post/glloadidentity glLoadIdentity(); } // Головна функція програми int main(int argc, char** argv) { // Ініціалізація FreeGLUT бібліотеки glutInit(&argc, argv); // Встановлення параметрів відображення // для подвійної буферизації і RGB кольорової схеми glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); // Встановлення розмірів вікна glutInitWindowSize (W_DEFAULT_WIDTH, W_DEFAULT_HEIGHT); // Створення вікна з назвою у якості шляху до виконуваного файлу glutCreateWindow (argv[0]); // Виклик процедури ініціалізації init (); // Встановлення головних процедур // для відображення схеми glutDisplayFunc(display); // для зміни розміру вікна glutReshapeFunc(reshape); // Головний цикл FreeGLUT бібліотеки // у якому обробляються події // і відбувається інша магія glutMainLoop(); return 0; }
Через те що файл з даними текстури куба займає більше 18 Мб і має більше мільйона рядків даних пікселів текстури не включив у дану сторінку. Їх можна переглянути на веб-інтерфейсі GitHub або, якщо його витримає, у текстовому редакторі. Дані текстури не стиснені жодним чином тому і займають такий розмір.
Через ускладнювання читання текстури з, наприклад, PNG-файлу і було обрано звичайний масив даних для текстури, що набагато спрощує код прикладу. Тому можна сконцентруватись на FreeGLUT/OpenGL замість додаткового коду читання з PNG. Крім того для прикладу залежності також не бажані.
Щоб зібрати даний проект необхідно виконати наступні команди:
git clone https://github.com/yuriysydor1991/freeglut-fast-start-article-resources.git cd freeglut-fast-start-article-resources mkdir -vp build cd build cmake ../ cmake --build vertex-cube-window ./vertex-cube-window/vertex-cube-window
Вивід даних команд у командному рядку Linux може виглядати наступним чином:
Після запуску програми можна отримати наступний результат:
Висновок
Текстурування об'єктів надає більш кращий досвід перегляду 3D сцени.
Хоча даний приклад і складається в основному з одного файлу, для реальних програм необхідно виконувати декомпозицію коду по класам і методам. Тут це зроблено для простоти і крім того коду вже і не так багато (якщо не враховувати мільйон рядків коду для текстури).
Незважаючи на те, що в даному прикладі малювання виконується ніби в ручну, насправді даний куб був створений у програмі 3-вимірного моделювання Blender. Програмний комплекс Blender можна завантажити за посиланням домашньої сторінки https://www.blender.org/. Не потрібно намагатись створювати складні фігури вручну.