Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Popov_vorobev

.pdf
Скачиваний:
7
Добавлен:
22.06.2018
Размер:
1.57 Mб
Скачать

begin

FillChar (pfd, SizeOf (pfd), 0); nPixelFormat: = ChoosePixelFormat (hdc, @pfd);

SetPixelFormat (hdc, nPixelFormat, @pfd); end;

В данном методическом пособии мы не будем подробно останавливаться на теме описания формата пикселя. Скажем только, что используется он для определения характеристик используемого оборудования, таких как конфигурация буфера цвета и вспомогательных буферов. Данные о формате пикселя хранятся в специальной структуре. Её тип — TpixelFormatDescriptor. Более подробную информацию о формате пикселя можно прочитать, опираясь на библиографический список.

Затем в обработчике события OnCreate формы необходимо написать две следующие строчки:

SetDCPixelFormat(Canvas.Handle); hrc:= wglCreateContext(Canvas.Handle);

Первой мы вызываем созданную нами ранее процедуру. Второй — создаем контекст воспроизведения. В обоих случаях, в качестве параметра мы передаем ссылку на текущее окно. Эти действия, как правило, выполняются один раз, как было уже написано, в обработчике события OnCreate формы. В обработчике же события onDestroy формы, необходимо написать следующую строчку:

wglDeleteContext (hrc);

Функция wglDeleteContext удаляет контекст воспроизведения, освобождая память.

Далее рассмотрим ещё четыре строчки кода, которые нам обязательно придется использовать в лабораторной работе:

wglMakeCurrent (Canvas.Handle, hrc); glClearColor (0.5, 0.5, 0.75, 1.0);

50

glClear (GL_COLOR_BUFFER_BIT); wglMakeCurrent (0, 0);

Первая и четвертая строчки, по сути, являются своеобразным «блоком», внутри которого проводятся все операции OpenGL. Первая строчка делает контекст воспроизведения текущим для вывода, а четвертая, соответственно, освобождает его. Между этими двумя строчками можно вызывать различные функции OpenGL. В данном случае, вызываются функции glClearColor и glClear. С их помощью производится очистка экрана. Причем, функция glClearColor задает цвет, которым будет очищен экран. Таким образом, две эти функции используются для задания фонового цвета.

Рисование примитивов в OpenGL.

Первое действие, которое необходимо выполнить для начала рисования — это задание области вывода. Выполняется оно с помощью функции glViewPort(). В качестве параметров ей передаются четыре целочисленных значения. Первые два — координаты левого нижнего угла области, последние два — её ширина и высота. Таким образом, для вывода какой-нибудь графической информации в область, размером 200х200 пикселей, начинающуюся ровно в левом нижнем углу, необходимо написать следующую строчку:

glViewPort (0, 0, 200, 200) ;

Далее необходимо определить размер и цвет элемента, который мы будем рисовать. Размер точек и линий задается разными способами:

glPointSize

(20);

// размер

точек

glLineWidth

(15);

//толщина

линий

Цвет можно задать с помощью следующей функции: glColor3f (1.0, 1.0, 1.0); // цвет примитивов

Итак, далее следует блок рисования. Для этого используются функции glBegin() и glEnd(). Функции glBegin() передается кон-

51

станта, определяющая тип примитива, который будет отрисовываться в этом блоке. Затем внутри блока определенное количество раз вызывается функция glVertex2f(). Она принимает в качестве параметров координаты точки. Эта функция определяет только одну точку в области вывода. Но если вызовов этой функции в блоке несколько, то точки будут группироваться в соответствии с константой, переданной функции glBegin(). Например, если эта константа GL_POINTS, а в блоке определяются две точки, то они так и будут выведены двумя отдельными точками, а если константа GL_LINES, то эти точки уже будут соединены линией.

Обращаем внимание, что блок Delphibegin…end и блок OpenGL glBegin(); glEnd(); могут быть вложенными друг в друга, но пересечений быть не должно. Если мы вызываем внутри блока begin…end функцию glBegin(), то функцию glEnd() мы также должны вызвать внутри begin…end. И наоборот.

Теперь необходимо отметить один очень важный момент: внутри области вывода OpenGL координаты определяются не так как на обычной форме (от нуля до ширины и высоты). Для OpenGL, левому нижнему углу области вывода соответствует координата (-1, -1), правому верхнему — (1, 1), а в центре области находится координата (0, 0).

Ещё одно действие, которое необходимо выполнить для вывода графической информации — это вызов функции SwapBuffers(). В качестве параметра ей передается ссылка на окно.

