Уроки OpenGL + C#.

Необходимые операции инициализации OpenGL.

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

В предыдущей главе мы рассмотрели пример программы, визуализировавшей каркас трехмерной сферы, но не углублялись в процесс настройки нашего приложения.
Теперь мы полностью проанализируем код этого приложения, и объясним, каким образом происходит инициализация OpenGL и реализуется визуализация объектов.


В следующих частях этой главы , и следующих 3 главах, мы будем рассматривать вопросы визуализации 2D графики, и только потом перейдем к программированию трехмерной графики.

Инициализация OpenGl.


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

Для визуализации трехмерной сцены, в предыдущей главе мы использовали следующий код:


private void Form1_Load(object sender, EventArgs e)
{
// инициализация Glut
Glut.glutInit();
Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_DOUBLE | Glut.GLUT_DEPTH);

// очитка окна
Gl.glClearColor(255, 255, 255, 1);

// установка порта вывода в соотвествии с размерами элемента anT
Gl.glViewport(0, 0, AnT.Width, AnT.Height);


// настройка проекции
Gl.glMatrixMode(Gl.GL_PROJECTION);
Gl.glLoadIdentity();
Glu.gluPerspective(45, (float)AnT.Width / (float)AnT.Height, 0.1, 200);
Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glLoadIdentity();

// настройка параметров OpenGL для визуализации
Gl.glEnable(Gl.GL_DEPTH_TEST);
Gl.glEnable(Gl.GL_LIGHTING);
Gl.glEnable(Gl.GL_LIGHT0);
}


Здесь, в первую очередь проходит инициализация библиотеки Glut.
Как видно из кода, для работы с функциями библиотеки OpenGL используется класс Gl, находящийся в пространстве имен Tao.OpenGL.
Для работы с функциями библиотеки Glut – используется класс Glut.
Таким образом, по сравнению с использованием этих библиотек в С++ - мы всего лишь вызываем методы из классов соответствующих библиотек, где они, кстати, очень удобно описаны.
Мы обязательно должны вызвать функцию glutInit() до того, как начнем использовать любые другие функции данной библиотеки, так как эта функция производит инициализацию библиотеки Glut.
Далее мы вызываем функцию

 

Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_DOUBLE | Glut.GLUT_DEPTH);


Эта функция устанавливает режим отображения. В нашем случае устанавливается RGB режим для визуализации (GLUT_RGB – это псевдоним GLUT_RGBA, он устанавливает RGBA режим битовой маски окна). Далее мы устанавливаем двойную буферизацию окна. Двойная буферизация, как правило используется для устранения мерцания, возникающего в процессе быстрой перерисовки кадров несколько раз подряд. GLUT_DEPTH указывает при инициализации окна, если в приложении будет использоваться буфер глубины.
После инициализации окна мы устанавливаем цвет очистки окна, с помощью функции

 

Gl.glClearColor(255, 255, 255, 1);


Чтобы анализировать код дальше, важно понимать, как устроен процесс создания сцены в OpenGL.

  1. Все начинается с позиционирования объема видимости в пространстве. Представьте, что мы установили камеру в каких-либо координатах.
  2. Теперь в данном пространстве мы устанавливаем некую модель (объект), которая будет попадать в объем видимости нашей камеры. Например, перед установленной камерой появился человек.
  3. Следующем шагом будет проецирование, в котором мы определим форму того объема в пространстве, который мы видим.
  4. И заключительным шагом, в рамках порта просмотра мы получаем изображение объекта.


Теперь нам необходимо определить значение порта вывода. Устанавливая значения в функции

Gl.glViewport(0, 0, AnT.Width, AnT.Height);


Здесь мы указываем библиотеке OpenGL на то, что вывод будет осуществляться во всей области элемента AnT (наш элемент, расположенный на форме для визуализации в него сцены). Определяем тот самый порт просмотра, куда последним шагом будет визуализироваться модель.
После этого происходит настройка проекции – для этого мы сначала вызываем функцию

 

Gl.glMatrixMode(Gl.GL_PROJECTION);


Функция glMatrixMode предназначена для того, чтобы задавать матричный режим: будет определена матрица, над которой мы в дальнейшем будем производить операции. В нашем случае это GL_PROJECTION – матрица проекций.
Следующей командой, мы очищаем матрицу, с помощью функции glLoadIdentity (функция заменяет текущую матрицу на единичную). Далее мы устанавливаем тип текущей проекции с помощью функции gluPerspective.

 

