Урок 5. Создание фигур в 3D

Продолжая последний урок, мы сделаем объект, как ИСТИННЫЙ трехмерный объект, а не 2D объекты в 3D мире. Мы будем делать это добавлением с левой, задней и правой сторон треугольника, и с левой, правой, верхней и нижней сторон квадрата. Сделав это, мы превращаем треугольник в пирамиду с четырьмя гранями и квадрат в куб.

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

GLvoid DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Очистка экрана и буфера глубины
glLoadIdentity();                               // Сброс просмотра
glTranslatef(-1.5f,0.0f,-6.0f);                 // Сдвиг влево и вглубь экрана
glRotatef(rtri,0.0f,1.0f,0.0f);                 // Вращение пирамиды по оси Y
glBegin(GL_TRIANGLES);                          // Начало рисования пирамиды

Некоторые из Вас взяли код из последнего урока и сделали свои собственные 3D объекты. Вот один вопрос, который вы задали : "как сделать, чтобы мои объекты не вращались по своим осям? Потому что кажется, что они вращаются на весь экран". Чтобы объект вращался вокруг оси, он должен быть разработан для вращения ВОКРУГ оси. Вы должны помнить, что центр любого объекта должен быть в 0 для X, 0 для Y, 0 для Z.

Следующий код создаст пирамиду вокруг центральной оси. Верх пирамиды на единицу выше центра, низ пирамиды на единицу ниже центра. Верхняя точка как раз в середине (ноль), а нижние точки одна слева от центра, а одна справа от центра.

Отмечу, что все треугольники рисуются с вращением против часовой стрелки. Это важно, и будет объяснено в следующих уроках, сейчас, только запомните, что отличной привычкой будет делать объекты или по часовой или против часовой стрелки, и вы не должны смешивать эти два метода, если на то нет веской причины.

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

        glColor3f(1.0f,0.0f,0.0f);                      // Красный
        glVertex3f( 0.0f, 1.0f, 0.0f);                  // Верх треугольника (Передняя)
        glColor3f(0.0f,1.0f,0.0f);                      // Зеленный
        glVertex3f(-1.0f,-1.0f, 1.0f);                  // Левая точка
        glColor3f(0.0f,0.0f,1.0f);                      // Синий
        glVertex3f( 1.0f,-1.0f, 1.0f);                  // Правая точка

Сейчас мы нарисуем правую грань. Отметим, что две нижних точки нарисованы на единицу справа от центра, верхняя точка нарисована на единицу выше оси Y, и справа от середины оси X. Поэтому эта грань имеет наклон от центральной точки сверху вниз с правой стороны.

Замечу, что левая точка нарисована синим цветом в этот раз. Так как она нарисована синей, это будет тот же самый цвет, какой у точки правого нижнего угла лицевой грани. Градиент синего цвета идет от одного угла вдоль лицевой и правой граней пирамиды.

Замечу, что все четыре грани включены внутрь тех же самых glBegin(GL_TRIANGLES) и glEnd(), словно одна сторона. Поскольку мы делаем целый объект из треугольников, OpenGL знает, что каждые три точки мы рисуем как три точки одного треугольника. Треугольник рисуется из трех точек, если больше трех точек, то OpenGL поймет, что надо рисовать другой треугольник. Если вы выведете четыре точки вместо трех, OpenGL будет рисовать первые три точки и примет четвертую точку как начальную точку нового треугольника. Но не будет нарисован Четырехугольник. Поэтому проверьте, что вы не добавили любые дополнительные точки нечаяно.

        glColor3f(1.0f,0.0f,0.0f);                      // Красная
        glVertex3f( 0.0f, 1.0f, 0.0f);                  // Верх треугольника (Правая)
        glColor3f(0.0f,0.0f,1.0f);                      // Синия
        glVertex3f( 1.0f,-1.0f, 1.0f);                  // Лево треугольника (Правая)
        glColor3f(0.0f,1.0f,0.0f);                      // Зеленная
        glVertex3f( 1.0f,-1.0f, -1.0f);                 // Право треугольника (Правая)

Сейчас задняя сторона. Снова переключим цвета. Левая точка – зеленного цвета, поскольку этот угол так же и зеленный угол правой грани.

        glColor3f(1.0f,0.0f,0.0f);                      // Красный
        glVertex3f( 0.0f, 1.0f, 0.0f);                  // Низ треугольника (Сзади)
        glColor3f(0.0f,1.0f,0.0f);                      // Зеленный
        glVertex3f( 1.0f,-1.0f, -1.0f);                 // Лево треугольника (Сзади)
        glColor3f(0.0f,0.0f,1.0f);                      // Синий
        glVertex3f(-1.0f,-1.0f, -1.0f);                 // Право треугольника (Сзади)

