netlib.narod.ru | < Назад | Оглавление | Далее > |
Давайте взглянем на программу, которая создала сферы и заставила их вращаться в окне. Даже если кое-что из увиденного покажется вам загадочным, постарайтесь понять то, что можете, и поверьте мне на слово: по мере чтения смысл происходящего будет проясняться. Я пока лишь собираюсь показать, как мало усилий от вас требуется, а не раскладывать все по полочкам. Вот как выглядят две функции из файла Basic.cpp, которые подготавливают макет и приводят его в движение:
Листинг 1.1. Функции InitInstance и OnIdle |
BOOL CBasicApp::InitInstance() { // Создать главное окно C3dWnd* pWnd = new C3dWnd; pWnd->Create("3D Basics", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 50, 50, 400, 350); m pMainWnd = pWnd; pWnd->UpdateWindow(); // Создать исходный макет, к которому // будут присоединяться объекты static C3dScene scene; scene.Create(); // Установить интенсивность рассеянного света scene.SetAmbientLight(0.4, 0.4, 0.4); // Добавить направленный источник света для создания бликов static C3dDirLight dl; dl.Create(0.8, 0.8, 0.8); scene.AddChild(&dl); dl.SetPosition(-2, 2, -5); dl.SetDirection(l, -1, 1); // Создать большую белую сферу static C3dShape sh1; sh1.CreateSphere(1); // Присоединить к макету большую белую сферу scene.AddChild(&sh1) ; // Создать маленькую синюю сферу static C3dShape sh2; sh2.CreateSphere(0.3); sh2.SetColor(0, 0, 1); // Присоединить синюю сферу к белой sh1.AddChild(&sh2); // Задать положение синей сферы // по отношению к белой sh2.SetPosition (0, 0, -2); // Создать маленькую красную сферу static C3dShape sh3; sh3.CreateSphere (0.15); sh3.SetColor(l, 0, 0); // Присоединить красную сферу к белой sh1.AddChild(&sh3); // Задать положение красной сферы // по отношению к белой sh3.SetPosition(0, 0, 5); // Начать медленно вращать белую сферу // вокруг оси 1, 1, 0 sh1.SetRotation(1, 1, 0, 0.015); // Присоединить весь макет к сцене pWnd->SetScene(&scene); return TRUE; } BOOL CBasicApp::OnIdle(LONG lCount) { BOOL bMore = CWinApp::Onldle(lCount); if (m_pMainWnd) { C3dWnd* pWnd = (C3dWnd*) m_pMainWnd; // Приказать трехмерному окну сместить макет //на одну единицу //и перерисовать окно if (pWnd->Update(l)) { bМоrе = TRUE; } } return bMore; } |
Функция CBasicApp::InitInstance создает окно и те объекты, которые образуют макет, а функция CBasicApp::OnIdle обновляет положение этих объектов во время пассивной (idle) работы приложения. Класс CBasicApp был сгенерирован AppWizard во время создания приложения. Он является производным от класса CWinApp, входящего в MFC и обеспечивающего основную функциональность Windows-приложения. Давайте подробно рассмотрим, что же делают эти две функции.
Первое, что происходит в InitInstance, — построение окна, в котором будет отображаться трехмерный макет. Окно класса CWinApp создается в качестве главного окна приложения. Класс C3dWnd принадлежит библиотеке 3dPlus, как и все остальные рассматриваемые нами классы, имена которых начинаются с префикса C3d (исходный текст библиотеки 3dPlus находится на прилагаемом диске CD-ROM вместе с другими примерами). Указатель на созданное окно присваивается переменной m_pMainWnd, являющейся членом базового класса CWinApp из MFC. Указатель на окно используется кодом MFC при обработке сообщений приложением и т.д. Завершающим шагом в создании окна является вызов функции UpdateWindow для прорисовки окна на экране.
Затем создается объект класса C3dScene. В него входят все элементы одной сцены, отображаемой в трехмерном окне (например, источники света, трехмерные объекты и т.д.).
Следующим шагом является задание источников света. Мы пользуемся двумя различными источниками света: рассеянным и направленным. Рассеянный свет равномерно освещает все объекты. Если не пользоваться другим освещением, при отсутствии бликов объекты выглядят плоскими. Направленный свет действует по принципу прожектора. Сам по себе он дает очень резкий контраст и не позволяет рассмотреть затемненные части объектов. Когда объект освещается направленным источником света, интенсивность цвета поверхности меняется так, чтобы имитировать яркий луч, падающий из одного направления. Используя оба источника света, мы получаем приличную имитацию объемности и можем рассмотреть все участки объектов, даже если они находятся в тени по отношению к направленному свету. В классе C3dScene имеется встроенный источник рассеянного света, так что нам остается лишь задать его интенсивность. Сцену (stage) можно упрощенно рассматривать как окно приложения, в котором воспроизводится текущий макет. Интенсивность рассеянного света задается следующим образом:
scene.SetAmbientLight(0.4, 0.4, 0.4);
Свет состоит из различных оттенков красного, зеленого и синего цветов. В данном случае интенсивности красной, зеленой и синей составляющих выбираются так, чтобы образованный ими свет был белым и не искажал цветов объектов. Интенсивность цветовых составляющих может меняться от нуля до единицы (от 0,0 до 1,0). В программировании для Windows обычно применяются целые значения цветовых составляющих, лежащие в диапазоне от 0 до 255. Использование значений с плавающей точкой (double) может показаться нерациональным, однако следует помнить, что данные значения используются для математического определения цветов граней объектов, входящих в макет. Для этого приходится производить многочисленные тригонометрические и иные вычисления, так что на самом деле использование значений с плавающей точкой выглядит вполне оправданным. Конечно же, страстные поклонники C++ могут самостоятельно определить класс для цвета и переделать некоторые функции библиотеки 3dPlus, чтобы они в качестве аргумента принимали цветовой объект вместо набора отдельных составляющих. Я не стал этого делать лишь для того, чтобы в некоторых важных случаях можно было сразу увидеть значения RGB-компонент.
В отличие от рассеянного света, который требуется лишь настроить, источник направленного света необходимо сначала создать и присоединить к макету, после чего можно будет задавать его позицию и направление. Позиция характеризуется координатами по осям X, Y и Z. Чтобы задать ориентацию луча, мы создаем вектор, направление которого соответствует направлению светового потока. Пока вам не стоит беспокоиться о координатах и векторах; просто считайте, что свет падает с левой верхней точки сцены.
ЗАМЕЧАНИЕ |
Я расположил направленный источник света наверху слева, потому что такое направление используется в объемных элементах управления (кнопках) Windows. |
На рис. 1.2 изображена сцена с установленным направленным источником света, а также с осями координат X, Y и Z.
Рис. 1.2. Сцена, оси координат и направленный источник света
После настройки освещения можно присоединять к макету объекты. Мы создаем три сферы и присоединяем их к макету функцией AddChild. Можете считать, что функция AddChild закрепляет объект-потомок за родительским объектом. В случае первой создаваемой сферы родительским объектом является сам макет, однако позднее мы увидим, что иерархия может быть значительно более сложной. Цвет сфер задается с помощью красных, зеленых и синих составляющих, как и для источников света. По умолчанию объектам присваивается белый цвет.
Первая сфера остается в своей позиции по умолчанию — в точке 0, 0, 0. Красная и синяя сферы удаляются на некоторое расстояние от нее с помощью функции SetPosition. Значения, передаваемые в качестве параметров SetPosition, измеряются в «единицах модели» (model units), которые могут выбираться достаточно произвольно. Позже мы научимся определять размеры объектов.
Последнее, что остается сделать, — это привести большую сферу во вращение и присоединить весь макет, вместе с источниками света и сферами, к трехмерному окну. Аргументами функции C3dShape::SetRotation являются ось вращения, заданная в виде вектора с координатами X, Y и Z, а также частота вращения. На самом деле вращение объекта — достаточно нетривиальная задача, однако функция SetRotation позволяет очень просто реализовывать простейшие виды вращения, вроде использованного в нашем случае. В главах 5 и 6 мы подробнее поговорим о вращении объектов.
Функция CBasicApp::OnIdle вызывается структурным кодом MFC в тот момент, когда приложение ведет себя пассивно, то есть когда у него в очереди нет сообщений, подлежащих обработке, и при этом процессор не занят другими работающими приложениями (что на самом деле происходит большую часть времени). Мы используем период пассивности для того, чтобы переместить макет к следующему положению и перерисовать его в окне. Все это происходит при вызове функции C3dWnd::Update(pWnd->Update(1)). Аргумент функции C3dWnd::Update определяет, насколько необходимо сместить макет. Этот аргумент будет использован нами позднее, чтобы обеспечить постоянную скорость перемещения макета даже при изменяющемся периоде пассивности. А пока мы пользуемся принятым по умолчанию значением 1, чтобы макет перемещался на одну (выбираемую достаточно произвольно) единицу.
Как видите, мы не стали писать большую программу, а скорее прошлись по некоторым основным положениям. Вы заслужили особой похвалы, если обратили внимание на несколько статических переменных, которыми я воспользовался. Я ввел их для того, чтобы программа вышла как можно короче. По мере ее усовершенствования мы сделаем так, что статические переменные станут не нужны.
Рискуя повториться, я все же напомню: не стоит волноваться, даже если не все было понятно с первого раза. В дальнейшем мы еще не раз вернемся к тексту этой программы. А пока давайте рассмотрим некоторые важные моменты.
netlib.narod.ru | < Назад | Оглавление | Далее > |