Gl.glLoadIdentity();
Glu.gluPerspective(45, (float)AnT.Width / (float)AnT.Height, 0.1, 200);


Функция gluPerspective определена в библиотеке GLUOpenGL Utility Library (GLU). Эта библиотека является надстройкой над библиотекой OpenGL, реализующей ряд более продвинутых функций. Она так же является свободно-распространяемой и поставляется вместе с библиотекой OpenGL.
Данная функция строит пирамиду охвата видимости, основываясь на угле визуального охвата, отношении сторон порта просмотра и установке ближней и дальней плоскости просмотра. Рисунок 1.


Пирамида видимости
Рисунок 1.


Теперь, когда проекция определена, мы устанавливаем в качестве текущей матрицы - объектно-видовую матрицу и очищаем ее.

 

Gl.glMatrixMode(Gl.GL_MODELVIEW);
Gl.glLoadIdentity();


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

 

Gl.glEnable(Gl.GL_DEPTH_TEST);
Gl.glEnable(Gl.GL_COLOR_MATERIAL);



Вот в принципе и все, что касалось инициализации OpenGL.
Теперь рассмотрим, что же делалось в функции, визуализировавшей трехмерную сферу.
Код этой функции:

 

// обработчик кнопки "визуализировать"
private void button1_Click(object sender, EventArgs e)
{
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);

Gl.glLoadIdentity();

Gl.glPushMatrix();
Gl.glTranslated(0,0,-6);
Gl.glRotated(45, 1, 1, 0);

// рисуем сферу с помощью библиотеки FreeGLUT
Glut.glutWireSphere(2, 32, 32);

Gl.glPopMatrix();
Gl.glFlush();
AnT.Invalidate();
}



Когда пользователь нажимает на кнопку и вызывается данная функция, первым делом производиться очистка окна (т.к. до этого уже мог быть реализован какой-либо вывод – очистка экрана перед визуализацией кадра – это стандартный метод).

Для этого используется функция glClear. В качестве параметра, функция получает значения каких буферов ей необходимо очистить – в нашем случае – это буфер цвета и буфер глубины.

Теперь мы очищаем объектно-видовую матрицу – таким образом, камера, как бы установилась в начала координат. Теперь мы можем совершить ее перемещение в пространстве.
Но перед этим мы вызываем функцию

Gl.glColor3f(255, 0, 0);

устанавливающую красный цвет отрисовки геометрии (основывается на RGB составляющих).

Итак, перемещение. Функция glPushMatrix осуществляет помещение текущей матрицы в стек матриц, откуда, в дальнейшем, мы сможем ее вернуть, с помощью функции glPopMatrix.


Таким образом, мы осуществим перемещение отрисовываемой сферы в пространстве, не изменив саму матрицу, отвечающую за положение камеры (наблюдателя).
Если не использовать такой подход, то с каждым визуализированным кадром, наша камера будет перемещаться, и очень скоро мы вообще не сможем ее найти.
Сохранив матрицу в стеке, мы производим перемещение объекта на 6 едениц по оси Z, после чего выполняем поворот сцены на 45 градусов сразу по двум осям

 

Gl.glTranslated(0,0,-6);
Gl.glRotated(45, 1, 1, 0);


После этого мы выполняем рисование сферы в той области, куда мы переместились, радиусом 2, в виде сетки. Сфера будет разбита на 32 меридиана и 32 параллели.
Для визуализации используется библиотека FreeGlut. В одном из уроков, посвященных трехмерной визуализации, мы реализуем с вами собственный алгоритм визуализации сферы.

 

// рисуем сферу с помощью библиотеки FreeGLUT
Glut.glutWireSphere(2, 32, 32);


Возвращаем сохраненную в стеке матрицу:

 

Gl.glPopMatrix();


Дожидаемся, пока библиотека OpenGL завершит визуализацию этого кадра

Gl.glFlush();


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

 

AnT.Invalidate();


Вот в принципе и все. Мы подробно рассмотрели весь код программы, созданной в последней части предыдущей главы. С одной стороны – это объяснило многие вещи в вопросе инициализации OpenGL и визуализации трехмерной графики.
С другой стороны, неосвещенными остались многие темы.


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

 

Обсуждение данного урока: Подробное описание инициализации и визуализации в OpenGL на языке C#.

Далее: 5.2 Визуализация 2D примитивов в OpenGL. Основы.