Уроки OpenGL. Часть 4. Создание 3D-объектов.

OpenGL

Продолжается цикл статей, посвященных работе с библиотекой OpenGL на нашем сайте, и, наконец, пришло время подключить третье измерение в наших экспериментах! Сегодня мы создадим в пространстве куб, а также добавим функцию вращения 3D-фигуры вокруг осей x и y. Как всегда разберем все по шагам, а в конце статьи я выложу полный проект с реализованным примером.

За основу давайте возьмем проект из предыдущего урока (ссылка), и ,как и тогда, для построения изображения мы сегодня будем использовать массивы вершин, индексов и цветов. Давайте разберем нашу задачу…

Что из себя представляет куб? Ответ прост – 6 граней (квадратов) и 8 вершин 🙂 Именно так мы и будем строить фигуру – построим по отдельности 6 его граней, а эта задача для нас уже не представляет никакой сложности. Но прежде, чем приступать к рисованию, добавим в функцию initializeGL() следующее:

glShadeModel(GL_FLAT);
glEnable(GL_CULL_FACE);

Помните на прошлом уроке, когда мы задавали разные цвета вершин у одной фигуры, то получали красивые переходы между цветами. Так вот сегодня нам это не нужно, грани будут иметь однородный цвет и именно для включения этого режима (а точнее для отключения режима сглаживания цветов) мы вызываем первую из этих функций. Цвет грани у нас теперь будет определяться цветом последней(!) нарисованной вершины квадрата.

Вторая функция устанавливает режим, когда строятся только внешние поверхности фигур. И вот тут есть один важный момент. Для корректного отображения объекта вершины в массиве вершин должны задаваться против(!) часовой стрелки.

Собственно, с приготовлениями на этом заканчиваем, начинаем отрисовку. Нам понадобятся три массива:

GLfloat cubeVertexArray[8][3];
GLfloat cubeColorArray[8][3];
GLubyte cubeIndexArray[6][4];

С размерами массивов мы разобрались на прошлом уроке, не буду повторяться, поэтому сразу же переходим к заполнению их данными. Кстати ребро нашего куба будет равно единице, в соответствии с этим задаем координаты вершин в трехмерном пространстве:

cubeVertexArray[0][0] = 0.0;
cubeVertexArray[0][1] = 0.0;
cubeVertexArray[0][2] = 1.0;

cubeVertexArray[1][0] = 0.0;
cubeVertexArray[1][1] = 1.0;
cubeVertexArray[1][2] = 1.0;

cubeVertexArray[2][0] = 1.0;
cubeVertexArray[2][1] = 1.0;
cubeVertexArray[2][2] = 1.0;

cubeVertexArray[3][0] = 1.0;
cubeVertexArray[3][1] = 0.0;
cubeVertexArray[3][2] = 1.0;

cubeVertexArray[4][0] = 0.0;
cubeVertexArray[4][1] = 0.0;
cubeVertexArray[4][2] = 0.0;

cubeVertexArray[5][0] = 0.0;
cubeVertexArray[5][1] = 1.0;
cubeVertexArray[5][2] = 0.0;

cubeVertexArray[6][0] = 1.0;
cubeVertexArray[6][1] = 1.0;
cubeVertexArray[6][2] = 0.0;

cubeVertexArray[7][0] = 1.0;
cubeVertexArray[7][1] = 0.0;
cubeVertexArray[7][2] = 0.0;

cubeColorArray[0][0] = 0.0;
cubeColorArray[0][1] = 0.0;
cubeColorArray[0][2] = 1.0;

cubeColorArray[1][0] = 0.6;
cubeColorArray[1][1] = 0.98;
cubeColorArray[1][2] = 0.6;

cubeColorArray[2][0] = 1.0;
cubeColorArray[2][1] = 0.84;
cubeColorArray[2][2] = 0.8;

cubeColorArray[3][0] = 0.8;
cubeColorArray[3][1] = 0.36;
cubeColorArray[3][2] = 0.36;

