Усім привіт.
У підтримку попередньої статті по бібліотекам 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/. Не потрібно намагатись створювати складні фігури вручну.