|
Июн
22
|
До сих пор мы с успехом осваивали плоские двухмерные объекты. Пришло время переходить к объемным — трехмерным. В принципе, они не намного сложнее, зато у них гораздо больше вершин (если создавать их из массива вершин) и трансформаций (если пытаться квадрат превратить в куб).
Наверное, сначала стоило рассмотреть точки и линии, но раз уж мы уже наложили текстуру на квадрат и раскрасили треугольники, будем скорее переходить к более интересным формам!
Вернемся мы еще и к трансформациям, и к вращению, рассмотрев их в деталях. Даже материалы для новичков мы еще не успели освоить в полном объеме… Так что впереди и нас еще множество уроков!!
Для начала урезаем метод “drawView”
Попрощайтесь с утомительным кодированием: убираем все лишнее и возвращаем метод “drawView” в первоначальное состояние.
Откорректируйте метод “drawView” как показано ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | - (void)drawView { // Сюда новый код определения объекта [EAGLContext setCurrentContext:context]; glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); glViewport(0, 0, backingWidth, backingHeight); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); // Сюда новый код рисования glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); [context presentRenderbuffer:GL_RENDERBUFFER_OES]; [self checkGLError:NO]; } |
Можете сказать спасибо за столь оперативный переход к трехмерному пространству вместо анализа буфера глубины. Думаю, приведенный выше код в общих чертах всем уже хорошо знаком.
Определение трехмерного объекта
Мы займемся построением куба — всем знакомого объекта, который часто встречается в трехмерной графике и отлично смотрится при вращении с наложенными текстурами.
Прежде чем приступать к рисованию, вспомним, что объемный куб представляет собой 6 уже привычных для нас квадратов. Все просто, но все равно подробно проанализируем его определение. Начнем с передней грани:
1 2 3 4 5 6 7 | const GLfloat cubeVertices[] = { // Определяем переднюю грань -1.0, 1.0, 1.0, // верхняя левая -1.0, -1.0, 1.0, // нижняя левая 1.0, -1.0, 1.0, // нижняя правая 1.0, 1.0, 1.0, // верхняя правая |
Все практически то же, что и в исходном квадрате за исключением того, что переднюю грань я придвинул вперед на один пункт. В конце урока расскажу, зачем это было нужно. Теперь переходим к верхней грани:
1 2 3 4 5 | // Верхняя грань -1.0, 1.0, -1.0, // верхняя левая (сбоку) -1.0, 1.0, 1.0, // нижняя левая (спереди) 1.0, 1.0, 1.0, // нижняя правая (спереди) 1.0, 1.0, -1.0, // верхняя правая (сбоку) |
Обратите внимание: верхнюю грань я рисую не только в том же направлении, что и переднюю (указывая вершины против часовой стрелки, если смотреть на куб не прямо, а с боковой грани), но и начинаю с той же точки. Если готовый куб повернуть на 90º вдоль оси Х, чтобы верхняя грань стала передней, то первой вершиной задана верхняя левая, а за ней — верхняя правая.
Переходим к боковой грани:
1 2 3 4 5 | // Боковая грань 1.0, 1.0, -1.0, // верхняя правая (если смотреть прямо) 1.0, -1.0, -1.0, // нижняя правая -1.0, -1.0, -1.0, // нижняя левая -1.0, 1.0, -1.0, // верхняя левая |
Заметьте, что мы продолжаем соблюдать порядок и начальную точку отсчета вершин. По тому же принципу работаем и с оставшимися гранями:
1 2 3 4 5 | // Нижняя грань -1.0, -1.0, 1.0, // верхняя левая передняя 1.0, -1.0, 1.0, // правая передняя 1.0, -1.0, -1.0, // правая боковая -1.0, -1.0, -1.0, // левая передняя |
И вновь я придерживаюсь того же порядка и начальной точки. Мысленно поверните грань вперед, чтобы увидеть, где точки выстраиваются по оси.
Остались левая и правая грани:
1 2 3 4 5 6 7 8 9 10 11 | // Левая грань -1.0, 1.0, -1.0, // верхняя левая -1.0, 1.0, 1.0, // верхняя правая -1.0, -1.0, 1.0, // нижняя правая -1.0, -1.0, -1.0, // нижняя левая // Правая грань 1.0, 1.0, 1.0, // верхняя левая 1.0, 1.0, -1.0, // верхняя правая 1.0, -1.0, -1.0, // правая 1.0, -1.0, 1.0 // левая |
Вот полное определение куба:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | const GLfloat cubeVertices[] = { // Определение передней грани -1.0, 1.0, 1.0, // верхняя левая -1.0, -1.0, 1.0, // нижняя левая 1.0, -1.0, 1.0, // нижняя правая 1.0, 1.0, 1.0, // верхняя правая // Верхняя грань -1.0, 1.0, -1.0, // верхняя левая (сбоку) -1.0, 1.0, 1.0, // нижняя левая (спереди) 1.0, 1.0, 1.0, // нижняя правая (спереди) 1.0, 1.0, -1.0, // верхняя правая (сбоку) // Боковая грань 1.0, 1.0, -1.0, // верхняя правая (если смотреть прямо) 1.0, -1.0, -1.0, // нижняя правая -1.0, -1.0, -1.0, // нижняя левая -1.0, 1.0, -1.0, // верхняя левая // нижняя грань -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, // левая грань -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, -1.0, // правая грань 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0 }; |
Даже если у кого-то проблемы с системой координат, все же попытайтесь все это визуализировать мысленно. Не получается? Возьмите лист бумаги и начерните трехмерное (изометрическое) представление. Крайне необходимо начать понимать сущность трехмерных объектов.
Ниже комментария “New object definition goes here” в методе “drawView” вставьте “cubeVertices“.
Ну вот, теперь можно и рисовать.
Рисуем куб
Чтобы быстро и просто освоить рисование куба, воспользуемся упомянутым ранее кодом. Позже мы освоим более прогрессивные методы 3D-моделирования (которые к тому моменту станут гораздо понятнее). А пока приступим к созданию первого объемного объекта.
Начнем с кода, который в объяснениях не нуждается. После примечания “Our new drawing code goes here” вставьте приведенные ниже строки:
1 2 3 4 | glLoadIdentity(); glTranslatef(0.0, 0.0, -6.0); glVertexPointer(3, GL_FLOAT, 0, cubeVertices); glEnableClientState(GL_VERTEX_ARRAY); |
Здесь ничего нового. Сбрасываем состояние работы с вершинами, отодвигаем куб вглубь экрана, чтобы его было видно, сообщаем OpenGL о массиве вершин и формате данных в нем, после активируем OpenGL для использования массива.
Представленный ниже код практически тот же, что и ранее:
1 2 3 | // Переднюю грань рисуем красным glColor4f(1.0, 0.0, 0.0, 1.0); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); |
Ничего особо нового: делаем цветом рисования красный и сообщаем OpenGL о необходимости рисовать вершины 0-4 из массива в виде квадрата. Теперь из следующих 4 вершин в массиве формируем верхнюю грань:
1 2 3 | // Верхнюю грань рисуем зеленым glColor4f(0.0, 1.0, 0.0, 1.0); glDrawArrays(GL_TRIANGLE_FAN, 4, 4); |
Взгляните на “glDrawArrays()“. Описывая эту функцию, я упоминал, что вторым параметром является начало смещения. Поскольку мы создаем вторую грань куба, то сперва сообщаем OpenGL о начале смещения 4 рисовать (то есть “cubeVertices[4]“, сдвиги 0~3 являются передней гранью), а уже потом рисуем еще четыре вершины.
Теперь можно рисовать боковую грань:
1 2 3 | // Боковую грань рисуем синим glColor4f(0.0, 0.0, 1.0, 1.0); glDrawArrays(GL_TRIANGLE_FAN, 8, 4); |
Тот же принцип, только в этот раз начинаем с “cubeVertices[8]“. Точно так же создаем три последние грани:
1 2 3 4 5 6 7 8 9 10 11 | // Рисуем нижнюю грань glColor4f(1.0, 1.0, 0.0, 1.0); glDrawArrays(GL_TRIANGLE_FAN, 12, 4); // Рисуем левую грань glColor4f(0.0, 1.0, 1.0, 1.0); glDrawArrays(GL_TRIANGLE_FAN, 16, 4); // Рисуем правую грань glColor4f(1.0, 0.0, 1.0, 1.0); glDrawArrays(GL_TRIANGLE_FAN, 20, 4); |
И опять мы всего лишь меняем цвет и меняем начало смещения для “glDrawArrays()“.
теперь сейчас нажать кнопку “Build and Go“, то получим статичный красный квадрат, ничем не отличающийся от того, что был вначале. Чтобы увидеть шесть граней во всем их великолепии, повернем куб вдоль трех осей.
Прямо перед “glLoadIdentity()” добавьте следующее утверждение:
1 | rota += 0.5; |
А вот и наш старый знакомый “rota“, позовем к нему в компанию еще одного старого друга — “glRotatef()“. После “glTranslatef()” добавьте представленную ниже строку:
1 | glRotatef(rota, 1.0, 1.0, 1.0); |
Ранее мы использовали “glRotatef()” вдоль одной оси. Теперь выполним параллельное вращение по всем трем осям.
Щелкнув на кнопке “Build and Go“, оцените результат.

Добро пожаловать в трехмерную реальность.
А как насчет наложения текстур?
Думаю, что элементарно раскрашенные объекты пора оставить в прошлом. Предлагаю сделать объект интереснее, наложив на все шесть граней текстуру из последнего урока.
Мы сохранили в проекте текстуру и код ее загрузки, поэтому осталось лишь поменять метод “drawView“. Сейчас вы убедитесь, что с подготовленными текстурами все происходит быстро и просто!
Для начала вспомним фрагмент кода из прошлого урока:
1 2 3 4 5 6 | const GLshort squareTextureCoords[] = { // Передняя грань 0, 1, // верхняя левая 0, 0, // нижняя левая 1, 0, // верхняя правая 1, 1, // нижняя правая |
Пока текстура накладывалась на одну грань, это работало. Теперь код придется дополнять, но это очень просто. Вспомните, как педантично я начинал задавать координаты, соблюдая порядок и сохранив его для куба? Теперь станет понятно, почему.
Поскольку все координаты после первой грани задавались со смещением (4, 8, 12 и т.д. для “glDrawArrays()“), при поиске координат текстуры для каждой грани в процессе визуализации куба будут соблюдаться те же смещения. Следовательно, для наложения текстур на шесть граней достаточно продублировать представленные выше четыре координаты еще 5 раз, получив массив текстур:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | const GLshort squareTextureCoords[] = { // Передняя грань 0, 1, // верхняя левая 0, 0, // нижняя левая 1, 0, // нижняя правая 1, 1, // верхняя правая // Верхняя грань 0, 1, // верхняя левая 0, 0, // нижняя левая 1, 0, // нижняя правая 1, 1, // верхняя правая // Боковая грань 0, 1, // верхняя левая 0, 0, // нижняя левая 1, 0, // нижняя правая 1, 1, // верхняя правая // Нижняя грань 0, 1, // верхняя левая 0, 0, // нижняя левая 1, 0, // нижняя правая 1, 1, // верхняя правая // Левая грань 0, 1, // верхняя левая 0, 0, // нижняя левая 1, 0, // нижняя правая 1, 1, // верхняя правая // Правая грань 0, 1, // верхняя левая 0, 0, // нижняя левая 1, 0, // нижняя правая 1, 1, // верхняя правая }; |
С копированием и вставкой пришлось повозиться!
Теперь осталась пара строк кода рисования, и можно будет визуализировать наш куб с текстурами.
Перед фрагментом для рисования первой грани добавьте следующий код:
1 2 | glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords); glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
Все действительно просто. Не удаляйте “glColour4f()” перед каждым кодом рисования, а просто щелкните на “Build and Go“, получив вот такой результат:

Вот это да! Трехмерный объект с текстурами, да еще и вращающийся в пространстве.
Пару слов об изображениях текстур
В прошлом уроке я забыл упомянуть один момент. Обязательно экспериментируйте с собственными текстурами, но не забывайте, что размеры текстуры всегда представляют собой 2 в n-ной степени. То есть, ширина и высота изображения должны составлять 1, 2, 4, … 32, … 512, 1024… Равенства от них не требуется, но ширина и высота обязательно равны степени двойки. Соответственно, 32 x 512 — корректный размер, точно так же, как 64 x 64, а вот 30 x 30 не подходит.
Пару слов о “glRotatef()” и вершинах объектов
Обязательно активно экспериментируйте с собственными объектами, но не забывайте о следующем. Заметили, что я всегда устанавливаю центры треугольников, квадратов, а теперь и куба на 0,0,0? Края каждого объекта при этом равноудалены от 0,0,0. Все для того, чтобы OpenGL естественнее вращала объекты вокруг централ объекта по отношению к 0,0,0 в матричной модели. Перед вращением программа не подстраивает объект под 0,0,0. Если он не выровнен по центру, то при вращении окажется перекошенным.
На сегодня все
Этим мы завершаем сегодняшний урок. Надеюсь, читать его вам было так же полезно и интересно, как мне писать.
Кстати, теперь мне кажется, что стоило переключиться на указание вершин против часовой стрелки. В следующем уроке я подробно рассмотрю оба варианта — создадим куб способом “против часовой стрелки”.


Октябрь 1st, 2009 at 17:00
Небольшое замечание к переводу -
так как в листинге вы перевели комментарий
“// Сюда новый код рисования”
то в том месте где сейчас написано : “После примечания “Our new drawing code goes here” вставьте приведенные”
надо написать : “После примечания “Сюда новый код рисования” ” вставьте приведенные
Ноябрь 19th, 2009 at 13:54
Спасибо.