netlib.narod.ru | < Назад | Оглавление | Далее > |
Прожектор излучает свет в форме конуса. Местоположение источника света влияет на местоположение вершины конуса, а ориентация источника света влияет на местоположение основания светового конуса.
В действительности, свет, испускаемый прожектором, лучше описать в терминах двух конусов: внутреннего и внешнего. Внешний конус определяет область, освещаемую прожектором. За пределами внешнего конуса свет отсутствует. Внутренний конус определяет область с максимальной интенсивностью света. В области между этими двумя конусами освещенность постепенно уменьшается от максимальной интенсивности во внутреннем конусе до отсутствия света за пределами внешнего конуса.
Внутренний конус называется конусом светового пятна (umbra cone) и определяется углом светового пятна (umbra angle). Внешний конус называется конусом зоны освещенности (penumbra cone) и определяется углом зоны освещенности (penumbra angle). Эти конусы показаны на рис. 6.8.
Рис. 6.8. Анатомия прожектора
Интерфейс Direct3DRMLight предоставляет следующие функции для настройки и контроля угла светового пятна и угла зоны освещенности:
В приложении Spotlight анимированный прожектор освещает три сферических сетки. Угол светового пятна и угол зоны освещенности прожектора могут быть изменены во время работы программы с помощью команд меню Beam. Окно приложения Spotlight изображено на рис. 6.9.
Рис. 6.9. Приложение Spotlight
Приложение Spotlight демонстрирует использование следующих технологий:
Чтобы продемонстрировать альтернативный способ, отличный от использовавшегося в других демонстрационных программах, в приложении Spotlight вместо интерфейса Direct3DRMMeshBuilder используется интерфейс Direct3DRMMesh. В этом нет ничего необычного, так как приложение Spotlight требует повышенного быстродействия, что и достигается использованием интерфейса Direct3DRMMesh.
Приложение Spotlight использует класс RMWin в качестве базового для своего собственного класса SpotlightWin:
class SpotlightWin : public RMWin { public: SpotlightWin(); BOOL CreateScene(); protected: //{{AFX_MSG(SpotlightWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); afx_msg void OnBeamNormal(); afx_msg void OnBeamNarrow(); afx_msg void OnBeamWide(); afx_msg void OnUpdateBeamNormal(CCmdUI* pCmdUI); afx_msg void OnUpdateBeamNarrow(CCmdUI* pCmdUI); afx_msg void OnUpdateBeamWide(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: static void MoveLight(LPDIRECT3DRMFRAME frame, void* arg, D3DVALUE delta); private: LPDIRECT3DRMMESH mesh1, mesh2, mesh3; LPDIRECT3DRMLIGHT spotlight; int beamwidth; };
Определение класса SpotlightWin слегка отличается от определений классов других демонстрационных программ, рассмотренных в этой главе. Первым и основным отличием является большее число защищенных функций. Шесть защищенных функций подобны тем, которые используются в других демонстрационных программах, и предназначены для реализации меню Render. Новые функции, которые мы рассмотрим чуть позже, предназначены для реализации меню Beam.
Другим отличием является объявление функции MoveLight(), являющейся функцией обратного вызова, используемой для анимации прожектора.
Члены данных класса также отличаются. В предыдущих демонстрационных программах использовался единственный указатель на интерфейс Direct3DRMMeshBuilder. В классе SpotlightWin объявлены три указателя на интерфейс Direct3DRMMesh. Эти три указателя применяются для доступа к трем, используемым в приложении сферическим сеткам.
В классе также объявлен указатель на интерфейс Direct3DRMLight. Этот указатель используется функциями, обрабатывающими команды меню Beam. Кроме того, эти функции используют переменную класса beamwidth для хранения текущих параметров прожектора.
Функция CreateScene() приложения Spotlight отвечает за создание сеток и прожектора, а также устанавливает функцию обратного вызова, использующуюся для анимации прожектора. Код функции SpotlightWin::CreateScene() представлен в листинге 6.5.
Листинг 6.5. Функция SpotlightWin::CreateScene() |
BOOL SpotlightWin::CreateScene() { //----- СЕТКИ -------- D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_SPHEREMESH); resinfo.lpType = "MESH"; LPDIRECT3DRMMESHBUILDER builder; d3drm->CreateMeshBuilder(&builder); builder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); builder->SetColorRGB(D3DVALUE(1.0), D3DVALUE(0.0), D3DVALUE(0.0)); builder->CreateMesh(&mesh1); builder->SetColorRGB(D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0)); builder->CreateMesh(&mesh2); builder->SetColorRGB(D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(1.0)); builder->CreateMesh(&mesh3); builder->Release(); builder = 0; //----- ФРЕЙМЫ ДЛЯ СЕТОК -------- LPDIRECT3DRMFRAME frame1; d3drm->CreateFrame(scene, &frame1); frame1->SetPosition(scene, D3DVALUE(-2), D3DVALUE(0), D3DVALUE(0)); frame1->AddVisual(mesh1); frame1->Release(); frame1 = 0; LPDIRECT3DRMFRAME frame2; d3drm->CreateFrame(scene, &frame2); frame2->SetPosition(scene, D3DVALUE(2), D3DVALUE(0), D3DVALUE(0)); frame2->AddVisual(mesh2); frame2->Release(); frame2 = 0; LPDIRECT3DRMFRAME frame3; d3drm->CreateFrame(scene, &frame3); frame3->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(2)); frame3->AddVisual(mesh3); frame3->Release(); frame3 = 0; //------- ПРОЖЕКТОР ------ d3drm->CreateLightRGB(D3DRMLIGHT_SPOT, D3DVALUE(0.8), D3DVALUE(0.8), D3DVALUE(0.8), &spotlight); OnBeamNormal(); //------ ФРЕЙМ ПРОЖЕКТОРА -------- LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->SetPosition(scene, D3DVALUE(0), D3DVALUE(10), D3DVALUE(-10)); lightframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-1), D3DVALUE(1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); lightframe->AddLight(spotlight); lightframe->AddMoveCallback(MoveLight, NULL); lightframe->Release(); lightframe = 0; //------ КАМЕРА ------ d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(6), D3DVALUE(-6)); camera->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-1), D3DVALUE(1.1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport); return TRUE; } |
Функция CreateScene() выполняет следующие действия:
Давайте взглянем на код, реализующий первый этап:
D3DRMLOADRESOURCE resinfo; resinfo.hModule = NULL; resinfo.lpName = MAKEINTRESOURCE(IDR_SPHEREMESH); resinfo.lpType = "MESH"; LPDIRECT3DRMMESHBUILDER builder; d3drm->CreateMeshBuilder(&builder); builder->Load(&resinfo, NULL, D3DRMLOAD_FROMRESOURCE, NULL, NULL); builder->SetColorRGB(D3DVALUE(1.0), D3DVALUE(0.0), D3DVALUE(0.0)); builder->CreateMesh(&mesh1); builder->SetColorRGB(D3DVALUE(0.0), D3DVALUE(1.0), D3DVALUE(0.0)); builder->CreateMesh(&mesh2); builder->SetColorRGB(D3DVALUE(0.0), D3DVALUE(0.0), D3DVALUE(1.0)); builder->CreateMesh(&mesh3); builder->Release(); builder = 0;
В самом начале выполняется объявление и инициализация экземпляра структуры D3DRMLOADRESOURCE. Структура resinfo идентифицирует сферическую сетку, которую мы будем использовать.
Далее объявляется указатель на интерфейс Direct3DRMMeshBuilder с именем builder. Для инициализации этого указателя вызывается функция CreateMeshBuilder() интерфейса Direct3DRM. Функция Load() применяется для загрузки сетки. Адрес подготовленной ранее структуры resinfo передается функции Load() в ее первом аргументе.
Затем инициализируются три указателя на интерфейс Direct3DRMMesh. Каждый из них инициализируется с помощью функции CreateMesh() интерфейса Direct3DRMMeshBuilder. После каждого вызова функции CreateMesh() конструктору сеток назначается новый цвет. В результате будут созданы три сетки различных цветов, каждая из которых представлена интерфейсом Direct3DRMMesh. Сразу после создания этих трех сеток указатель builder освобождается.
На втором этапе работы функции CreateScene() выполняется создание и размещение трех фреймов, по одному для каждой из сеток:
LPDIRECT3DRMFRAME frame1; d3drm->CreateFrame(scene, &frame1); frame1->SetPosition(scene, D3DVALUE(-2), D3DVALUE(0), D3DVALUE(0)); frame1->AddVisual(mesh1); frame1->Release(); frame1 = 0; LPDIRECT3DRMFRAME frame2; d3drm->CreateFrame(scene, &frame2); frame2->SetPosition(scene, D3DVALUE(2), D3DVALUE(0), D3DVALUE(0)); frame2->AddVisual(mesh2); frame2->Release(); frame2 = 0; LPDIRECT3DRMFRAME frame3; d3drm->CreateFrame(scene, &frame3); frame3->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(2)); frame3->AddVisual(mesh3); frame3->Release(); frame3 = 0;
Каждый фрейм создается функцией CreateFrame() интерфейса Direct3DRM, после чего помещается на предназначенное ему место сцены. Третий фрейм помещается за первыми двумя. Каждая из созданных ранее сеток присоединяется к одному из фреймов с помощью функции AddVisual(). После того, как сетка присоединена к фрейму, указатель на фрейм освобождается.
На третьем этапе, код которого показан ниже, создается прожектор:
d3drm->CreateLightRGB(D3DRMLIGHT_SPOT, D3DVALUE(0.8), D3DVALUE(0.8), D3DVALUE(0.8), &spotlight); OnBeamNormal();
Вначале для инициализации указателя spotlight вызывается функция CreateLightRGB() интерфейса Direct3DRM. Константа D3DRMLIGHT_SPOT указывает, что мы создаем прожектор. Для красной, зеленой и синей составляющих цвета мы указываем значение 0.8, чтобы цвет нашего источника света был светло-серым.
Затем вызывается функция OnBeamNormal(), являющаяся обработчиком сообщения для пункта Normal меню Beam. Здесь мы вызываем ее для инициализации значений угла светового пятна и угла области освещенности. Чуть позже код функции OnBeamNormal() будет рассмотрен более подробно.
На четвертом этапе создается фрейм к которому будет присоединен прожектор:
LPDIRECT3DRMFRAME lightframe; d3drm->CreateFrame(scene, &lightframe); lightframe->SetPosition(scene, D3DVALUE(0), D3DVALUE(10), D3DVALUE(-10)); lightframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-1), D3DVALUE(1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); lightframe->AddLight(spotlight); lightframe->AddMoveCallback(MoveLight, NULL); lightframe->Release(); lightframe = 0;
Указатель lightframe инициализируется функцией CreateFrame() интерфейса Direct3DRM. Функция SetPosition() применяется, чтобы поместить фрейм в точку, расположенную на 10 единиц выше и на 10 единиц дальше начала координат. Затем вызывается функция SetOrientation(), чтобы направить фрейм на начало координат.
После этого созданный ранее прожектор присоединяется к новому фрейму с помощью функции AddLight(). Кроме того, с помощью функции AddMoveCallback() устанавливается функция обратного вызова MoveLight(). После выполнения всех описанных действий указатель lightframe освобождается.
На заключительном, пятом, этапе работы функции CreateScene() осуществляется создание порта просмотра:
d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(6), D3DVALUE(-6)); camera->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-1), D3DVALUE(1.1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport);
Сначала инициализируется фрейм camera. Затем этот фрейм перемещается от начала координат на шесть единиц вверх и на шесть единиц ближе к зрителю. Функция SetOrientation() применяется, чтобы направить лицевую грань фрейма на начало координат. И, в самом конце, вызывается функция CreateViewport() интерфейса Direct3DRM для инициализации указателя viewport.
В функции CreateScene() приложения Spotlight выполняется установка функции обратного вызова MoveLight(), осуществляющей изменение ориентации фрейма прожектора при каждом обновлении изображения на экране. Код функции MoveLight() выглядит следующим образом:
void SpotlightWin::MoveLight(LPDIRECT3DRMFRAME frame, void*, D3DVALUE) { static const D3DVALUE LIM = D3DVALUE(0.3); static D3DVALUE xi = D3DVALUE(0.03); static D3DVALUE yi = D3DVALUE(0.04); static D3DVALUE x, y; if (x < -LIM || x > LIM) xi = -xi; if (y < -LIM || y > LIM) yi = -yi; x += xi; y += yi; frame->SetOrientation(NULL, x, y-1, D3DVALUE(1), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0)); }
Так же, как в приложении Jade из главы 5, в приложении Spotlight для выполнения анимации используется простой алгоритм «подскакивающего мяча» (более сложные приемы анимации мы изучим в главе 7). Объявленные в функции MoveLight() статические переменные применяются для отслеживания и ограничения изменений ориентации фрейма. После вычисления нового значения ориентации фрейма, оно устанавливается с помощью функции SetOrientation().
Ранее в этой главе, когда мы изучали источник рассеянного света и создавали приложение AmbientLight, мы рассмотрели шесть функций, необходимых для реализации меню Render. В них мы использовали функции GetQuality() и SetQuality() интерфейса Direct3DRMMeshBuilder. В приложении Spotlight такая стратегия не работает, поскольку здесь мы используем интерфейс Direct3DRMMesh.
Интерфейс Direct3DRMMesh практически полностью посвящен работе с группами. Группой называется набор граней сетки, который может модифицироваться как единая сущность. Для контроля и назначения цвета группы граней интерфейс Direct3DRMMesh предоставляет функции GetGroupColor() и SetGroupColor(). Существует только одна проблема: эти функции требуют идентификатор, который указывает, какая группа граней сетки нас интересует.
Как мы увидим в главе 8, идентификаторы групп важны, когда мы используем сетку, содержащую несколько групп. Однако сейчас все грани каждой из трех сеток принадлежат одной и той же группе. К счастью для нас, если сетка содержит только одну группу, то в качестве идентификатора группы можно использовать ноль. Этот идентификатор группы по умолчанию и используется в следующих функциях приложения Spotlight:
void SpotlightWin::OnRenderWireframe() { mesh1->SetGroupQuality(0, D3DRMRENDER_WIREFRAME); mesh2->SetGroupQuality(0, D3DRMRENDER_WIREFRAME); mesh3->SetGroupQuality(0, D3DRMRENDER_WIREFRAME); } void SpotlightWin::OnRenderFlat() { mesh1->SetGroupQuality(0, D3DRMRENDER_FLAT); mesh2->SetGroupQuality(0, D3DRMRENDER_FLAT); mesh3->SetGroupQuality(0, D3DRMRENDER_FLAT); } void SpotlightWin::OnRenderGouraud() { mesh1->SetGroupQuality(0, D3DRMRENDER_GOURAUD); mesh2->SetGroupQuality(0, D3DRMRENDER_GOURAUD); mesh3->SetGroupQuality(0, D3DRMRENDER_GOURAUD); } void SpotlightWin::OnUpdateRenderWireframe(CCmdUI* pCmdUI) { D3DRMRENDERQUALITY quality = mesh1->GetGroupQuality(0); pCmdUI->SetCheck(quality == D3DRMRENDER_WIREFRAME); } void SpotlightWin::OnUpdateRenderFlat(CCmdUI* pCmdUI) { D3DRMRENDERQUALITY quality = mesh1->GetGroupQuality(0); pCmdUI->SetCheck(quality == D3DRMRENDER_FLAT); } void SpotlightWin::OnUpdateRenderGouraud(CCmdUI* pCmdUI) { D3DRMRENDERQUALITY quality = mesh1->GetGroupQuality(0); pCmdUI->SetCheck(quality == D3DRMRENDER_GOURAUD); }
Одна из первых трех функций вызывается, когда пользователь выбирает один из пунктов в меню Render приложения. Функция SetGroupQuality() используется чтобы изменить метод визуализации сетки. Следующие три функции вызываются MFC перед выводом на экран меню Render. Функция SetCheck() используется для установки флажка слева от пункта меню, соответствующего используемому в данный момент методу визуализации.
Приложение Spotlight предоставляет пользователю меню Beam, позволяющее изменить угол светового пятна и угол зоны освещенности прожектора. Шесть функций, реализующих эту функциональность, показаны ниже:
void SpotlightWin::OnBeamNormal() { spotlight->SetUmbra(D3DVALUE(0.2)); spotlight->SetPenumbra(D3DVALUE(0.4)); beamwidth = BEAM_NORMAL; } void SpotlightWin::OnBeamNarrow() { spotlight->SetUmbra(D3DVALUE(0.1)); spotlight->SetPenumbra(D3DVALUE(0.2)); beamwidth = BEAM_NARROW; } void SpotlightWin::OnBeamWide() { spotlight->SetUmbra(D3DVALUE(0.4)); spotlight->SetPenumbra(D3DVALUE(0.8)); beamwidth = BEAM_WIDE; } void SpotlightWin::OnUpdateBeamNormal(CCmdUI* pCmdUI) { pCmdUI->SetCheck(beamwidth == BEAM_NORMAL); } void SpotlightWin::OnUpdateBeamNarrow(CCmdUI* pCmdUI) { pCmdUI->SetCheck(beamwidth == BEAM_NARROW); } void SpotlightWin::OnUpdateBeamWide(CCmdUI* pCmdUI) { pCmdUI->SetCheck(beamwidth == BEAM_WIDE); }
Одна из первых трех функций вызывается, когда пользователь выбирает один из пунктов меню Beam. Функции SetUmbra() и SetPenumbra() применяются для модификации характеристик прожектора. Кроме того, переменной класса beamwidth присваивается значение, соответствующее установленным в данный момент параметрам прожектора. Эта переменная используется следующими тремя функциями, которые предназначены для отображения метки возле активного пункта меню.
netlib.narod.ru | < Назад | Оглавление | Далее > |