В завершении мы рисуем левую грань. Цвета переключаются в последний раз. Левая точка синего цвета, и смешивается с правой точкой на задней грани. Правая точка зеленного цвета, и смешивается с левой точкой на передней грани.

Мы закончили рисовать пирамиду. Поскольку пирамида только крутиться вдоль оси Y, мы не когда не увидим низ, поэтому нет необходимости выводить низ пирамиды. Если вы чувствуете потребность в экспериментах, попробуйте добавить низ используя четырехугольник, тогда вращайте по оси X для того чтобы увидеть низ, если конечно вы сделали все корректно. Проверьте, что цвет каждого угла в четырехугольнике совпадает с цветом, который использован в каждом из четырех углов пирамиды.

        glColor3f(1.0f,0.0f,0.0f);                      // Красный
        glVertex3f( 0.0f, 1.0f, 0.0f);                  // Верх треугольника (Лево)
        glColor3f(0.0f,0.0f,1.0f);                      // Синий
        glVertex3f(-1.0f,-1.0f,-1.0f);                  // Лево треугольника (Лево)
        glColor3f(0.0f,1.0f,0.0f);                      // Зеленный
        glVertex3f(-1.0f,-1.0f, 1.0f);                  // Право треугольника (Лево)
glEnd();                                                // Кончили рисовать пирамиду

Сейчас мы будем рисовать куб. Чтобы сделать это надо шесть квадратов. Все квадраты рисуются против часовой стрелке. Примем, что первая точка справа вверху, вторая точка слева вверху, третья точка слева внизу, и последняя слева внизу. Когда мы рисуем заднюю грань, то будет казаться, что мы рисуем по часовой стрелке, но вы помните, что если мы были позади куба и смотрели на него, то левая сторона экрана, фактически была бы с правой стороны квадрата, и правая сторона экрана была бы фактически с левой стороны квадрата.

Замечу, что мы сдвинули куб немного вглубь экрана в этом уроке. Поэтому размер куба будет казаться меньше размера пирамиды. Если мы переместили бы куб на 6 единиц к экрану, то куб будет казаться больше чем пирамида, и часть куба будет за пределами экрана. Вы можете поиграться с этим настройками, и сдвинув куб дальше от экрана он будет казаться меньше, а придвинув к экрану он будет казаться больше. Это происходит из-за переспективы. Объекты на расстоянии кажутся меньше :).

glLoadIdentity();
glTranslatef(1.5f,0.0f,-7.0f);          // Сдвинуть вправо и вглубь экрана
glRotatef(rquad,1.0f,1.0f,1.0f);        // Вращение куба по X, Y & Z
glBegin(GL_QUADS);                      // Рисуем куб

Мы начнем рисовать куб сверху. Мы сдвигаемся на одну единицу от центра куба. Отметим, что по оси Y всегда единица. Затем мы рисуем квадрат на Z плоскости. Мы начинаем рисовать с правой точки вверху экрана. Правая верхняя точка должна быть на одну единицу справа, и на одну единицу вглубь экрана. Вторая точка будет на одну единицу влево и на единицу вглубь экрана. Сейчас мы нарисуем ту часть квадрата, которая ближе к зрителю. Поэтому для того чтобы сделать это, вместо смещения вглубь экрана, мы сдвигаемся на одну единицу к экрану. Улавливаете смысл?

        glColor3f(0.0f,1.0f,0.0f);              // Синий
        glVertex3f( 1.0f, 1.0f,-1.0f);          // Право верх квадрата (Верх)
        glVertex3f(-1.0f, 1.0f,-1.0f);          // Лево верх
        glVertex3f(-1.0f, 1.0f, 1.0f);          // Лево низ
        glVertex3f( 1.0f, 1.0f, 1.0f);          // Право низ

Нижняя часть квадрата рисуется таким же образом, как и верхняя, но поскольку это низ, сдвигаемся вниз на одну единицу от центра куба. Замечу, что ось Y всегда минус единица. Если мы окажемся под кубом, и взглянем на квадрат, который снизу, вы заметите, что правый верхний угол – это угол ближний к зрителю. Поэтому вместо того чтобы рисовать дальше от зрителя в начале, мы рисуем ближе к зрителю, тогда левая сторона ближе к зрителю. И затем мы движемся вглубь экрана, для того чтобы нарисовать дальние две точки.

