Top.Mail.Ru

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

Продолжается цикл статей, посвященных работе с библиотекой 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

18 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
sva
sva
10 лет назад

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

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

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

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

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

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

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

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

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

123
123
Ответ на комментарий  Анатолий
8 лет назад

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

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

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

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

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

Максим
Максим
Ответ на комментарий  Aveal
1 год назад

Здравствуйте, у меня щас стоит такая задача рисовать фигуры в 3д через библиотеку glaux. Я скачал заголовочный файл. И даже библиотеку glaux.lib скачал. Но пишет на aux функции так:
Ошибка   LNK2019   ссылка на неразрешенный внешний символ auxSolidSphere
Я может не туда поместил Lib файл, либо версии не совпадают. Как быть?

Максим
Максим
Ответ на комментарий  Aveal
1 год назад

Я пробовал и через прагму путь к lib файлу указывать и через настройки компоновщика, но ничего не идет(
Нашел способ рисовать 3д фигуры через glut, но текстуры накладываются через stb_image, с которым та же проблема. Хотя в stb_image.h есть все определения функций, но выводит такую же ошибку.

Александр
Александр
4 лет назад

СУПЕР!

18
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x