На базе визуализации примитивов строится все рисование графики в OpenGL. Поэтому изучив основу инициализации окна приложения, мы сразу переходим к теме визуализации. Рассматривать эту тему мы будем на рядe как простых, так и довольно интересных, но более сложных примеров. В данной части главы мы изучим минимальные основы, которые необходимо понимать при работе с визуализацией примитивов.
Важные темы, рассматриваемые в данной части главы:
- Работа с 2D примитивами в OpenGL и С#.
- Первая OpenGL визуализация 2D примитивов.
- Визуализация буквы с помощью OpenGL 2D примитивов.
Работа с 2D примитивами
Начнем с того, как происходит рисование в компьютерной графике.Как известно, рисование всех базовых элементов сводится к рисованию точек, которые в дальнейшем образуют рисунок. В случае компьютерной графики точками создаются линии, из которых могут быть построены простейшие геометрические фигуры – полигоны. Полигоны могут создавать собой сетку, которая представляет в трехмерном пространстве какую-либо модель.
Другое определение полигона: полигон – это некая область в пространстве, которая ограничивается одной замкнутой ломаной линией, причем каждый из отрезков данной ломаной линии задается с помощью двух вершин на его концах. Но несложно догадаться, что описанный таким образом полигон может оказаться очень сложным геометрическим объектом.
Поэтому нельзя, чтобы ребра полигонов пересекались, а также он обязательно должен быть выпуклым.
Если соблюдать эти два условия, нет никаких ограничений на то, из скольких точек стоит полигон.
Для рисования примитивов в OpenGL мы будем использовать различные режимы визуализации, но перед тем как перейти к их изучению, сначала рассмотрим, как происходит указание вершин в OpenGl.
Для указания вершин используется специальная команда: glVertex*()
А имеет следующий формат:
/*http://esate.ru, Anvi*/
void glVertex {234} {ifd}(TYPE coords);
Здесь 2, 3, 4 – это количество координат, которыми вы будете задавать вершину. В OpenGL точки на самом деле задаются 4 координатами: x, y, z, r. Таким образом, положение точки описывается как x/r, y/r, z/r. Когда r не указывается, оно принимается равным единице. Если указываются точки в двухмерной системе координат, z считается равным нулю.
i, f, d – это возможный тип принимаемых значений. К примеру, если написать команду glVertex3f (,,) то скобках должны последовать три переменные типа float. Для d – double, для I – int.
Вызов функции glVertex может иметь результат только между вызовами функций glBegin() и glEnd(), о которых мы сейчас и поговорим.
Для построения геометрии из точек используются разные режимы, которые описываются различными правилами объединения вершин в полигоны.
Такие режимы рисования возможны лишь с помощью перечисления точек между командами glBegin() и glEnd(), причем при вызове функции glBegin указывается, в каком режиме будут соединяться полигоны.
Значения параметров, которые может принимать glBegin:
GL_POINTS. На месте каждой указанной с помощью команды glVertex вершины рисуется точка.
GL_LINES. Каждые 2 вершины объединяются в отрезок.
GL_LINE_STRIP. Вершины соединяются последовательно, образую кривую линию.
GL_LINE_LOOP. То же, что и GL_LINE_STRIP, но от последней точки будет идти отрезок к начальной точке.
GL_TRIANGLES. Каждые три вершины объединятся в треугольник.
GL_TRIANGLE_STRIP. Треугольники рисуются таким образом, что последняя грань треугольника является начальной гранью следующего (смотрите рисунок).
GL_TRIANGLE_FAN. То же, что и GL_TRIANGLE_STRIP, только изменен порядок следования вершин (смотрите рисунок).
GL_QUADS. Каждые четыре вершины объединяются в квадрат.
GL_QUAD_STRIP. Вершины объединяются в квадраты, причем последняя грань квадрата является первой гранью следующего (смотрите рисунок).
GL_POLYGON. Рисуется полигон на основе вершин. Вершин не должно быть менее 3 и должны отсутствовать самопересечения, а также должны сохраняться условия выпуклости.
Первая OpenGL визуализация 2D примитивов
Теперь попробуем нарисовать какой-либо рисунок. Для этого создайте приложение, основываясь на опыте, полученном в главе 4.4 (то есть подключите необходимые ссылки к библиотекам Tao, подлечите необходимые пространства имен, расположите элементы на окне, а также назначьте необходимые обработчики событий и инициализируйте необходимые элементы).Эта программа будет отличаться от программы, написанной в главе 4.4 лишь тем, что здесь будет использоваться другая проекция в функции Form1_Load и код, отвечающий за визуализацию в функции button1_Click.
Перейдя к свойствам формы, измените название окна на «Рисование 2D примитивов».
Теперь рассмотрим код функции Form1_Load. Он будет выглядеть следующим образом:
/*http://esate.ru, Anvi*/
private void Form1_Load(object sender, EventArgs e)
{
// инициализация Glut
Glut.glutInit();
Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_SINGLE);
// очистка окна
Gl.glClearColor(255, 255, 255, 1);
// установка порта вывода в соответствии с размерами элемента anT
Gl.glViewport(0, 0, AnT.Width, AnT.Height);
// настройка проекции
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
// теперь необходимо корректно настроить 2D ортогональную проекцию
// в зависимости от того, какая сторона больше
// мы немного варьируем то, как будет сконфигурированный настройки проекции
if ((float)AnT.Width <= (float)AnT.Height)
{
Glu.gluOrtho2D(0.0, 30.0 * (float)AnT.Height / (float)AnT.Width, 0.0, 30.0);
}
else
{
Glu.gluOrtho2D(0.0, 30.0 * (float)AnT.Width / (float)AnT.Height, 0.0, 30.0);
}
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glLoadIdentity();
}
Как видно из кода, за настройку 2D ортогональной проекции отвечает функция gluOrtho2D, реализация которой предоставлена библиотекой Glu. Эта функция, как бы помещает начало координат в самый левый нижний квадрат, а камера (наблюдатель) в таком случае находиться на оси Z, и таким образом мы визуализируем графику в 2D режиме.
По сути, мы должны передать в качестве параметров координаты области видимости окном проекции: left , right, bottom и top. В нашем случае мы передаем параметры таким образом, чтобы исключить искажения, связанные с тем, что область вывода не квадратная, поэтому параметр top рассчитывается как произведение 30 и отношения высоты элемента AnT (в котором будет идти визуализация) к его ширине.
В том же случае, если высота больше ширины, то наоборот: отношением ширины к высоте.
Теперь рассмотрим код функции button1_Click.
В нем мы будем производить очистку буфера цвета кадра, после чего будет очищаться текущая объектно-видовая матрица. Затем мы будем в режиме рисования линий задавать координаты, для того чтобы нарисовать букву А.
После этого мы перерисовываем содержимое элемента AnT.
Итак, перед тем как нарисовать букву А для большей понятности давайте просто потренируемся: проведем линии из начала координат в противоположенный угол окна.
Тогда код функции будет выглядеть следующим образом:
/*http://esate.ru, Anvi*/
private void button1_Click(object sender, EventArgs e)
{
// очищаем буфер цвета
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
// очищаем текущую матрицу
Gl.glLoadIdentity();
// устанавливаем текущий цвет - красный
Gl.glColor3f(255, 0, 0);
// активируем режим рисования линий, на основе
// последовательного соединения всех вершин в отрезки
Gl.glBegin(Gl.GL_LINE_STRIP);
// первая вершина будет находиться в начале координат
Gl.glVertex2d(0, 0);
// теперь в зависимости от того, как была определена проекция
if ((float)AnT.Width <= (float)AnT.Height)
{
// рисуем вторую вершину в противоположенном углу
Gl.glVertex2d(30.0f * (float)AnT.Height / (float)AnT.Width, 30);
}
else
{
// рисуем вторую вершину в противоположенном углу
Gl.glVertex2d(30.0f * (float)AnT.Width / (float)AnT.Height, 30);
}
// завершаем режим рисования
Gl.glEnd();
// дожидаемся конца визуализации кадра
Gl.glFlush();
// посылаем сигнал перерисовки элемента AnT.
AnT.Invalidate();
}
Теперь, если откомпилировать проект и запустить приложение (F5), а затем нажать на кнопку «визуализировать», мы увидим следующий результат (рис. 1).
Рисунок 1. Пример визаулизации линии.
Визуализация буквы с помощью OpenGL 2D примитивов
Теперь заменим код:/*http://esate.ru, Anvi*/
Gl.glBegin(Gl.GL_LINE_STRIP);
// первая вершина будет находиться в начале координат
Gl.glVertex2d(0, 0);
// теперь в зависимости от того, как была определена проекция
if ((float)AnT.Width <= (float)AnT.Height)
{
// рисуем вторую вершину в противоположенном углу
Gl.glVertex2d(30.0f * (float)AnT.Height / (float)AnT.Width, 30);
}
else
{
// рисуем вторую вершину в противоположенном углу
Gl.glVertex2d(30.0f * (float)AnT.Width / (float)AnT.Height, 30);
}
// завершаем режим рисования
Gl.glEnd();
На код, который будет рисовать букву А.
Сейчас все заключается в том, чтобы правильно указать координаты вершин. Буква будет рисовать с помощью 2 кривых (первая кривая выделена красным цветом, вторая кривая - синим).
То, как выглядит буква и координаты соответствующих вершин, вы можете видеть на рисунке 2.
Рисунок 2. 2D координаты точек и последовательность их соединений.
Код рисования буквы А (буква будет рисоваться двумя линиями).
/*http://esate.ru, Anvi*/
// активируем режим рисования линий на основе
// последовательного соединения всех вершин в отрезки
Gl.glBegin(Gl.GL_LINE_LOOP);
// первая вершина будет находиться в начале координат
Gl.glVertex2d(8, 7);
Gl.glVertex2d(15, 27);
Gl.glVertex2d(17, 27);
Gl.glVertex2d(23, 7);
Gl.glVertex2d(21, 7);
Gl.glVertex2d(19, 14);
Gl.glVertex2d(12.5, 14);
Gl.glVertex2d(10, 7);
// завершаем режим рисования
Gl.glEnd();
// вторая линия
Gl.glBegin(Gl.GL_LINE_LOOP);
Gl.glVertex2d(18.5, 16);
Gl.glVertex2d(16, 25);
Gl.glVertex2d(13.2, 16);
// завершаем режим рисования
Gl.glEnd();
Пример нарисованной в программе буквы – на рисунке 3.
Рисунок 3. Результат работы функции, визуализирующей 2D кривые.
Мы познакомились с основными принципами рисования примитивов в OpenGL.
В следующей части главы мы визуализируем треугольный полигон с разложенным по нему цветовым спектром, причем в окне программы будут иметься ползунки, с помощью которых можно будет управлять коэффициентами, влияющими на разложение спектра.