cubeColorArray[4][0] = 1.0;
cubeColorArray[4][1] = 0.27;
cubeColorArray[4][2] = 0.0;

cubeColorArray[5][0] = 0.82;
cubeColorArray[5][1] = 0.13;
cubeColorArray[5][2] = 0.56;

cubeColorArray[6][0] = 0.54;
cubeColorArray[6][1] = 0.17;
cubeColorArray[6][2] = 0.89;

cubeColorArray[7][0] = 0.0;
cubeColorArray[7][1] = 1.0;
cubeColorArray[7][2] = 1.0;

Мы задали координаты и цвета вершин куба. Давайте для наглядности посмотрим на рисунок:

Рисуем куб в OpenGL

Номера вершин на рисунке полностью соответствуют тому, как мы их задали в нашей программе. Теперь, глядя на картинку можно с легкостью пройтись по всем 6 граням куба и заполнить массив индексов. Давайте смотреть:

  • Грань 1 – вершины 0, 3, 2, 1
  • Грань 2 – вершины 0, 1, 5, 4
  • Грань 3 – вершины 7, 4, 5, 6
  • Грань 4 – вершины 3, 7, 6, 2
  • Грань 5 – вершины 1, 2, 6, 5
  • Грань 6 – вершины 0, 4, 7, 3

Теперь тоже самое делаем в программе:

cubeIndexArray[0][0] = 0;
cubeIndexArray[0][1] = 3;
cubeIndexArray[0][2] = 2;
cubeIndexArray[0][3] = 1;

cubeIndexArray[1][0] = 0;
cubeIndexArray[1][1] = 1;
cubeIndexArray[1][2] = 5;
cubeIndexArray[1][3] = 4;

cubeIndexArray[2][0] = 7;
cubeIndexArray[2][1] = 4;
cubeIndexArray[2][2] = 5;
cubeIndexArray[2][3] = 6;

cubeIndexArray[3][0] = 3;
cubeIndexArray[3][1] = 7;
cubeIndexArray[3][2] = 6;
cubeIndexArray[3][3] = 2;

cubeIndexArray[4][0] = 1;
cubeIndexArray[4][1] = 2;
cubeIndexArray[4][2] = 6;
cubeIndexArray[4][3] = 5;

cubeIndexArray[5][0] = 0;
cubeIndexArray[5][1] = 4;
cubeIndexArray[5][2] = 7;
cubeIndexArray[5][3] = 3;

Теперь осталось только вызвать функцию рисования:

glVertexPointer(3, GL_FLOAT, 0, cubeVertexArray);
glColorPointer(3, GL_FLOAT, 0, cubeColorArray);
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, cubeIndexArray);

Запускаем программу и видим наш куб!

3D изображение в OpenGL

Правда на данный момент он больше похож на квадрат 🙂 Все потому что мы смотрим на куб со стороны оси z и видим его проекцию на плоскость X0Y. Для того, чтобы все-таки увидеть, что это действительно куб, давайте добавим возможность вращать фигуру при удержании кнопки мыши и перемещении ее указателя.

Сделаем так – при перемещении указателя мыши от левого края до правого будем поворачивать куб на 180 градусов. Осталось вычислить, на сколько градусов нужно повернуть фигуру при произвольном перемещении курсора. С этим все просто, получаем формулу:

yAngle = 180 * d / w;

Здесь d – это расстояние, на которое мы переместили курсор, w – ширина нашего окна. Обратите внимание, что при движении мыши вдоль оси x поворот будет осуществляться вокруг оси y, и наоборот, перемещение вдоль y – поворот вокруг x. Итак, сейчас нам нужно получить в программе данные о перемещениях мыши. Для этого переопределим методы:

void MainScene::mousePressEvent(QMouseEvent *event)
{
	pressPosition = event->pos();
}

