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

GMSAPR

.pdf
Скачиваний:
11
Добавлен:
16.03.2016
Размер:
9.01 Mб
Скачать

181

glEnable(GL_LESS);

// можно задать, как минимум, 8 источников

return 0;

}

Для того, чтобы размер окна OpenGL соответствовал размеру Windowsокна программы, определим в классе CFirstGLView метод обработки сообщения WM_SIZE.

void CFirstGLView::OnSize(UINT nType, int cx, int cy)

{

CView::OnSize(nType, cx, cy);

//Вывод осуществляется в экранно-ориентированную

//прямоугольную область окна OpenGL — видовой порт

glViewport(0,0,cx,cy); // Устанавливаем размеры порта

m_dProportion=(double)cy/cx; // пропорции окна

//Делаем активной матрицу GL_PROJECTION glMatrixMode( GL_PROJECTION ); glLoadIdentity();

//Устанавливаем масштаб по осям координат пропорционально

//соотношению размеров окна.Если окно станет прямоугольным,

//пропорции фигуры не изменятся

glOrtho(-5./m_dProportion, 5./m_dProportion, -5., 5., 0, 10); // Направление взгляда

gluLookAt( 0,0,5, 0, 0, 0, 0, 1, 0 ); // Делаем активной матрицу GL_MODELVIEW glMatrixMode( GL_MODELVIEW );

}

После завершения работы программы (закрытия окна) хорошим тоном будет вернуть все ресурсы, которые мы позаимствовали у системы. Поэтому добавим в класс CFirstGLView обработку еще одного сообщения WM_DESTROY.

void CFirstGLView::OnDestroy()

{

// Делаем контекст отображения неактивным

182

if(wglGetCurrentContext()!=NULL) wglMakeCurrent(NULL, NULL);

// Уничтожаем контекст отображения if(m_hGLRC!=NULL)

{

wglDeleteContext(m_hGLRC); m_hGLRC = NULL;

}

// Уничтожаем контекст устройства if(m_pDC)

delete m_pDC; CView::OnDestroy();

}

Наконец-то подготовительные этапы закончены и можно реализовывать методы, предназначенные для рисования! Метод CFirstGLView::Display() выводит на экран движущиеся трехмерные объекты. Метод CFirstGLView::DisplayBackground() вызывается из метода Display() и предназначен для вывода на экран фонового изображения. В качестве фона используется растровая картинка, загруженная при создании объекта-документа.

void CFirstGLView::Display(void)

{

 

 

double

rf=.5, //радиус фигуры

rt=3.,

//радиус траектории движения

dalfa=2., //шаг поворота фигуры вокруг своей оси dbeta=0.5; //шаг поворота траектории движения

static double

alfa=0., //угол поворота фигуры вокруг своей оси beta=0.; //угол поворота траектории движения

//вычисляем положение фигуры double x=rt*cos(beta*3.14/180.); double y=rt*sin(beta*3.14/180.); if((beta+=dbeta)>360) beta=0;

//выводим на экран

183

if((alfa+=dalfa)>360) alfa=0; //рисуем сцену

glClearColor(.0, .0, .0, .0); // цвет фона окна

//подготовка буфера

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

DisplayBackground();

// рисуем фон

glTranslated(x,y,0);

//смещаем оси координат

//поворачиваем оси координат

 

glRotated(2*alfa+2*beta, 1.0, 1.0, 1.8);

glColor3d(1.0, 0.0, 0.0);

//устанавливаем цвет

//auxSolidSphere(rf);

//сплошная сфера

auxWireSphere(rf*2);

//проволочная сфера

//поворачиваем оси координат

 

glRotated(-2*alfa, 1.0, 1.0, 1.8);

 

glColor3d(0.0, 1.0, 0.0);

//устанавливаем цвет

auxSolidTorus(rf, rf*5);

//сплошной тор

//glTranslated(x,y,0);

//смещаем оси координат

//поворачиваем оси координат

 

glRotated(2*beta+30, 1.0, 1.0, 1.8);

glColor3d(1.0, 1.0, 1.0);

//устанавливаем цвет

auxSolidTorus(rf, rf*7);

//сплошной тор

//auxWireTorus(rf, rf*7);

//проволочный тор

//поворачиваем и смещаем оси координат обратно, //чтобы преобразование не накапливалось glRotated(-4*beta-30, 1.0, 1.0, 1.8); glTranslated(-x,-y,0);

// закончить вывод и преобразования и отобразить объекты glFinish();

SwapBuffers(wglGetCurrentDC());

}

