|
Июн
25
|
В этом уроке мы по-быстрому освоим оставшиеся последними примитивы — точки и линии. Ничего сложного, но мне уже пришлось ответить на несколько вопросов по данным объектам, поэтому я решил остановиться на них подробнее.
Итак, наша цель — оперативное знакомство с точками и линиями, которое позволит нам двигаться дальше в нужном направлении.
Главное, не взять слишком высокий темп. При вызове “glVertexPointer()” я буду работать с таинственным параметром “stride” (шаг индекса) Рассмотрим эту тему сейчас, поскольку один из следующих уроков будет посвящен объектам Blender в программировании на iPhone, позволяющим без особого труда обзаводится на экране объектами в неприлично больших количествах
Шаг индекса — интересный параметр, которым многие просто пренебрегают (как, собственно, до этого момента и я) или описывают как “слишком сложную тему, к которой вы еще не готовы” (возможно, потому, что сами ее еще не проработали….). Главное — разобраться в основах — и все станет понятно.
Точки
В OpenGL ES объект “point” представляет собой точку в трехмерном пространстве. При активированном режиме “GL_POINT_SMOOTH” он визуализируется как круг. В противном случае — как квадрат.
В принципе, не прибегая к объекту “point“, точку можно на скорую руку состряпать из двух треугольников, но я бы этого не рекомендовал.
Как раз недавно я дискутировал кое с кем, утверждавшим, что он всегда рисует точки из двух треугольников, поскольку “такой способ подразумевается аппаратно”. Не утверждая, что такой подход в корне неверен, правильным его назвать я тоже не берусь.
В качестве довода приводилась аппаратная реализация точки, при которой квадрат формируется двумя треугольниками (во многих случаях это действительно так). Поэтому, объясняли мне, передавая GL данные по точке, мы увеличиваем аппаратную нагрузку. Однако при этом не берется в расчет элементарный вопрос: “Какой метод аппаратно эффективнее — “GL_TRIANGLE_STRIP” или “GL_TRIANGLE_FAN“?
В общем, не ломайте голову. При работе на iPhone можно было бы оптимизировать код для получения максимальной отдачи от чипа OpenGL ES, но, полагаю, нам это просто не нужно.
Предлагаю переходить к рисованию точки. Визуализируем на экране четыре точки разных цветов. Для описания точки понадобятся координаты X, Y и Z и размер.
Первым делом загружаем базовый проект здесь.
Координаты точки нужно указать в массиве вершин, а цвета — в массиве цветов. Итак, в метод “drawView[]” добавляем следующую структуру:
1 2 3 4 5 6 | const GLfloat points[] = { 1.0, 1.0, -6.0, 1.0, 0.0, 0.0, 1.0, // Первая точка, краская 1.0, -1.0, -6.0, 0.0, 1.0, 0.0, 1.0, // Вторая точка, зеленая -1.0, -1.0, -6.0, 0.0, 0.0, 1.0, 1.0, // Третья точка, синяя -1.0, 1.0, -6.0, 1.0, 1.0, 0.0, 1.0 // Четвертая точка, желтая }; |
Надеюсь, все заметили, что я объединил в один массив вершины (т.е. координаты) и цвета (значения RGBA). Ничего страшного?
Код рисования останется практически тем же — подробно рассмотрим только одну строку. Рисование точек будет идти в несколько этапов, но все они знакомы за исключением указания размера.
Код рисования четырех точек:
1 2 3 4 5 6 7 8 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPointSize(50.0); glVertexPointer(3, GL_FLOAT, 28, points); glColorPointer(4, GL_FLOAT, 28, &points[3]); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_POINTS, 0, 4); |
Несложно догадаться, что “glPointSize()” задает размер точек. В принципе, в этой функции необходимости нет, поскольку в OpenGL ES предусмотрен размер точек по умолчанию, с которым они визуализируются в отсутствие других параметров. Наши же точки я сделал большими и красивыми.
Следующие две строки не похожи на то, что мы делаем обычно:
1 2 | glVertexPointer(3, GL_FLOAT, 28, points); glColorPointer(4, GL_FLOAT, 28, &points[3]); |
Ранее я уже останавливался на этом моменте, но третий параметр “stride” всегда был равен нулю, а ссылка на данные указывала на элемент “arrayName[0]” Теперь, поскольку в массиве вершин оказались еще и данные по цвету, нужно сообщить OpenGL о необходимости игнорировать цвета при поиске координат объекта и пропускать координаты при обращении к цветам.
Вот здесь нам придет на помощь шаг по индексу (третий параметр “stride“). В руководстве его определение звучит так:
Задает байтовое смещение между последовательными вершинами. При параметре “stride”, равном нулю, вершины в массиве расположены вплотную. По умолчанию равен нулю.
Здесь и кроется единственная и главная сложность, о которой я предупреждал. Прочтите внимательно.
Шаг индекса — это смещение между последовательными вершинами, а не заполнение места между ними!
Для “sizeof(GLfloat)” результат составляет 4 байта. Следовательно, при наличии трех “GLfloat” для каждой вершины и четырех “GLfloat” для каждого цвета определение точки займет 28 байт. Это и есть значение шага по индексу!
Мы рассчитали разницу между “points[0]” и “points[7]“. Понятно, что “points[7]” является началом координат следующей вершины? В массиве она хранится на 28 байт дальше предыдущей.
С шагом индекса разобрались. Теперь можете с видом знатока отвечать на бесконечные вопросы по этой теме.
Теперь нужно сообщить OpenGL, что массив цветов начинается в “points[3]” — это первое значение цвета RGBA (программа обработки цветов не будет анализировать структуру: не рассчитывайте, что укажете на “points[0]“, а она сама выяснит, откуда начинать). Шаг по индексу остается тем же самым.
После щелчка на кнопке “Build and Go” получаем в симуляторе следующий результат:
Четыре точки размером 50.0 без сглаживания, поэтому квадратной формы. Как сделать их круглыми? С помощью одной строки кода:
1 | glEnable(GL_POINT_SMOOTH); |
Добавьте эту строку прямо перед “glPointSize()“. Снова запустите симулятор и получите сглаженные (круглые) точки:
Поэкспериментируйте с размерами, цветами и прочими параметрами. Для начала добавьте приведенную ниже строку:
1 | glRotatef(0.5, 0.0, 0.0, 1.0); |
прямо перед только что внесенной “glEnable(GL_POINT_SMOOTH)“. Теперь с помощью “glTranslatef()” поменяйте для объектов координату Z. Продолжайте эксперименты, пока не почувствуете себя уверенно.
Линии
Линии ничем не сложнее остальных объектов: достаточно проанализировать один пример, чтобы все встало на свои места. Для начала добавим представленный ниже код:
1 2 3 4 5 6 | const GLfloat lines[] = { 2.0, 2.0, -6.0, 1.0, 1.0, 0.0, 1.0, // Начальная точка желтым 2.0, -2.0, -6.0, 0.0, 1.0, 1.0, 1.0, // вторая точка цвета морской волны -2.0, -2.0, -6.0, 1.0, 0.0, 0.0, 1.0, // третья точка красная -2.0, 2.0, -6.0, 0.0, 0.0, 1.0, 1.0 // четвертая точка синяя }; |
После визуализации все станет понятно.
Чтобы нарисовать линии, обратимся к следующему коду (из “drawView[]“):
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Настраиваем и визуализируем точки glEnable(GL_POINT_SMOOTH); glPointSize(50.0); glVertexPointer(3, GL_FLOAT, 28, points); glColorPointer(4, GL_FLOAT, 28, &points[3]); glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glDrawArrays(GL_POINTS, 0, 4); // Настраиваем и визуализируем линии glVertexPointer(3, GL_FLOAT, 28, lines); glColorPointer(4, GL_FLOAT, 28, &lines[3]); glDrawArrays(GL_LINES, 0, 4); |
После настройки и отрисовки точек останется сообщить OpenGL о новом массиве вершин и цветов линий, а потом инициализировать рисование вызовом “glDrawArrays()“.
По щелчку на кнопке “Build and Go” получаем следующий результат:
Не правда ли, все очевидно? Из четырех точек в массиве получились две линии, у каждой из которых есть начало и конец. Но это еще не все. Обратите внимание на первый параметр “glDrawArrays()” — “GL_LINES“. Как и в случае с треугольниками, есть несколько способов рисования линий.
Рассмотрим их все.
GL_LINES: линия строится по паре вершин.
Вот как это выглядит для OpenGL:
GL_LINE_STRIP: первая вершина указывает начальную точку, а каждая последующая становится источником для линии, визуализируемой между ней и предыдущей вершиной.
1 | glDrawArrays(GL_LINE_STRIP, 0, 4); |
Вот как выглядит проект для нас:
и для OpenGL:
GL_LINE_LOOP: первая вершина определяет начальную точку, а каждая последующая становится источником линии, визуализируемой между ней и предыдущей вершиной. Последняя вершина соединяется с первой.
Так выглядит код:
1 | glDrawArrays(GL_LINE_LOOP, 0, 4); |
и экран симулятора:
Проект для OpenGL:
Линия 4: OpenGL соединяет последнюю вершину с первой (P4 > P1)
Заметьте, что останавливаться на четырех линиях (или начинать с них) необязательно: число линий может быть любым, но для каждой, само собой, необходимо минимум две координаты.
C линиями мы тоже закончили. Чтобы сделать их толще, поэкспериментируйте с вызовом “glLineWidth(GLfloat lineWidth)“. Обратите внимание: если линии станут толще, первую и последнюю координаты придется откорректировать, чтобы получить настоящий” квадрат.
Попробуйте применить к линиям базовое сглаживание, подключив “GL_LINE_SMOOTH“, как показано ниже:
1 | glEnable(GL_LINE_SMOOTH); |
Большой разницы не будет — по ряду причин (сглаживанию будет посвящена отдельная статья этой серии).
Линии и точки не обязательно раскрашивать: можно накладывать на них текстуры, применять качественные спецэффекты.
Вот и подошел к концу урок, получившийся достаточно длинным, но, я надеюсь, полезным. Помимо линий и точек мы рассмотрели значение шага индекса для “glVertexArray();“, а это обязательно пригодится в дальнейшей работе.











Июнь 27th, 2009 at 18:08
А зачем коммент удалять?
Июнь 27th, 2009 at 23:55
Потому что оффтоп… и потомучто он был непонятным совершенно)
Июнь 28th, 2009 at 15:14
Ну я имел ввиду, что будет после Open GLES?На том блоге просто не было написано.
Июнь 28th, 2009 at 22:39
Пока еще будет только Open GL ES, когда закончим эту серию будут другоие уроки на разные темы. Что будет конкретно я незнаю. Есть предложения пишите на мыло lookapp@gmail.com
Октябрь 5th, 2009 at 16:16
Подскажите пожалуйста, каким образом накладывать текстуры на точки?
Декабрь 11th, 2009 at 07:48
Замечательный туториал, но я бы в таких местах как:
glVertexPointer(3, GL_FLOAT, 28, points);
вместо 28 использовал бы sizeOf * k - и понятней будет и правильней
И еще - а где картинки?
Декабрь 12th, 2009 at 07:47
С картинками разберусь. Возможно изменился путь к ним на сайте оригинала.