Если Вы действительно не заботитесь о порядке рисования полигонов (по часовой или против), вы должны скопировать код для верхнего квадрата, сдвинуть вниз по оси Y на единицу, и это будет работать, но игнорируя порядок рисования квадрата можно получить неверный результат, если вы захотите сделать, например, наложение текстуры.

        glColor3f(1.0f,0.5f,0.0f);              // Оранжевый
        glVertex3f( 1.0f,-1.0f, 1.0f);          // Верх право квадрата (Низ)
        glVertex3f(-1.0f,-1.0f, 1.0f);          // Верх лево
        glVertex3f(-1.0f,-1.0f,-1.0f);          // Низ лево
        glVertex3f( 1.0f,-1.0f,-1.0f);          // Низ право

Сейчас мы рисуем передний квадрат. Мы сдвигаемся на одну единицу ближе к экрану, и дальше от центра для того чтобы нарисовать переднею грань. Заметим, что ось Z всегда равна единице. В гранях пирамиды ось Z не всегда единица. Вверху, ось Z равна нулю. Если Вы попробуете установить ось Z равной нулю в привиденом ниже коде, вы увидите, что угол, который вы изменили наклонен к экрану. Но это не то, что мы хотим сейчас сделать ;).

        glColor3f(1.0f,0.0f,0.0f);              // Красный
        glVertex3f( 1.0f, 1.0f, 1.0f);          // Верх право квадрата (Перед)
        glVertex3f(-1.0f, 1.0f, 1.0f);          // Верх лево
        glVertex3f(-1.0f,-1.0f, 1.0f);          // Низ лево
        glVertex3f( 1.0f,-1.0f, 1.0f);          // Низ право

Задняя грань квадрата такая же ка передняя грань, но сдвинута вглубь экрана. Отметим, что ось Z всегда минус один во всех точках.

        glColor3f(1.0f,1.0f,0.0f);              // Желтый
        glVertex3f( 1.0f,-1.0f,-1.0f);          // Верх право квадрата (Зад)
        glVertex3f(-1.0f,-1.0f,-1.0f);          // Верх лево
        glVertex3f(-1.0f, 1.0f,-1.0f);          // Низ лево
        glVertex3f( 1.0f, 1.0f,-1.0f);          // Низ право

Сейчас нам осталось нарисовать только два квадрата. Как вы уже успели заметить одна ось всегда имеет тоже самое значение у всех точек квадрата. В этом случае ось X всегда равна минус один. Поскольку мы рисуем левую грань.

        glColor3f(0.0f,0.0f,1.0f);              // Синий
        glVertex3f(-1.0f, 1.0f, 1.0f);          // Верх право квадрата (Лево)
        glVertex3f(-1.0f, 1.0f,-1.0f);          // Верх лево
        glVertex3f(-1.0f,-1.0f,-1.0f);          // Низ лево
        glVertex3f(-1.0f,-1.0f, 1.0f);          // Низ право

И последняя грань завершит куб. Для нее ось X всегда равна единице. Рисуем против часовой стрелки. Если вы хотите, то вы можете не рисовать эту грань и получите коробку ;).

Если вы хотите поэксперементировать, вы всегда можете изменить цвет каждой точки в кубе для того чтобы сделать градиент, как в пирамиде. Вы можете посмотреть пример интерполяционной заливки куба скопировав первую демонстрацию Evil с моего сайта. Запустите ее и нажмите TAB. Вы увидите чудесный цветной куб, с изменяющимися цветами вдоль всех граней.

        glColor3f(1.0f,0.0f,1.0f);              // Фиолетовый
        glVertex3f( 1.0f, 1.0f,-1.0f);          // Верх право квадрата (Право)
        glVertex3f( 1.0f, 1.0f, 1.0f);          // Верх лево
        glVertex3f( 1.0f,-1.0f, 1.0f);          // Низ лево
        glVertex3f( 1.0f,-1.0f,-1.0f);          // Низ право
        glEnd();                                // Закончили квадраты

rtri+=0.2f;             // Увеличим переменную вращения для треугольника
rquad-=0.15f;           // Уменьшим переменную вращения для квадрата
}

В конце этого урока, вы должны лучше понимать как объекты создаются в 3D пространстве. Вы должны представлять экран OpenGL, как гиганская милиметровка, с множеством прозрачных слоев за ней. Это похоже на гигантский куб сделаный из точек. Некоторые точки двигаются слева направо, некоторые двигаются верх и вниз, и некоторые точки двигаются взад и вперед в кубе. Если вы может представить глубину экрана, вы не будете иметь проблем с разработкой новых 3D объектов.

Если вы с трудом понимаете 3D пространство, то это не бесполезно. Это может быть сложным только вначале. Объекты подобные кубу хороший пример для обучения. Если вы заметили задняя грань рисуется также как передняя грань, только дальше от экрана. Поиграйте с этим кодом, и если вы не можете понять это, спросите меня, и я вам отвечу.

© Jeff Molofee (NeHe)

 12 сентября 2001 (c)  Сергей Анисимов