void CFirstGLView::DisplayBackground()

{

glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

// Получаем указатель на растровую картинку фона

184

CRaster *pImage=GetDocument()->GetBackground();

// Проверяем условия, при которых картинка может быть показана if(!m_bViewBackground || pImage==NULL ||

pImage->GetBMWidth()==NULL) return;

// Позиция картинки glRasterPos3d(-5./m_dProportion, -5, -5);

//Прозрачность картинки задается параметром от 0 до 1

//0-прозрачная 1-непрозрачная,

glPixelTransferf(GL_ALPHA_SCALE, 1); // Выводим картинку

glDrawPixels(pImage->GetBMWidth(), pImage->GetBMHeight(), GL_BGR_EXT, GL_UNSIGNED_BYTE, pImage->GetBMDataPtr());

}

Для того, чтобы можно было включать или выключать показ заднего плана, добавим в меню View программы команду Background, а в класс CFirstGLView методы-обработчики сообщений, связанных с этой командой.

void CFirstGLView::OnViewBackground()

{m_bViewBackground=!m_bViewBackground; }

void CFirstGLView::OnUpdateViewBackground(CCmdUI* pCmdUI)

{pCmdUI->SetCheck(m_bViewBackground); }

Метод CFirstGLView::OnViewBackground() управляет значением переменной m_bViewBackground (первоначально установленной в TRUE

конструктором класса CFirstGLView).

Метод CFirstGLView::OnUpdateViewBackground() ставит

галочку напротив имени команды Background в меню, если значение

m_bViewBackground равно TRUE.

185

7.2.1 Модификация класса документа

В класс документа требуется добавить лишь объект класса CRaster и метод, возвращающий указатель на него.

CRaster m_BackgroundImage;

CRaster* GetBackground(){return &m_BackgroundImage;};

Загрузка изображения будет выполняться методом

CRaster::LoadBMP() в конструкторе класса CFirstGLDoc.

CFirstGLDoc::CFirstGLDoc()

{m_BackgroundImage.LoadBMP("Car.bmp"); }

Загрузка изображения выполняется из файла с именем Car.bmp, который должен находиться в одном каталоге с приложением (рабочем каталоге).

7.2.2 Модификация класса приложения

Обновление позиции трехмерного объекта происходит каждый раз при вызове метода CFirstGLView::Display(). Поэтому, для того, чтобы происходило движение объекта, этот метод должен периодически вызываться. Вызывать этот метод можно было бы, например, при обработке сообщений таймера. Для этого нам пришлось бы добавить в программу таймер и метод обработки его сообщений. Однако мы используем другой подход. Виртуальный метод CWinApp::OnIdle() вызывается каждый раз, когда очередь сообщений окна пуста и приложение простаивает. Для этого переопределим метод OnIdle() в классе CFirstGLApp.

BOOL CFirstGLApp::OnIdle(LONG lCount)

{

// Вызываем метод рисования

( (CFirstGLView*) ((CMainFrame*)m_pMainWnd)->GetActiveView() ) ->Display();

return 1;// Повторять вызов OnIdle

186

}

Вряд ли, конечно, этот подход понадобится в серьёзных приложениях, зато позволит нам обеспечить вычисление параметра FPS (Frame per second) при различной нагрузке на приложение. Это полезно в познавательных целях.

7.3 Заключение

Результат работы программы FirstGL показан на рис. 1. Конечно, прочитав эту главу, вы не научитесь в совершенстве использовать OpenGL. Однако кое-какую "пищу для ума" можно извлечь из рассмотренного материала, можно поэкспериментировать с программой. Например, заменив в методе CFirstGLView::Display() функцию auxSolidTorus() на auxWireTorus(), можно получить изображение каркасного (проволочного) тора, можно изменить траекторию движения объектов, составить из фигур новую композицию, поменять положение источника освещения и т. д.

Рис. 1. FirstGL в работе

187

Приложение 1. Основные понятия и принципы объектно-ориентированного программирования

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

Гради Буч. "Объектно-ориентированный анализ и проектирование"

Основными понятия и принципы объектно-ориентированного программирования (ООП) являются — объект, класс, инкапсуляция,

наследование, полиморфизм.

Преимущества объектно-ориентированного подхода:

интуитивная близость произвольной предметной области;

возможность моделирования сколь угодно сложной предметной области;

