Esate.ru
Esate.ru Уроки Программирование 3D Уроки OpenGL + C#Визуализация графика функции в С# и OpenGL.

Уроки OpenGL + C#

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

5.4 Визуализация графика функции. (Рисование графиков в C#, OpenGL).

Создание визуализации анимированного графика функции на С# и OpenGL.


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

Это будет реализовано следующим образом: по графику двигается красная точка, принимающая значения y для заданного x в нашем графике (по всей видимой области).
Помимо этого, возле курсора будут визуализироваться его координаты (рисунок 1).


Рисунок 1.


Создайте основу приложения, так как это было описано в главе 4.4. Только не добавляйте кнопки «Визуализировать» и «Закрыть», ограничьтесь элементом SimpleOpenGLControl.
Окно должно иметь форму, как показано на рисунке 1.
Добавьте в проект 1 таймер – назовите его (параметр name в свойствах таймера) PointInGrap, и установите в его свойствах интервал 30 миллисекунд.
После этого щелкните по нему дважды, чтобы создалась функция PointInGrap_Tick, отвечающая за обработку события ontimer.


Теперь, когда основа приложения создана, мы перейдем к исходному коду.
Он будет основан на 7 функциях, которые мы сейчас рассмотрим, но сначала, перед кодом функции-конструктора класса добавьте инициализацию следующих переменных:

/*http://esate.ru, Anvi*/


// размеры окна 
double ScreenW, ScreenH; 

// отношения сторон окна визуализации 
// для корректного перевода координат мыши в координаты, 
// принятые в программе 

private float devX; 
private float devY; 

// массив, который будет хранить значения x,y точек графика 
private float[,] GrapValuesArray; 
// количество элементов в массиве 
private int elements_count = 0; 

// флаг, означающий, что массив с значениями координат графика пока еще не заполнен 
private bool not_calculate = true; 

// номер ячейки массива, из которой будут взяты координаты для красной точки, 
// для визуализации текущего кадра 
private int pointPosition = 0; 

// вспомогательные переменные для построения линий от курсора мыши к координатным осям 
float lineX, lineY; 

// текущение координаты курсора мыши 
float Mcoord_X = 0, Mcoord_Y = 0; 


Перед каждой переменной закомментировано ее назначение, так что вопросов не возникнуть не должно.
Теперь вернемся к нашим 7 функциям.

Первая функция – это обработчик события загрузки формы - Form1_Load. Здесь, при загрузке приложения будет произведена инициализация OpenGL для последующей визуализации.
Инициализацию OpenGL с двухмерной проекцией мы рассмотрели в предыдущей части главы, так что здесь все для нас понятно. Единственным отличием является то, что код стал немного более подробным. Код этой функции:

/*http://esate.ru, Anvi*/

private void Form1_Load(object sender, EventArgs e) 
{ 

	// инициализация библиотеки glut 
	Glut.glutInit(); 
	// инициализация режима экрана 
	Glut.glutInitDisplayMode(Glut.GLUT_RGB | Glut.GLUT_DOUBLE); 

	// установка цвета очистки экрана (RGBA) 
	Gl.glClearColor(255, 255, 255, 1); 

	// установка порта вывода 
	Gl.glViewport(0, 0, AnT.Width, AnT.Height); 

	// активация проекционной матрицы 
	Gl.glMatrixMode(Gl.GL_PROJECTION); 
	// очистка матрицы 
	Gl.glLoadIdentity(); 

	// определение параметров настройки проекции, в зависимости от размеров сторон элемента AnT. 
	if ((float)AnT.Width <= (float)AnT.Height) 
	{ 
		ScreenW = 30.0; 
		ScreenH = 30.0 * (float)AnT.Height / (float)AnT.Width; 
		Glu.gluOrtho2D(0.0, ScreenW, 0.0, ScreenH);
	} 
	else 
	{
		ScreenW = 30.0 * (float)AnT.Width / (float)AnT.Height; 
		ScreenH = 30.0; 
		Glu.gluOrtho2D(0.0, 30.0 * (float)AnT.Width / (float)AnT.Height, 0.0, 30.0);
	} 

	// сохранение коэффициентов, которые нам необходимы для перевода координат указателя в оконной системе, в координаты 
	// принятые в нашей OpenGL сцене 
	devX = (float)ScreenW / (float)AnT.Width; 
	devY = (float)ScreenH / (float)AnT.Height; 

	// установка объектно-видовой матрицы 
	Gl.glMatrixMode(Gl.GL_MODELVIEW); 

	// старт счетчика, отвечающего за вызов функции визуализации сцены 
	PointInGrap.Start();

} 


Как видите, почти ничего не изменилось.

Теперь обратимся к функции PointInGrap_Tick. Эта функция вызывается с задержкой в 30 миллисекунд.
В ней мы ведем отсчет того, из какого элемента массива с координатами графика, мы сейчас возьмем координаты, которые используем для рисования красной точки.
Отсюда так же вызывается функция Draw, отвечающая за визуализацию.

Код этой функции:

/*http://esate.ru, Anvi*/


// функция обработчик события таймера 
private void PointInGrap_Tick(object sender, EventArgs e) 
{ 

	// если мы дошли до последнего элемента массива 
	if (pointPosition == elements_count-1) 
		pointPosition = 0; // переходим к начальному элементу 

	// функция визуализации 
	Draw(); 

	// переход к следующему элементу массива 
	pointPosition++;

} 



Теперь, перед тем как перейти к функциям, отвечающим за визуализацию, мы рассмотрим несолько небольших вспомогательных функций.
Начтем с функции AnT_MouseMove.
Эта функция добавляется созданием события MouseMove для элемента SimpleOpnGLControl (AnT). Событие создается аналогично тому, как мы его создавали в главе 2.2. Только в данном случае мы переходим к свойствам элемента AnT, и уже в них переходим во вкладку Event и добавляем событие MouseMove.
В данной функции, мы производим сохранение текущих координат мыши, чтобы в будущем использовать их при визуализации графика, а так же производим вычисление размеров линий, которые будут по нормалям соединять координаты указателя мыши с координатными осями (две красные линии на рисунке 1).

Код этой функции выглядит следующим образом:

/*http://esate.ru, Anvi*/


// обработка движения мыши над элементом AnT 
private void AnT_MouseMove(object sender, MouseEventArgs e) 
{ 

// созраняем координаты мыши 
Mcoord_X = e.X; 
Mcoord_Y = e.Y; 

// вычисляем параметры для будущей дорисовке линий от указателя мыши к координатным осям. 
lineX = devX * e.X; 
lineY = (float)(ScreenH - devY * e.Y);

} 


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

/*http://esate.ru, Anvi*/


// функция визуализации текста 
private void PrintText2D(float x, float y, string text) 
{ 

	// устанавливаем позицию вывода растровых символов 
	// в переданных координатах x и y. 
	Gl.glRasterPos2f(x, y); 

	// в цикле foreach перебираем значения из массива text, 
	// который содержит значение строки для визуализации 
	foreach (char char_for_draw in text) 
	{ 
		// визуализируем символ c, с помощью функции glutBitmapCharacter, используя шрифт GLUT_BITMAP_9_BY_15. 
		Glut.glutBitmapCharacter(Glut.GLUT_BITMAP_9_BY_15, char_for_draw);
	}

} 



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

Код этой функции с подробными комментариями:

/*http://esate.ru, Anvi*/

// функция, производящая вычисления координат графика 
// и заносящая их в массив GrapValuesArray 
private void functionCalculation() 
{ 

	// определение локальных переменных X и Y 
	float x = 0, y = 0; 

	// инициализация массива, который будет хранить значение 300 точек 
	// из которых будет состоять график 

	GrapValuesArray = new float[300, 2]; 

	// счетчикэлементов массива 
	elements_count = 0; 

	// вычисления всех значений y, для x принадлежащего промежутку от -15 до 15, с шагом в 0.01f 
	for (x = -15; x < 15; x += 0.1f) 
	{ 
		// вычисление y для текущего x 
		// по формуле y = (float)Math.Sin(x)*3 + 1; 
		// эта строка задает формулу, описывающую график функции для нашего уравнения y = f(x). 
		y = (float)Math.Sin(x)*3 + 1; 

		// запись координаты x 
		GrapValuesArray[elements_count, 0] = x; 
		// запись координаты y 
		GrapValuesArray[elements_count, 1] = y; 
		// подсчет элементов 
		elements_count++;

	} 

	// изменяем флаг, сигнализировавший о том, что координаты графика не вычислены 
	not_calculate = false;

} 


Функция, выполняющая визуализацию графика.

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

В этой функции сначала будет проверен флаг, сигнализирующий о том, что координаты графика вычислены и занесены в массив (переменная not_calculate). В том случае, если флаг указывает, что просчета значений еще не было – вызывается функция , которая посчитает значения координат точек графика и заполнит ими массив.

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

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

Исходный код данной функции.

/*http://esate.ru, Anvi*/

// визуализация графика 
private void DrawDiagram() 
{ 

	// проверка флага, сигнализирующего о том, что координаты графика вычислены 
	if (not_calculate) 
	{ 
		// если нет - то вызываем функцию вычисления координат графика 
		functionCalculation();
	} 

	// стартуем отрисовку в режиме визуализации точек 
	// объединяемых в линии (GL_LINE_STRIP) 
	Gl.glBegin(Gl.GL_LINE_STRIP); 

	// рисуем начальную точку 
	Gl.glVertex2d(GrapValuesArray[0, 0], GrapValuesArray[0, 1]); 

	// проходим по массиву с координатами вычисленных точек 
	for (int ax = 1; ax < elements_count; ax+=2) 
	{ 
		// передаем в OpenGL информацию о вершине, участвующей в построении линий 
		Gl.glVertex2d(GrapValuesArray[ax, 0], GrapValuesArray[ax, 1]);
	} 

	// завершаем режим рисования 
	Gl.glEnd(); 

	// устанавливаем размер точек, равный 5 пикселям 
	Gl.glPointSize(5); 
	// устанавливаем текущим цветом - красный цвет 
	Gl.glColor3f(255, 0, 0); 
	// активируем режим вывода точек (GL_POINTS) 
	Gl.glBegin(Gl.GL_POINTS); 
	// выводим красную точку, используя ту ячейку массива, до которой мы дошли (вычисляется в функции обработчике событий таймера) 
	Gl.glVertex2d(GrapValuesArray[pointPosition, 0], GrapValuesArray[pointPosition, 1]); 
	// завершаем режим рисования 
	Gl.glEnd(); 
	// устанавливаем размер точек равный единице 
	Gl.glPointSize(1);

} 



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

/*http://esate.ru, Anvi*/

// функция, управляющая визуализацией сцены 
private void Draw() 
{ 

	// очистка буфера цвета и буфера глубины 
	Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); 

	// очищение текущей матрицы 
	Gl.glLoadIdentity(); 

	// установка черного цвета 
	Gl.glColor3f(0, 0, 0); 

	// помещаем состояние матрицы в стек матриц 
	Gl.glPushMatrix(); 

	// выполняем перемещение в пространстве по осям X и Y 
	Gl.glTranslated(15, 15, 0); 

	// активируем режим рисования (Указанные далее точки будут выводиться как точки GL_POINTS) 
	Gl.glBegin(Gl.GL_POINTS); 

	// с помощью прохода вдумя циклами, создаем сетку из точек 
	for (int ax = -15; ax < 15; ax++) 
	{
		for (int bx = -15; bx < 15; bx++) 
		{ 
			// вывод точки 
			Gl.glVertex2d(ax, bx);
		} 
	} 

	// завершение режима рисования примитивов 
	Gl.glEnd(); 

	// активируем режим рисования, каждые 2 последовательно вызванные команды glVertex 
	// объединяются в линии 
	Gl.glBegin(Gl.GL_LINES); 

	// далее мы рисуем координатные оси и стрелки на их концах 
	Gl.glVertex2d(0, -15); 
	Gl.glVertex2d(0, 15); 

	Gl.glVertex2d(-15, 0); 
	Gl.glVertex2d(15, 0); 

	// вертикальная стрелка 
	Gl.glVertex2d(0, 15); 
	Gl.glVertex2d(0.1, 14.5); 
	Gl.glVertex2d(0, 15); 
	Gl.glVertex2d(-0.1, 14.5); 

	// горизонтальная трелка 
	Gl.glVertex2d(15, 0); 
	Gl.glVertex2d(14.5, 0.1); 
	Gl.glVertex2d(15, 0); 
	Gl.glVertex2d(14.5, -0.1); 

	// завершаем режим рисования 
	Gl.glEnd(); 

	// выводим подписи осей "x" и "y" 
	PrintText2D(15.5f, 0, "x"); 
	PrintText2D(0.5f, 14.5f, "y"); 

	// вызываем функцию рисования графика 
	DrawDiagram(); 

	// возвращаем матрицу из стека 
	Gl.glPopMatrix(); 

	// выводим текст со значением координат возле курсора 
	PrintText2D(devX * Mcoord_X + 0.2f, (float)ScreenH - devY * Mcoord_Y + 0.4f, "[ x: " + (devX * Mcoord_X - 15).ToString() + " ; y: " + ((float)ScreenH - devY * Mcoord_Y - 15).ToString() + "]"); 

	// устанавливаем красный цвет 
	Gl.glColor3f(255, 0, 0); 

	// включаем режим рисования линий, для того чтобы нарисовать 
	// линии от курсора мыши к координатным осям 
	Gl.glBegin(Gl.GL_LINES); 

	Gl.glVertex2d(lineX, 15); 
	Gl.glVertex2d(lineX, lineY); 
	Gl.glVertex2d(15, lineY); 
	Gl.glVertex2d(lineX, lineY); 

	Gl.glEnd(); 

	// дожидаемся завершения визуализации кадра 
	Gl.glFlush(); 

	// сигнал для обновление элемента реализующего визуализацию. 
	AnT.Invalidate();

} 


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



