Продолжается цикл статей, посвященных работе с библиотекой 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;
Мы задали координаты и цвета вершин куба. Давайте для наглядности посмотрим на рисунок:
Номера вершин на рисунке полностью соответствуют тому, как мы их задали в нашей программе. Теперь, глядя на картинку можно с легкостью пройтись по всем 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);
Запускаем программу и видим наш куб:
Правда на данный момент он больше похож на квадрат ) Все потому что мы смотрим на куб со стороны оси 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.
Отличный курс. С интересом слежу за ним и думаю не только я. В дальнейшем планируется затрагивать тему редактирования объектов? Например, было бы интересно узнать, как при наведении мыши на грань куба подсветить ее, как цепляться за узел и перетаскивать его.
Раз интерес есть, то постараюсь описать )
Великолепная вводная в OpenGL - с благодарностью жду от автора продолжения. Однозначно респект.
Большое спасибо автору статьи.
Спасибо за хороший отзыв!
Здравствуйте, можно ли Вам задавать вопросы? У меня проблема с массивами данных. У меня задание - построить полигонами деталь. координаты и как соединить их в элементы заданы. Не знаю как задать массив нормалей , и почему строиться невероятно большая деталь, хотя координаты в пределах [0.5;2] . Вы можете что то посоветовать? Как менять масштаб например
На самом деле так сложно что-то конкретное и определенное сказать... Надо хотя бы код или пример полностью посмотреть.
Здравствуйте! У меня такая проблема: нужно научиться конвертировать любую плоскую фигуру в объёмную opengl c++. Не подскажете? Пжл
учисъ, изучай!
Спасибо, отличный мануал, вместе со всеми предидущими. Долго не мог разобраться с OpenGL, а тут буквально за пару вечеров понял основы.
Только я заметил одну вещь. Если сделать как у вас написано
xAxisRotation += (формула)
yAxisRotation += (формула)
кубик почему-то вращается совершенно непредсказуемым образом.
Если написать там просто "=" а не "+=" то всё работает как надо.
Спасибо за отличный отзыв!!!
Блин)
Я просто забыл
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
И так всё работает отлично)
Супер! )
Здравствуйте, у меня щас стоит такая задача рисовать фигуры в 3д через библиотеку glaux. Я скачал заголовочный файл. И даже библиотеку glaux.lib скачал. Но пишет на aux функции так:
Ошибка LNK2019 ссылка на неразрешенный внешний символ auxSolidSphere
Я может не туда поместил Lib файл, либо версии не совпадают. Как быть?
Приветствую ) Судя по ошибке .lib не подцепляется - идет обращение к функции, которая в хэдере описана, а само определение функции не находит.
Я пробовал и через прагму путь к lib файлу указывать и через настройки компоновщика, но ничего не идет(
Нашел способ рисовать 3д фигуры через glut, но текстуры накладываются через stb_image, с которым та же проблема. Хотя в stb_image.h есть все определения функций, но выводит такую же ошибку.
А проект на чем вообще в целом написан?
СУПЕР!