Теперь учитывая всё, что было написано выше, приведем пример кода, который необходимо написать, например, в обработчике события OnClick какой-нибудь кнопки на форме, чтобы вывести пять точек в поле размером 200х200 пикселей:

wglMakeCurrent (Canvas.Handle, hrc); glClearColor (0. 5, 0. 5, 0. 75, 1. 0); glClear(GL_COLOR_BUFFER_BIT);

glBegin(GL_POINTS);

glVertex2f (-1, -1); // левыйнижнийугол glVertex2f (-1, 1);

glVertex2f (0, 0);

52

glVertex2f (1, -1);

glVertex2f (1, 1); // правый верхний угол glEnd;

SwapBuffers(Canvas.Handle) wglMakeCurrent (0, 0);

Еще стоит обратить внимание на то, какие используются типы данных в OpenGL и как формируются имена функций. В OpenGL имеется набор собственных типов данных, большинству из которых соответствуют типы данных языка, на котором пишется программа. Имя каждого из них начинается с префикса GL, например: GLbyte, GLfloat. Полный список можно найти в документации по OpenGL. Имена функций состоят из префикса gl, именной части, числа, обозначающего, сколько параметров принимает функция и суффикса, описывающего тип этих параметров. Например, функция glVertex2f принимает 2 параметра типа GLfloat. Соответствие суффиксов типам данных также можно найти в документации.

Практическая часть

Итак, основные моменты, касающиеся работы с OpenGL, мы рассмотрели. Теперь перейдем непосредственно к лабораторной работе. Как говорилось выше, задача работы — написать часы со стрелкой, которые будут перерисовываться средствами OpenGL, в соответствии с системным временем.

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

SendMessage (Form1.Handle, WM_PAINT, 0, 0);

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

53

В общем виде, уравнение окружности выглядит так (рис. 27):

(x – a)2 + (y b)2 = R2.

Соответственно, если центр окружности располагается в точке с координатами (0, 0), то уравнение принимает вид

x2 + y2 = R2.

y

y1B

b

A

C

 

a x1

x

Рис. 27. График окружности

Геометрическое определение тригонометрических функций.

Теперь вспомним геометрическое представление тригонометрических функций «синус» и «косинус». Ниже представлена единичная окружность, на которой наглядно продемонстрировано, что же собой представляют данные тригонометрические функции

(рис. 28).

Как говорилось ранее, точки в области вывода OpenGL имеют координаты от (-1, -1) до (1, 1), а центр как раз располагается в точке (0, 0). Если рисовать окружность из точки (0, 0) радиусом 1, то и получится та самая единичная окружность.

54

cos α

sin

α α

Рис. 28. Геометрическое представление синуса и косинуса

Требования к программе

Программа обязательно должна иметь круглый циферблат и три стрелки разного размера. Часы должны правильно показывать время и при движении стрелки не должно происходить никакого «мерцания» формы. Кроме того, каждый студент должен дополнить минимальный интерфейс каким-то собственным оформлением. Для этого рекомендуется почитать литературу по OpenGL, список которой в конце данного методического пособия. На рис. 29 представлен скриншот готового приложения без дополнительного внешнего оформления.

Рис. 29. Окно приложения «Часы»

55

Лабораторная работа № 7

Трехмерная графика в OpenGL.

Теоретическая часть

Вданной лабораторной работе мы продолжим знакомство с библиотекой OpenGL и её использованием в среде Delphi. Как вам уже известно, для вывода графических объектов в OpenGL используется функция glVertex. Конечно, существуют и другие способы для вывода сложных объектов. Однако в рамках данного курса мы ограничимся использованием только этой. В частности, не будем прибегать к загрузке готовых моделей, созданных в 3D-редакторах. Итак, как говорилось ранее, чтобы вывести на экран графический примитив, необходимо определить блок glBegin() … glEnd(). В качестве параметра функции glBegin() нужно передать константу, указывающую, какой именно примитив должен быть прорисован, и внутри блока вызвать функцию glVertex, которая определит вершины примитива.

Впредыдущей лабораторной работе мы рассматривали только функцию glVertex2f(), использующуюся для рисования двумерных объектов, которая принимает два вещественных параметра: координату x и координату y. Для рисования трехмерных объектов используется, по сути, та же функция, только принимающая три параметра: x, y, z. Эта функция выглядит так: glVertex3f(). Таким образом, мы добавляем плоской фигуре третью координату, благодаря чему переводим её в трехмерное пространство. Однако для построения объемных фигур, в большинстве случаев, необходимо создавать некоторую совокупность плоских фигур в трехмерном пространстве. В данном методическом пособии пока были рассмотрены только способы рисования точек и линий. Для рисования других примитивов необходимо знать, какую константу передать функции glBegin(). Всего таких констант десять: GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_LINE_LOOP, GL_TRIANGLES,

