netlib.narod.ru | < Назад | Оглавление | Далее > |
В реальном мире рассеянный свет является отраженным светом. Рассеянный свет — это свет, который был отражен и рассеян в окружающем пространстве. Например, вечером рассеянный свет позволяет нам видеть окружающие предметы уже после того, как солнце скрылось. Лучи солнца рассеиваются в атмосфере и обеспечивают ровное, слабое освещение не имеющее направления и видимого источника.
Свет, который действительно не имеет направления и источника, в реальном мире не существует. Отраженные и рассеянные в атмосфере солнечные лучи приблизительно похожи на рассеянный свет — в действительности солнечный свет распространяется в конкретном направлении и у него есть неявный источник (солнце). В Direct3D рассеянный свет не имеет ни направления, ни источника.
С технической точки зрения рассеянный свет подобен остальным источникам света. Он представляется интерфейсом Direct3DRMLight и, чтобы стать видимым, должен быть присоединен к фрейму. Однако источник рассеянного света игнорирует местоположение и ориентацию того фрейма, к которому он подсоединен.
Откровенно говоря, рассеянный свет — довольно скучная вещь. Он полезен, особенно когда применяется совместно с источниками света других типов, но сам по себе рассеянный свет весьма невыразителен. После нескольких безуспешных попыток создать демонстрационную программу, в которой использовался бы только рассеянный свет, я отступил и решил, что рассеянный свет не заслуживает собственного примера. Вместо этого давайте применим мастер Direct3D AppWizard чтобы создать приложение, в котором рассеянный свет будет использоваться для освещения выбранного вами объекта.
Запустите программу Visual C++ Developer Studio, и выберите команду New в меню File. На экран будет выведено диалоговое окно New. Выберите в списке пункт Project Workspace. Будет открыто диалоговое окно New Project Workspace. В списке Type выберите Direct3D AppWizard, введите имя проекта, например AmbientLight, в поле Name, и щелкните по кнопке Create.
На экран будет выведено первое окно мастера Direct3D AppWizard. Щелкните по кнопке Next. В следующем диалоговом окне вам будет предложено выбрать объект, который будет отображать новое приложение. По умолчанию используется объект Swirl (завиток). Выберите переключатель Let me choose an object и введите имя объекта в текстовом поле Object, либо щелкните по кнопке Browse и воспользуйтесь диалоговым окном выбора файла. На рис. 6.1 показано диалоговое окно Object Selection в котором выбран файл sphere1.x из DirectX SDK.
Рис. 6.1. Диалоговое окно выбора объекта мастера Direct3D AppWizard
СОВЕТ |
Содержимое X-файлов. Помните, что вы должны выбрать X-файл, содержащий единственную сетку. X-файлы, содержащие анимации или иерархию фреймов, будут отображаться неправильно. Большинство X-файлов на сопроводительном CD-ROM содержат единственную сетку. |
После того, как вы выбрали сетку, которая будет отображаться приложением, щелкните по кнопке Next.
На экран будет выведено диалоговое окно, позволяющее выбрать используемые в приложении источники света. Внешний вид этого окна показан на рис. 6.2.
Рис. 6.2. Окно выбора источников света мастера Direct3D AppWizard
По умолчанию используется источник направленного света. Снимите флажок Directional и установите флажок Ambient. Кроме того, в группе Color Model установите переключатель RGB. Это позволит использовать цветное освещение. После всех изменений диалоговое окно Light Selection должно выглядеть так, как показано на рис. 6.2.
В следующих диалоговых окнах оставьте предлагаемые по умолчанию параметры, и в завершающем окне щелкните по кнопке Finish. Мастер AppWizard выведет окно с просьбой подтвердить правильность заданных параметров. Щелкните по кнопке OK. Developer Studio создаст новое приложение в соответствии с указанными вами параметрами. Когда Developer Studio завершит работу (это не займет много времени), скомпилируйте и запустите новое приложение. Окно нового приложения должно выглядеть аналогично рис. 6.3 (особенно, если вы выбрали в качестве объекта файл sphere1.x).
Рис. 6.3. Приложение, созданное с помощью мастера Direct3D AppWizard
СОВЕТ |
Предупреждения о включаемых файлах. Когда вы в первый раз компилируете новый проект, или когда в меню Build выбрана команда Update All Dependencies Visual C++ часто выводит несколько предупреждений. Это происходит потому что компилятор пытается обнаружить все указанные в директивах #include включаемые файлы и выводит предупреждение всякий раз, когда не может обнаружить требуемый файл. К сожалению, поиск включаемых файлов выполняется до обработки программы препроцессором. Это означает, что включаемые файлы, которые не используются в проекте из-за директив условной компиляции #ifdef все равно будут искаться среди включаемых файлов проекта. Предупреждения об их отсутствии можно игнорировать. |
Независимо от того, какой объект вы выбрали, он, скорее всего, выглядит не очень хорошо. Это связано с тем, что источник рассеянного света освещает каждую часть сетки с одинаковой интенсивностью. В результате мы видим силуэт объекта. Если наложить на сетку текстуру, результат будет чуть лучше, но изображение все равно останется скучным и плоским.
Мы используем только что созданный проект для демонстрации следующих технологий:
Львиная доля функциональности нашего проекта сосредоточена в классе AmbientLightWin. Определение этого класса выглядит следующим образом:
class AmbientLightWin : public RMWin { public: AmbientLightWin(); BOOL CreateScene(); protected: //{{AFX_MSG(AmbientLightWin) 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 DECLARE_MESSAGE_MAP() private: LPDIRECT3DRMMESHBUILDER meshbuilder; };
Класс AmbientLightWin наследуется от класса RMWin. В нем объявлены две открытые функции: конструктор и функция CreateScene(). Конструктор инициализирует единственную переменную класса:
AmbientLightWin::AmbientLightWin() { meshbuilder=0; }
Переменная meshbuilder является указателем на интерфейс Direct3DRMMeshBuilder и будет использоваться в приложении в качестве указателя на сетку. Указатель meshbuilder инициализируется в функции CreateScene(), которую мы рассмотрим чуть позже.
Кроме того, в классе объявлены шесть защищенных функций, которые используются в качестве обработчиков событий. Первые три функции, OnRenderWireframe(), OnRenderFlat() и OnRenderGouraud(), вызываются, когда пользователь выбирает одну из команд в меню Render. Оставшиеся три функции вызываются MFC каждый раз перед отображением меню. Эти функции применяются для отображения в меню флажка рядом с используемым в данный момент методом визуализации. Эти функции также будут рассмотрены чуть позже.
Сцена создается в функции CreateScene(). Если вы назвали проект AmbientLight, функция CreateScene() будет выглядеть подобно показанной в листинге 6.1.
Листинг 6.1. Функция AmbientLightWin::CreateScene() |
BOOL AmbientLightWin::CreateScene() { HRESULT r; // ------ СЕТКА -------- d3drm->CreateMeshBuilder(&meshbuilder); r = meshbuilder->Load(meshname, NULL, D3DRMLOAD_FROMFILE, NULL, NULL); if (r != D3DRM_OK) { CString msg; msg.Format("Failed to load file '%s'\n", meshname); AfxMessageBox(msg); return FALSE; } ScaleMesh(meshbuilder, D3DVALUE(25)); //------- ФРЕЙМ СЕТКИ -------- LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(meshbuilder); meshframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(.1)); meshframe->Release(); meshframe = 0; // -------- РАССЕЯННЫЙ СВЕТ -------- LPDIRECT3DRMLIGHT alight; d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00), &alight); scene->AddLight(alight); alight->Release(); alight = 0; //------ КАМЕРА ---------- d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport); return TRUE; } |
Функция CreateScene() выполняет следующие действия:
На первом этапе для загрузки сетки из файла используется интерфейс Direct3DRMMeshBuilder. Рассмотрим этот фрагмент более пристально:
d3drm->CreateMeshBuilder(&meshbuilder); r = meshbuilder->Load(meshname, NULL, D3DRMLOAD_FROMFILE, NULL, NULL); if (r != D3DRM_OK) { CString msg; msg.Format("Failed to load file '%s'\n", meshname); AfxMessageBox(msg); return FALSE; } ScaleMesh(meshbuilder, D3DVALUE(25));
Сначала с помощью функции CreateMeshBuilder() интерфейса Direct3DRM инициализируется переменная meshbuilder. Затем для загрузки файла сетки с диска вызывается функция Load() (переменная meshname хранит имя файла сетки, которое было указано при создании приложения с помощью мастера AppWizard). Если выполнение функции Load() завершается неудачей, выводится окно сообщения и возвращается значение FALSE. Если функция Load() завершилась успешно, вызывается функция ScaleMesh() для задания оптимального размера сетки.
На втором этапе работы функции CreateScene() выполняется создание фрейма к которому будет присоединена сетка. Эта часть кода выглядит так:
LPDIRECT3DRMFRAME meshframe; d3drm->CreateFrame(scene, &meshframe); meshframe->AddVisual(meshbuilder); meshframe->SetRotation(scene, D3DVALUE(0), D3DVALUE(1), D3DVALUE(0), D3DVALUE(.1)); meshframe->Release(); meshframe = 0;
Указатель meshframe объявлен как указатель на интерфейс Direct3DRMFrame. Для создания нового фрейма применяется функция CreateFrame() интерфейса Direct3DRM. Конструктор сеток meshbuilder, созданный на предыдущем этапе, присоединяется к новому фрейму с помощью функции AddVisual().
Далее производится вызов функции SetRotation(), чтобы назначить фрейму атрибуты вращения. Фрейм будет вращаться вокруг оси Y (на это указывают первые три аргумента функции). Последний аргумент функции SetRotation() указывает, что фрейм будет поворачиваться при каждом обновлении изображения на 0.1 радиан.
На третьем этапе работы функции CreateScene() создается источник рассеянного света:
LPDIRECT3DRMLIGHT alight; d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00), &alight); scene->AddLight(alight); alight->Release(); alight = 0;
Переменная alight объявляется как указатель на интерфейс Direct3DRMLight. Функция CreateLightRGB() используется для создания источника света и инициализации указателя alight. Первый аргумент функции CreateLightRGB() — это константа, которая определяет тип создаваемого источника света. Используемая здесь константа, D3DRMLIGHT_AMBIENT, является одной из пяти возможных:
Следующие три аргумента функции CreateLightRGB() определяют цвет источника света. Использованные значения (1, 1, 1) указывают, что будет создан источник белого света, поскольку для красной, зеленой и синей составляющих заданы одинаковые, максимально возможные значения. Ниже показаны некоторые альтернативные варианты.
d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(1.00), D3DVALUE(0.00), D3DVALUE(0.00), &alight); // создает источник красного света d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(0.00), D3DVALUE(1.00), D3DVALUE(0.00), &alight); // создает источник зеленого света d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(0.00), D3DVALUE(0.00), D3DVALUE(1.00), &alight); // создает источник синего света d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(1.00), D3DVALUE(0.00), D3DVALUE(1.00), &alight); // создает источник пурпурного света d3drm->CreateLightRGB(D3DRMLIGHT_AMBIENT, D3DVALUE(0.30), D3DVALUE(0.30), D3DVALUE(0.30), &alight); // создает источник темно-серого света
Если в сцене присутствует только один источник рассеянного света (что справедливо для нашей сцены), все грани в сцене будут визуализированы с использованием цвета этого источника света. Последним аргументом функции CreateLightRGB() является адрес указателя alight.
Затем новый источник света присоединяется к фрейму сцены:
scene->AddLight(alight);
Как вы увидите далее в этой главе, когда мы будем рассматривать другие типы источников света, остальные источники света не будут присоединены непосредственно к корневому фрейму сцены. Источник рассеянного света может быть присоединен к коревому фрейму потому, что на него не оказывает влияние местоположение и ориентация фрейма. Фактически, источник рассеянного света может быть присоединен к любому фрейму сцены и результат от этого не изменится.
После этого мы освобождаем указатель alight и обнуляем его:
alight->Release(); alight = 0;
Помните, что вызов функции Release() освобождает указатель, а не сам объект. Объект будет уничтожен только когда не останется ни одного указывающего на него указателя. В данном случае источник света не будет уничтожен, поскольку он был добавлен к сцене.
Четвертым и последним этапом работы функции CreateScene() является создание порта просмотра:
d3drm->CreateFrame(scene, &camera); camera->SetPosition(scene, D3DVALUE(0), D3DVALUE(0), D3DVALUE(-50)); d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport);
В этом фрагменте кода выполняется инициализация двух указателей: camera и viewport. Как говорилось в главе 4, эти указатели являются членами класса RMWin. Наша функция CreateScene() должна инициализировать эти указатели. Если мы не сделаем этого, выполнение приложения будет остановлено, и будет выведено сообщение об ошибке.
Указатель camera — это указатель на интерфейс Direct3DRMFrame, который инициализируется функцией CreateFrame() интерфейса Direct3DRM. Затем новый фрейм перемешается на 50 единиц от начала координат по направлению к зрителю. Это делается из-за того, что сетка, созданная на первом этапе, была помещена в начало координат (по умолчанию). Мы смещаем камеру от начала координат, чтобы объект попал в область просмотра. Затем для инициализации указателя viewport вызывается функция CreateViewport() интерфейса Direct3DRM.
Завершая работу, функция CreateScene() возвращает TRUE, чтобы сообщить об успешном выполнении всех действий. Если функция CreateScene() возвращает FALSE, работа приложения прекращается и выводится сообщение об ошибке.
Одна из возможностей, которую мастер Direct3D AppWizard добавляет в проект (независимо от того, просите вы об этом или нет) — это меню Render, позволяющее изменить метод визуализации сетки во время работы программы. Взглянув на определение класса AmbientLightWin, вы увидите объявление шести функций, реализующих эту функциональность. Функции OnRenderWireframe(), OnRenderFlat() и OnRenderGouraud() являются обработчиками событий, которые вызываются при выборе одной из команд меню Render. Эти функции выглядят следующим образом:
void AmbientLightWin::OnRenderWireframe() { if (meshbuilder) meshbuilder->SetQuality(D3DRMRENDER_WIREFRAME); } void AmbientLightWin::OnRenderFlat() { if (meshbuilder) meshbuilder->SetQuality(D3DRMRENDER_FLAT); } void AmbientLightWin::OnRenderGouraud() { if (meshbuilder) meshbuilder->SetQuality(D3DRMRENDER_GOURAUD); }
Каждая функция проверяет значение указателя meshbuilder. Если указатель был инициализирован, вызывается функция SetQuality() интерфейса Direct3DRMMeshBuilder для изменения метода визуализации сетки.
Оставшиеся три функции, OnUpdateRenderFlat(), OnUpdateRenderGouraud() и OnUpdateRenderWireframe(), вызываются Windows каждый раз при отображении на экране меню Render. Они применяются для вывода отметки рядом с активным в данный момент пунктом меню. Ниже приведен код этих функций:
void AmbientLightWin::OnUpdateRenderWireframe(CCmdUI* pCmdUI) { if (meshbuilder) { D3DRMRENDERQUALITY meshquality = meshbuilder->GetQuality(); pCmdUI->SetCheck(meshquality == D3DRMRENDER_WIREFRAME); } } void AmbientLightWin::OnUpdateRenderFlat(CCmdUI* pCmdUI) { if (meshbuilder) { D3DRMRENDERQUALITY meshquality = meshbuilder->GetQuality(); pCmdUI->SetCheck(meshquality == D3DRMRENDER_FLAT); } } void AmbientLightWin::OnUpdateRenderGouraud(CCmdUI* pCmdUI) { if (meshbuilder) { D3DRMRENDERQUALITY meshquality = meshbuilder->GetQuality(); pCmdUI->SetCheck(meshquality == D3DRMRENDER_GOURAUD); } }
Каждая из трех функций вызывает функцию GetQuality() интерфейса Direct3DRMMeshBuilder, чтобы определить используемый в данный момент метод визуализации сетки. Затем выполняется сравнение полученного метода визуализации, чтобы определить должна ли быть проставлена отметка рядом с данным пунктом меню. Результат сравнения используется в качестве аргумента функции SetCheck(), если он равен TRUE, рядом с пунктом меню ставится флажок.
Рассмотренные шесть функций в той или иной форме присутствуют в большинстве демонстрационных приложений, помещенных на CD-ROM.
netlib.narod.ru | < Назад | Оглавление | Далее > |