событийно-ориентированный подход (динамика объектов и возможность манипулирования ими посредством методов приводят к управлению объектами посредством событий);

высокий уровень абстракции;

возможность повторного использования описаний (основано на обращении к полям и методам извне описания классов, а также на использовании механизма наследования);

параметризация методов обработки объектов (основана на использовании механизма интерфейсов).

Каждый объект принадлежит некоторому классу. Класс является обобщением понятия типа данных и задает свойства и поведение объектов класса, называемых также экземплярами класса (instantiation). Класс можно представлять как объединение данных, а так же функций (методов, method), предназначенных для их обработки. Переменные определяют состояние объекта (state), методы определяют поведение объекта (behavior).

Другими словами:

188

Классом называется общая сущность, которая может быть определена как совокупность элементов.

Объектом некоторого класса называется математическое представление сущности реального мира (или предметной области), которое используется для моделирования.

Свойством (или атрибутом) называется пропозициональная функция, определенная на произвольном типе (данных).

Методом (или функцией) называется операция, определенная над объектами некоторого класса.

Теперь разберёмся с отношениями классов. Пусть определен класс А,

тогда можно определить новый класс В, наследующий свойства и поведение объектов класса А. Это означает, что в классе В будут определены переменные и методы класса А. Класс В в данном случае является производным

(наследованным, derived) от класса А. Класс А является родительским (базовым, parent, base) по отношению к В. В производном классе можно задать новые свойства и поведение, определив новые переменные и методы. Класс B,

производный от класса А, может быть базовым для класса С и т. д. Все порожденные классы называются наследниками, а классы, от которых они порождены — предками.

Можно также переопределить (перегрузить) существующие методы базового класса. Перегрузка (overloading) метода класса А — это определение в классе В метода с именем, уже являющимся именем какого-либо метода класса

А. Метод базового класса можно объявить виртуальным (с атрибутом virtual). Тогда при переопределении метода в производном классе должно сохраняться число параметров метода и их типы. Виртуальность обеспечивает возможность написания полиморфных функций, аргумент которых может иметь разные типы (классы) при разных обращениях к функции, и при этом будут выполняться действия, специфичные для типа фактически переданного аргумента. Полиморфизм – одно из замечательнейших свойств ООП.

189

Например, пусть в классе А определен виртуальный метод

VirtualMethod(), который выдает сообщение: "Я метод класса А".

Переопределим этот метод в классе B так, чтобы он выдавал сообщение: "Я

метод-член класса В".

class A // Определяем класс A

{

public:

// Виртуальный метод класса A virtual void VirtualMethod()

{printf("Я метод класса А");};

}

class B: public A // Определяем класс B, производный от класса A

{

public:

// Виртуальный метод класса B void VirtualMethod()

{ printf(""Я метод класса B");};

}

//Полиморфная функция. Обратите внимание: в качестве аргумента

//она принимает указатель на класс А, базовый по отношению к

//классу B.

void ShowMessage(A* pClass)

{// Вызов метода VirtualMethod()объекта pClass pClass ->VirtualMethod();

}

A ObjectA; // Создаем объект ObjectA класса А

B ObjectB; // Создаем объект ObjectB класса В

//Вызываем полиморфную функцию с аргументом — указателем

//на объект класса А

//Будет выдано сообщение "Я метод класса А"

190

ShowMessage(&ObjectA);

//Вызываем полиморфную функцию с аргументом — указателем

//на объект класса В

//Будет выдано сообщение "Я метод класса В" ShowMessage(&ObjectB);

Данный пример достаточно прост, тем не менее, он демонстрирует полиморфизм во всей его красе. Мы наследуем классы от некоторого базового класса (с виртуальными методами) и передаём указатели на них некоторому методу, который умеет работать с указателем на базовый класс.

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

Модульность позволяет:

Упростить разработку и реализацию;

Облегчить чтение программ;

Упростить настройку и модификацию;

Облегчить работу с данными, имеющими сложную структуру;

Избежать чрезмерной детализации алгоритмов;

Разделить работу по реализации модулей между программистами.

Рассмотрим пример из области САПР: допустим, нам необходимо реализовать некоторый редактор электрических схем. Все классы, представляющие топологические элементы (индуктивность, резистор, конденсатор и т.п.) логично наследовать от некоторого базового класса

CElement с виртуальными методами Calculate, SetParameter,

GetParameter, Draw, и т.д. Таким образом, среда редактирования и обсчёта

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]