56

GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS, GL_QUAD_STRIP, GL_POLYGON.

Далее будет приведено краткое описание того, как соединяются точки, взависимости отпереданнойфункции glBegin() константы:

GL_POINTS — точки не соединяются;

GL_LINES — пары точек соединяются в отдельные линии; GL_LINE_STRIP — все точки соединяются последовательно, в

результате чего получается ломаная линия;

GL_LINE_LOOP — точки также соединяются последовательно, однако, в данном случае, соединяется ещё и первая точка с последней;

GL_TRIANGLES — точки соединяются по три. Лишние точки не выводятся;

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

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

GL_QUADS — точки соединяются по четыре; GL_QUAD_STRIP — точки соединяются по такому же прин-

ципу, что и в случае с константой GL_TRIANGLE_STRIP, только не по три, а по четыре;

GL_POLYGON — все точки соединяются в один выпуклый многоугольник.

Итак, как было сказано ранее, объемные фигуры формируются из плоских фигур, нарисованных в трехмерном пространстве. Например, куб будет состоять из шести квадратов:

glBegin (GL_QUADS); glVertex3f (1.0, 1.0, 1.0); glVertex3f (-1.0, 1.0, 1.0); glVertex3f (-1.0, -1.0, 1.0); glVertex3f (1.0, -1.0, 1.0); glEnd;

57

glBegin (GL_QUADS); glVertex3f (1.0, 1.0, -1.0); glVertex3f (1.0, -1.0, -1.0); glVertex3f (-1.0, -1.0, -1.0); glVertex3f (-1.0, 1.0, -1.0); glEnd;

glBegin (GL_QUADS); glVertex3f (-1.0, 1.0, -1.0); glVertex3f (-1.0, 1.0, -1.0); glVertex3f (-1.0, -1.0, 1.0); glVertex3f (-1.0, -1.0, 1.0); glEnd;

glBegin (GL_QUADS); glVertex3f (1.0, 1.0, 1.0); glVertex3f (1.0, -1.0, 1.0); glVertex3f (1.0, -1.0, -1.0); glVertex3f (1.0, 1.0, -1.0); glEnd;

glBegin (GL_QUADS); glVertex3f (-1.0, 1.0, -1.0); glVertex3f (-1.0, 1.0, 1.0); glVertex3f (1.0, 1.0, 1.0); glVertex3f (1.0, 1.0, -1.0); glEnd;

glBegin(GL_QUADS);

glVertex3f (-1.0, -1.0, -1.0); glVertex3f (1.0, -1.0, -1.0); glVertex3f (1.0, -1.0, 1.0); glVertex3f (-1.0, -1.0, 1.0); glEnd;

Но в таком виде код выведет на экран прямоугольник. Точнее, фигура, которую он выведет, будет выглядеть как прямоугольник.

58

Это связано с тем, что все его грани окрашены в один цвет, а расположен он на сцене так, что мы его видим ровно спереди.

Для придания объекту трехмерности перед началом его прорисовки необходимо добавить следующие строчки:

glFrustum (-1, 1, -1, 1, 3, 10); glTranslatef (0.0, 0.0, -8.0); glRotatef (30.0, 1.0, 0.0, 0.0); glRotatef (70.0, 0.0, 1.0, 0.0);

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

Ещё один важный момент, который нельзя не отметить — это необходимость использования функций glPushMatrix и glPopMatrix. Все основные действия должны выполняться между этими функциями. Иначе, при каждой перерисовке окна, сцена будет уменьшаться в размерах.

Несмотря на все эти действия, объект ещё не будет казаться действительно объемным, так как все его грани будут окрашены в один цвет. Для того чтобы это исправить, необходимо добавить на сцену «источник света» и указать для каждой грани вектор нормали. Этот вектор используется для расчета цветовых параметров пикселей. Яркость каждой грани зависит от того, под каким углом на неё падает свет. Таким образом, даже если грани изначально окрашены одинаково, то после добавления на сцену источника света и указания для каждой грани вектора нормали, оттенок их будет различаться.

Источник света задается следующими строчками кода: glEnable (GL_LIGHTING); glEnable(GL_LIGHTO);

59