Источник: Esate.ru
08 Января 2010


Комментарии (из ветки форума)

Авторизируйтесь или Зарегистрируйтесь
чтобы оставлять комментарии.

OpenGL

OpenGL

OpenGL (Open Graphics Library — открытая графическая библиотека, графический API) — спецификация, определяющая независимый от языка программирования платформонезависимый программный интерфейс для написания приложений, использующих двумерную и трёхмерную компьютерную графику.

Регистрация

Регистрируясь, вы принимаете правила сайта. Если вы не получили код подтв. регистрации - не забудьте проверить папку спам.
Логин*
Email*
Пароль*
Подтверждение пароля*
 
Логин*
Код*
 

Восстановление пароля

Пожалуйста, заполните поля, после чего вы получите код подтверждения на ваш E-mail. Если код не пришел в течении нескольких минут - проверьте папку спам.
Логин

или Email
 
Логин*
Код подтверждения*
Новый пароль*
Подтверждение пароля*
 

Авторизация

Пожалуйста, авторизуйтесь, для входа на сайт с помощью соц. сети:
  • Используйте вашу учетную запись на Facebook.com для входа на сайт.
  • Используйте вашу учетную запись VKontakte для входа на сайт.
  • Используйте вашу учетную запись Google для входа на сайт.

или с помощью аккаунта на сайте:

Логин
Пароль