netlib.narod.ru | < Назад | Оглавление | Далее > |
Речь пойдет лишь о самых распространенных интерфейсах, включенных мной в библиотеку 3dPlus. Все интерфейсы механизма визуализации документированы в справочных файлах, входящих в комплект DirectX 2 SDK, так что я не собираюсь подробно рассматривать работу всех функций каждого интерфейса. На рис. 3.1 изображена иерархия интерфейсов, входящих в библиотеку 3dPlus. Диаграмма была создана на основании определений интерфейсов из файла d3drmobj.h, входящего в DirectX 2 SDK.
Рис. 3.1. Иерархия интерфейсов в механизме визуализации
Как видите, в иерархии существуют две основные группы: интерфейсы, производные от IDirect3DRMObject, и интерфейсы, производные от IDirect3DRMVisual. Интерфейс IDirect3DRMObject является общим предком для всех интерфейсов библиотеки и включает функцию SetAppData, которая позволяет включить в любой интерфейс закрытую 32-разрядную переменную. Например, такая возможность оказывается очень полезной при инкапсуляции интерфейса в классе C++. Закрытая переменная будет содержать указатель на объект класса C++, и при наличии указателя на интерфейс можно быстро добраться до объекта-оболочки C++.
Важно учесть, что интерфейсы, производные от IDirect3DRMVisual, могут использоваться в качестве аргумента любой функции, для которой требуется указатель на интерфейс IDirect3DRMVisual (см. пример IDirect3DRMFrame::AddVisual, приведенный ниже). Что касается аргументов функций, следует упомянуть о том, что в реальных прототипах функций, определенных в DirectX 2 SDK, типы интерфейсов не указываются прямо. Например, функция, аргументом которой является указатель на интерфейс IDirect3DRMVisual, может быть определена следующим образом:
HRESULT IDirect3DRMFrame::AddVisual(LPDIRECT3DRMVISUAL pVisual);
Как видите, указатель на интерфейс IDirect3DRMVisual имеет тип LPDIRECT3DRMVISUAL.
ПРИМЕЧАНИЕ |
Использование специальных типов данных в качестве указателей общепринято в Microsoft Windows. Мне кажется, что в новом 32-разрядном мире эта практика устарела, поскольку нам уже не нужно различать near и far-указатели. Как можно видеть в приведенном выше примере, использование специальных типов данных также затрудняет ответ на вопрос, что же собой представляет аргумент функции на самом деле. Имена типов, состоящие из прописных букв, также являются общепринятыми — регистр помогает отличить тип указателя от типа объекта, на который ссылается данный указатель. Как бы то ни было, я все равно не считаю такую практику полезной. И все же определения в SDK записаны именно так, к тому же они соответствуют стандартам Windows, поэтому мы должны учесть это обстоятельство и следовать ему в своих программах. |
В библиотеке 3dPlus я определил тип указателя так, как показано ниже, для тех немногочисленных случаев, когда функция получает в качестве аргумента указатель на интерфейс:
void AttachVisual(IDirect3DRMVisual* pIVisual);
Почти все интерфейсные функции возвращают значение типа HRESULT, которое проверяется в моих программах следующим образом:
ASSERT(SUCCEEDED(m_hr));
А иногда проверка выглядит так:
return SUCCEEDED(m_hr);
Обратите внимание — COM-интерфейсы могут возвращать значение S_FALSE, которое свидетельствует об ошибке, но успешно проходит проверку макроса SUCCEEDED. Ни один из интерфейсов Direct3D не возвращает S_FALSE, поэтому использование макросов SUCCEEDED и FAILED всегда будет давать правильный результат.
Давайте пройдемся по интерфейсам, производным от IDirect3DRMObject и изображенным на рис. 3.1, и кратко рассмотрим назначение каждого из них.
Интерфейс содержит управляющие функции, которые влияют на отображение макета в вашем окне. Функции работают со вспомогательным слоем Direct3D, и, в сущности, со многими аспектами физического устройства вывода. Вероятнее всего, вы будете пользоваться этим интерфейсом для изменения качества визуализации с помощью функции SetQuality. Кроме того, функция SetShades интерфейса IDirect3DRMDevice служит для ограничения количества цветовых оттенков при работе с палитрами. В качестве примера давайте посмотрим, как устанавливается качество визуализации. Ниже приведена реализация функции SetQuality в классе C3dDevice (он находится в файле 3dStage.cpp):
void C3dDevice::SetQuality(D3DRMRENDERQUALITY quality) { if (!m_pIDevice) return; m_hr = m_pIDevice->SetQuality(quality); ASSERT(SUCCEEDED(m_hr)); }
А вот как функция C3dDevice::SetQuality используется при первом создании объекта C3dStage и инициализации переменной m_Quality значением D3DRMRENDER_GOURARD:
BOOL C3dStage::Create(CDirect3D* pD3D) { . . . // Установить текущее качество визуализации m_Device.SetQuality(m_Quality); . . . }
Качество визуализации может соответствовать нескольким уровням — от простейшего «проволочного каркаса» до закраски методом Гуро, как показано в табл. 3.1. Я выбрал в качестве стандартной закраску Гуро (одна из технологий для получения плавной закраски), поскольку, на мой взгляд, она дает самый реалистичный результат.
Таблица 3.1. Возможные значения параметров функции SetQuality | |||
Качество визуализации | Закраска | Освещение | Заполнение |
D3DRMRENDER_WIREFRAME | Однородная | Нет | Нет («проволочный каркас») |
D3DRMRENDER_UNLIT | Однородная | Нет | Сплошное |
D3DRMRENDER_FLAT | Однородная | Да | Сплошное |
D3DRMRENDER_GOURARD | Метод Гуро | Да | Сплошное |
D3DRMRENDER_PHONG | Метод Фонга* | Да | Сплошное |
* Не поддерживается в Direct3D версии 2 (DirectX 2).
ПРИМЕЧАНИЕ |
Многие функции библиотеки 3dPlus возвращают значение типа BOOL, которое показывает, успешно ли завершилась функция. Тем не менее я решил, что некоторые функции могут закончиться неудачей лишь при полной катастрофе, и такие функции не возвращают никакого значения. Вместо этого в функцию включается директива ASSERT, которая отлавливает любые возможные проблемы. |
Интерфейс IDirect3DRMViewport управляет работой проекционной системы, изображенной на рис. 3.2 и преобразующей пространственные координаты в двумерные координаты на экране вашего компьютера. Функция SetBack используется для задания положения задней отсекающей плоскости на оси Z. Функция SetField изменяет фокальное расстояние камеры, воспроизводящей макет.
Рис. 3.2. Проекционная система
Функция SetProjection определяет, следует ли применять к изображению корректировку перспективы, или же объекты должны воспроизводиться в простой ортогональной проекции. В большинстве случаев следует пользоваться перспективной проекцией для повышения реализма. Коррекция перспективы рассматривается в главе 8, где мы будем изучать наложение текстур.
Помимо определения исходных условий, основное назначение этого интерфейса связано с выбором объектов в макете. Функция Pick определяет, какой объект (если он имеется) лежит под заданной точкой экрана. Мы подробнее рассмотрим эту функцию в главе 7.
Интерфейс IDirect3DRMFace позволяет определить или задать атрибуты одной грани трехмерного объекта. Например, вы можете задать цвет грани функцией SetColor, или же получить вектор, направленный по нормали к ней, функцией GetNormal. Для получения указателя на интерфейс IDirect3DRMFace обычно следует запросить у интерфейса IDirect3DRMMeshBuilder список граней, после чего выбрать из возвращаемого массива одну конкретную грань. Присвоение цвета грани в функции C3dShape::SetFaceColor происходит следующим образом:
BOOL C3dShape::SetFaceColor(int nFace, double r, double g, double b) { if (nFace >= GetFaceCount()) return FALSE; // Получить список граней IDirect3DRMFaceArray* pIFaces = NULL; ASSERT(m_pIMeshBld); m_hr = m_pIMeshBld->GetFaces(&pIFaces); ASSERT(SUCCEEDED(m_hr)); // Выбрать из списка нужную грань IDirect3DRMFace* pIFace = NULL; m_hr = pIFaces->GetElement(nFace, &pIFace); ASSERT(SUCCEEDED(m_hr)); // Задать цвет грани m_hr = pIFace->SetColorRGB(r, g, b); ASSERT(SUCCEEDED(m_hr)); // Освободить интерфейсы грани и списка граней pIFace->Release(); pIFaces->Release(); return TRUE; }
Интерфейс IDirect3DRMLight предназначен для управления различными источниками света, поддерживаемыми механизмом визуализации (источники света более подробно рассматриваются в главе 10). Источник света может обладать различными характеристиками, от цвета до закона изменения интенсивности с расстоянием. Приведу простой пример установки цвета источника в функции C3dLight::SetColor:
BOOL C3dLight::SetColor(double r, double g, double b) { ASSERT(m_pILight); m_hr = m_pILight->SetColorRGB(D3DVAL(r), D3DVAL(g), D3DVAL(b)); return SUCCEEDED(m_hr); }
Макрос D3DVAL преобразует величины к формату с плавающей точкой, который используется в механизме визуализации.
Покрытие (wrap) определяет способ наложения текстуры на объект. Покрытия могут быть плоскими, цилиндрическими, сферическими и хромовыми. Для наложения покрытий (за исключением хромовых) на сетку используется функция Apply. Хромовое покрытие, предназначенное для имитации отражающих поверхностей, накладывается функцией ApplyRelative; при этом текстура ориентируется по отношению к фрейму, а не к объекту, благодаря чему достигается правильное поведение «отражений» даже при вращении объекта.
Покрытие также можно наложить на одну грань объекта. Ниже приводится функция (из файла 3dImage.cpp), которая накладывает объект-покрытие C3dWrap на заданную грань объекта C3dShape:
BOOL C3dWrap::Apply(C3dShape* pShape, int nFace) { ASSERT(pShape); ASSERT(m_pIWrap); if (nFace >= pShape->GetFaceCount()) return FALSE; // Получить список граней IDirect3DRMMeshBuilder* pIBld = pShape->GetMeshBuilder(); ASSERT(pIBld); IDirect3DRMFaceArray* pIFaces = NULL; m_hr = pIBld->GetFaces(&pIFaces); ASSERT(SUCCEEDED(m_hr)); // Выбрать из списка нужную грань IDirect3DRMFace* pIFace = NULL; m_hr = pIFaces->GetElement(nFace, &pIFace); ASSERT(SUCCEEDED(m_hr)); // Наложить покрытие на грань m_hr = m_pIWrap->Apply(pIFace); ASSERT(SUCCEEDED(m_hr)); // Освободить интерфейсы pIFace->Release(); pIFaces->Release(); return SUCCEEDED(m_hr); }
Материал определяет отражающие свойства поверхности. Используя их, вы можете регулировать блеск поверхности и придавать ей вид, характерный для металла или пластика.
В общем случае материал имеет два цвета: нормальный и цвет, присущий ему при сильном освещении. Посмотрите на зеленое яблоко при ярко-белом свете. Поверхность яблока выглядит зеленой за исключением тех мест, где на нее падает прямой свет — в этих участках она белая. Цвета, которые вы видите, обусловлены диффузными и зеркальными отражающими свойствами объекта, они имитируются с помощью материала. В главе 8 материалы рассматриваются более подробно.
Интерфейс IDirect3DRMVisual не содержит собственных функций. Он лишь является базой, от которой порождаются все интерфейсы, которые могут использоваться в качестве визуальных элементов. Хотя в документации по SDK интерфейс IDirect3DRMVisual упоминается довольно часто, обычно он используется лишь как тип аргументов различных функций, как показано в показанном в начале этого раздела объявлении AddVisual.
IDirect3DRMFrame используется чаще других интерфейсов и служит для изменения свойств фрейма. Например, можно задать положение фрейма функцией SetPosition или определить его ориентацию функцией SetOrientation. Приведу другой пример — функция SetTexture закрепляет за фреймом текстуру, которая используется сетками, прикрепленными к фрейму в качестве визуальных элементов. Таким образом, одна сетка, определяющая форму объекта, может использоваться с различными текстурами. Ниже приводится функция C3dFrame::SetPosition, которая пользуется интерфейсом для установки положения фрейма (при наличии объявления IDirect3DRMFrame* m_pIFrame):
void C3dFrame::SetPosition(double x, double y, double z, C3dFrame* pRef) { ASSERT(m_pIFrame); m_hr = m_pIFrame->SetPosition(_GetRef(pRef), D3DVAL(x), D3DVAL(y), D3DVAL(z)); ASSERT(SUCCEEDED(m_hr)); }
Функция SetRotation задает вращение фрейма вокруг заданного вектора, а функция SetVelocity — скорость вращения. Такая возможность оказывается полезной, если в вашем макете происходит непрерывное движение и вы не хотите постоянно пересчитывать положение объектов.
Если фрейм является корневым (то есть не имеет родительского фрейма), можно задать для него фоновое изображение функцией SceneSetBackground или просто выбрать цвет фона функцией SceneSetBackGroundRGB.
Интерфейс сеток IDirect3DRMMesh в основном используется для задания атрибутов групп внутри сетки. Группой называется набор вершин с общими атрибутами (например, цветом). Группировка сходных элементов повышает производительность визуализации и часто используется абстрактным режимом Direct3D.
Интерес представляют еще две функции этого интерфейса — функция Save, сохраняющая сетку в файле на диске, и функция Translate, прибавляющая заданное смещение к каждой вершине сетки. Последняя функция особенно полезна для присоединения нескольких сеток к общему фрейму при построении сложной фигуры.
Интерфейс IDirect3DRMShadow не содержит собственных функций и служит в качестве типа данных для объектов-теней, которые являются разновидностью визуальных элементов. Работа с тенями рассмотрена в главе 10.
Комплексный интерфейс, используемый для создания трехмерных объектов. Большая часть функций класса C3dShape реализована именно с помощью интерфейса IDirect3DRMMeshBuilder. Интерфейс содержит много функций, от очень простых (например, Load, загружающей сетку из файла на диске) до более сложных, типа функции AddFaces, которая по списку вершин, нормалей (векторов, обозначающих направление) и описаниям граней создает новый набор граней сетки. О применении сеток для создания трехмерных объектов рассказано в главе 4.
Ниже приводится функция C3dShape::Create, которая использует интерфейс построения сеток для создания нового объекта по описаниям вершин и граней (при условии, что переменная m_pIMeshBld объявлена как указатель на IDirect3DRMMeshBuilder):
BOOL C3dShape::Create(D3DVECTOR* pVectors, int iVectors, D3DVECTOR* pNormals, int iNormals, int* pFaceData, BOOL bAutoGen) { ASSERT(m_pIMeshBld); // Построить сетку по списку векторов ASSERT(sizeof(ULONG) == sizeof(int)); m_hr = m_pIMeshBld->AddFaces(iVectors, pVectors, iNormals, pNormals, (ULONG*)pFaceData, NULL); ASSERT(SUCCEEDED(m_hr)); if ((iNormals == 0) && bAutoGen) { m_pIMeshBld->GenerateNormals(); } AttachVisual(m_pIMeshBld); // Разрешить коррекцию перспективы m_pIMeshBld->SetPerspective(TRUE); return TRUE; }
Интерфейс построения сеток также содержит много справочных функций, предназначенных для получения информации о сетке. Например, можно узнать, сколько граней входит в сетку:
int C3dShape::GetFaceCount() { ASSERT(m_pIMeshBld); int i = (int) m_pIMeshBld->GetFaceCount(); return i; }
Текстурой (texture) называется изображение, которое накладывается на фигуры или на их отдельные грани для придания им большего реализма. Функции интерфейса IDirect3DRMTexture чаще всего используются для управления процессом визуализации текстур. Например, если вы желаете ограничить количество цветов при воспроизведении текстуры, следует вызвать функцию SetShades. В противном случае одна насыщенная цветами текстура может заполнить всю палитру и не оставить в ней места для других фигур и текстур.
Функция SetDecalTransparencyColor задает прозрачные области текстуры. Декалом (decal) называется текстура, которая воспроизводится непосредственно как визуальный элемент и обычно представляет собой что-то вроде плоского спрайта, всегда обращенного лицевой стороной к камере. Тем не менее прозрачные текстуры вовсе не обязаны использоваться в качестве декалов. Текстуры подробнее рассмотрены в главе 8, а спрайты — в главе 9.
netlib.narod.ru | < Назад | Оглавление | Далее > |