void MainScene::mouseMoveEvent(QMouseEvent *event)
{
	xAxisRotation += (180 * ((GLfloat)event->y() - (GLfloat)pressPosition.y())) / (currentHeight);
	yAxisRotation += (180 * ((GLfloat)event->x() - (GLfloat)pressPosition.x())) / (currentWidth);

	pressPosition = event->pos();

	updateGL();
}

При нажатии кнопки мыши ( mousePressEvent() ) сохраняем в переменную pressPosition текущие координаты курсора. При перемещениях указателя производим расчет углов поворота, на которые необходимо развернуть куб, а затем вызываем функцию updateGL() для перерисовки сцены в соответствии с полученными данными. Не забываем объявить все используемые методы и переменные в файле MainScene.h.

Функцию updateGL() то мы вызвали, но само собой ничего, естественно, не повернется 🙂 Для поворота 3D-фигуры OpenGL предлагает нам следующую функцию:

glRotatef(angle, x, y, z);

Здесь первый параметр – угол поворота, а три остальных определяют, вокруг какой из осей необходимо выполнить вращение. То есть если мы сделаем так:

glRotatef(45, 1, 0, 0);

То фигура повернется вокруг оси x на 45 градусов.

С этим все понятно, осталось только добавить вызов этой функций к нам в программу, а точнее в функцию paintGL():

void MainScene::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	glRotatef(yAxisRotation, 0.0, 1.0, 0.0);
	glRotatef(xAxisRotation, 1.0, 0.0, 0.0);
	
	// Далее продолжается функция paintGL()
	.......................................

Как видите, все оказалось довольно-таки просто!

Теперь нам предстоит собрать проект и проверить результат:

На этом заканчиваем сегодняшнюю статью! А уже скоро, а именно в следующем уроке, мы разберемся с текстурами в OpenGL – создадим собственные текстуры и нанесем их на 3D-объекты, так что до скорого, не пропустите новую статью!

Как и обещал прилагаю архив с полным проектом этого примера: OpenGLTest_Cube.

Поделиться!

Подписаться
Уведомление о
guest
14 Комментарий
старее
новее большинство голосов
Inline Feedbacks
View all comments
sva
sva
5 лет назад

Отличный курс. С интересом слежу за ним и думаю не только я. В дальнейшем планируется затрагивать тему редактирования объектов? Например, было бы интересно узнать, как при наведении мыши на грань куба подсветить ее, как цепляться за узел и перетаскивать его.

Олег
Олег
5 лет назад

Великолепная вводная в OpenGL – с благодарностью жду от автора продолжения. Однозначно респект.

Иришка
Иришка
4 лет назад

Большое спасибо автору статьи.

Иришка
Иришка
4 лет назад

Здравствуйте, можно ли Вам задавать вопросы? У меня проблема с массивами данных. У меня задание – построить полигонами деталь. координаты и как соединить их в элементы заданы. Не знаю как задать массив нормалей , и почему строиться невероятно большая деталь, хотя координаты в пределах [0.5;2] . Вы можете что то посоветовать? Как менять масштаб например

Анатолий
4 лет назад

Здравствуйте! У меня такая проблема: нужно научиться конвертировать любую плоскую фигуру в объёмную opengl c++. Не подскажете? Пжл

123
123
Reply to  Анатолий
4 лет назад

учисъ, изучай!

Геннадий
2 лет назад

Спасибо, отличный мануал, вместе со всеми предидущими. Долго не мог разобраться с OpenGL, а тут буквально за пару вечеров понял основы.
Только я заметил одну вещь. Если сделать как у вас написано
xAxisRotation += (формула)
yAxisRotation += (формула)
кубик почему-то вращается совершенно непредсказуемым образом.
Если написать там просто “=” а не “+=” то всё работает как надо.

Геннадий
2 лет назад

Блин)
Я просто забыл
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
И так всё работает отлично)

Александр
Александр
12 минут назад

СУПЕР!

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Октябрь 2020
Пн Вт Ср Чт Пт Сб Вс
 1234
567891011
12131415161718
19202122232425
262728293031  

© 2013-2020 MicroTechnics.ru