Программирование графики с использованием Direct3D

         

DS2POV


Утилита 3DS2POV преобразует сцены 3DS в сцены POV-ray. Утилита обладает небольшим числом возможностей, но предоставляет простой способ использовать программы визуального моделирования для разработки сцен POV-ray. Обычно утилиту лучше всего использовать для создания первого чернового варианта сцены. Полученный в результате исходный код POV-ray можно затем изменять. Утилиту 3DS2POV написали Стив Энджэ (Steve Anger) и Джефф Боуэрмастер (Jeff Bowermaster).



Алфавитный указатель


A

Абсолютные значения

задание 140

Р

Рассеянный свет 190–202

демонстрационная программа 191–202

класс AmbientLightWin 194–195 функция AmbientLightWin::CreateScene 196–200 функции меню Render 201–202

добавление к сцене 75 определение 42

A

AmbientLightWin, класс 194–195

определение класса 194–195 функции меню Render 200–202 функция CreateScene 195–200

создание и загрузка сетки 197 создание источника света 198–199 создание порта просмотра 199–200 создание фрейма 197–198



Анимация


Трехмерная графика в реальном масштабе времени не доставит удовольствия без анимации. Трехмерная анимация может быть выполнена двумя путями: заданием параметров движения и с помощью ключевых кадров.





Анимация текстуры


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

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


В этой главе вы узнали, как различными методами накладывать текстуры на сетки. Мы также обсудили декалы и прозрачность текстур. Теперь пришло время поговорить об анимации текстур.

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

Для анимации единственной текстуры есть масса возможностей. Чтобы модифицировать способ наложения текстуры на сетку можно изменять масштаб текстуры, начало координат, метод наложения и даже параметры прозрачности. Наиболее простой способ для перемещения текстуры по сетке— изменение начала координат текстуры.



Анимация вершин


Анимацией вершин называется изменение местоположения одной или нескольких вершин сетки. Реализовать этот вид анимации достаточно просто, гораздо труднее решить когда и куда перемещать вершины.

Анимация вершин может быть выполнена как с использованием интерфейса Direct3DRMMeshBuilder, так и с помощью интерфейса Direct3DRMMesh. Поскольку применение интерфейса Direct3DRMMeshBuilder приводит к снижению быстродействия и увеличению объема используемой памяти, в данной главе мы будем использовать исключительно интерфейс Direct3DRMMesh.



Благодарности


Спасибо Кейт Вейскамп (Keith Weiskamp) и Джеффу Дантеманну (Jeff Duntemann). Уникальность Кейт и Джеффа в том, что они издатели, являющиеся одновременно программистами и авторами. Их понимание издательского дела не остается незамеченным.

Особая благодарность Скотту Палмеру (Scott Palmer), моему редактору. Подобно Кейт и Джеффу, Скотт также является автором и понимает трудности и напряжение написания книг.

Спасибо также моему давнему другу и коллеге Патрику Луйану (Patrick Lujan). Патрик помог мне с написанием и инсталляцией программ для CD-ROM, но это ничто, по сравнению с той помощью, которую он оказывал мне все эти годы.



Book_left


Стен Трухильо

Программирование графики с использованием Direct3D

" onclick="parent.info_pane.location = parent.info_pane.jump_next();">



Что такое Direct3D?


Direct3D это компонент библиотеки DirectX, который поддерживает высокопроизводительную трехмерную визуализацию (rendering) и анимацию для Windows 95. Direct3D имеет два интерфейса: абстрактный режим (Retained Mode) и непосредственный режим (Immediate Mode). Абстрактный режим обеспечивает мощный интерфейс высокого уровня, который позволяет создавать и рисовать сложные трехмерные сцены. Непосредственный режим — это интерфейс низкого уровня для обработки элементарных треугольников, составляющих изображение. Функции абстрактного режима используют при работе непосредственный режим. В данной книге мы будем рассматривать только абстрактный режим.

Direct3D не был разработан фирмой Microsoft. Первоначально он был создан расположенной в Лондоне компанией RenderMorphics и назывался Reality Lab. В феврале 1995 года фирма Microsoft приобрела RenderMorphics и начала переработку Reality Lab для работы под Windows 95. В существующем виде Direct3D базируется на Direct Draw — части DirectX, работающей с двухмерной графикой. Direct3D применяет DirectDraw для работы с видеобуферами и использования механизма переключения страниц. Подобно DirectDraw, Direct3D написан таким образом, чтобы использовать любые установленные на видеоплате аппаратные ускорители (при условии, что у вас установлены правильные драйверы видеокарты). Когда приложение, использующее DirectDraw выполняется на компьютере с видеокартой, обеспечивающей аппаратное копирование прямоугольных областей изображения, сразу же увеличивается скорость работы. Когда приложение использующее Direct3D выполняется на компьютере с аппаратным ускорителем трехмерной графики, ускорение работы еще заметнее. Таким образом, для наилучшей работы требуется видеоплата с аппаратными ускорителями для двухмерной и трехмерной графики. Однако, наличие видеоплаты с ускорителем трехмерной графики не является необходимым для разработки приложений с использованием Direct3D 2.



CLW


Утилита ClassWizard VisualC++ хранит свои данные в файлах CLW. Обычно Visual C++ использует эти файлы незаметно для вас. Однако, возможна ситуация, когда файл CLW поврежден и не может быть прочитан Visual C++. Если это произошло, закройте рабочую область, удалите файл CLW, после чего снова откройте рабочую область. В результате будет запущена утилита ClassWizard. На экран будет выведено диалоговое окно, позволяющее указать файлы с исходным кодом, которые должны быть использованы для генерации новых данных ClassWizard.



COM


Microsoft COM (Component Object Model) — это программная модель разработанная с целью позволить создание полностью переносимых компонентов, допускающих безопасное обновление. С точки зрения COM программные компоненты должны устанавливаться также легко, как и аппаратные. COM — основа некоторых API, таких как OLE и DirectX.

COM использует объектно-ориентированную модель, отличающуюся от модели, применяемой в таких языках как C++. Модель COM более строгая. Например, версия наследования в COM ограничена по сравнению с C++. Также COM-объекты не могут содержать открытых членов данных. Все взаимодействие с объектом должно осуществляться через функции-члены.

COM-компонент состоит из объекта и одного или нескольких интерфейсов посредством которых осуществляется доступ к объекту. Объект обеспечивает функциональность, предоставляемую COM-компонентом, но к нему нельзя обратиться напрямую. К COM-объектам всегда обращаются через интерфейсы. Один объект может поддерживать несколько интерфейсов. Рис. 3.1 иллюстрирует отношения объект/интерфейс.


Рис. 3.1. COM-объекты и интерфейсы

Рисунок иллюстрирует взаимодействие приложения с COM-объектом. Приложение использует COM-объект только через поддерживаемые объектом интерфейсы.

COM-объект может поддерживать сразу несколько интерфейсов, чтобы его функциональность можно было совершенствовать или расширять без риска нарушить работу существующих программ.

COM также поддерживает свойство, названное инкапсуляция времени существования (lifetime encapsulation). Инкапсуляция времени существования означает, что COM-объекты управляют своим собственным уничтожением. Когда объект обнаружит, что он больше не нужен, он уничтожит себя сам. Чтобы эта функция работала корректно, программы должны уведомлять объекты, при создании нового указателя на объект и при уничтожении указателя.

Все COM-объекты являются производными от COM-объекта IUnknown, предоставляющего три функции: AddRef(), Release() и QueryInterface().
Функции AddRef() и Release() увеличивают и уменьшают счетчик ссылок на объект. Функция QueryInterface() применяется для поиска дополнительных интерфейсов объекта (или определения того, что интерфейс не поддерживается данным объектом). Один из аргументов функции QueryInterface() — Глобальный Уникальный Идентификатор (GUID), который идентифицирует искомый интерфейс.

Все API DirectX построены на основе COM. Это, однако, не говорит о том, что вы должны быть профессионалом в COM, чтобы применять DirectX. Фактически, использование COM-объектов подобно использованию объектов C++. Ниже приведены некоторые правила, которые вам надо соблюдать при работе с COM.

COM-объекты и объекты C++ не могут быть производными друг от друга. Простейший способ получения класса C++ из COM-объекта, состоит во «внедрении» COM-объекта в классы C++ и написании функций-оболочек (wrapper functions) для каждой функции COM.

Каждый раз при создании копии указателя на COM-объект должна быть вызвана функция объекта AddRef() чтобы известить объект о создании дополнительной ссылки. Это не относится к созданию объекта. Обычно API DirectX выполняет эту работу за вас и включает вызов функции AddRef() в функцию, создающую COM-объект.

Каждый раз, когда указатель на COM-объект больше не нужен, должна быть вызвана функция объекта Release().

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

Если вы хотите узнать больше о COM, загрузите спецификацию с сайта Microsoft (www.microsoft.com).


Conv3ds


Direct3D предоставляет для создания X-файлов утилиту CONV3DS, преобразующую файлы 3D Studio (.3DS) в X-файлы. У утилиты CONV3DS есть ряд параметров командной строки. Наиболее часто применяемые параметры перечислены в таблице 3.1.

Таблица 3.1. Параметры утилиты CONV3DS

Параметр Описание
m Создает единую сеть вместо иерархии фреймов
t Создает текстовый X-файл
A Включает в файл данные анимации (если они есть)
f Не импортировать данные о перемещении фрейма



Cover


  Далее >

1996 The Coriolis Group.



Цвет испускаемого света


Испускаемый свет (emissive light) — это свет, исходящий от самого объекта. Испускаемый свет применяется при представлении таких источников освещения, как лампы и неоновые трубки. По умолчанию объекты не испускают света (цвет испускаемого света — черный). Для контроля испускаемого света используются функции GetEmissive() и SetEmissive().

СОВЕТ Испускаемый свет в цветовых моделях Ramp и RGB. Цветовая модель Ramp не поддерживает цветного освещения, однако испускаемый свет не зависит от источников освещения, и объекты могут испускать цветной свет как при использовании цветовой модели Ramp так и в цветовой модели RGB.



Цвет источника света


Все источники света имеют один общий атрибут: цвет. Обычно источники света белые, соответственно все их цветовые составляющие имеют максимальную интенсивность. Цветовая система может изменяться от одного графического пакета к другому, но наиболее часто для определения цвета освещения используется схема RGB (Red, Green, Blue— Красный, Зеленый, Синий). В Direct3D значение каждого компонента RGB может изменяться от 0 (выключено) до 1 (максимум), таким образом белый цвет в схеме RGB задается набором значений 1, 1, 1. Красный источник освещения задается набором значений 1, 0, 0. Синий источник освещения задается набором значений 0, 1, 0. Цвета, отличающиеся от красного, зеленого и синего могут быть представлены с использованием смеси этих трех цветов. Например, желтый цвет задается набором значений 1, 1, 0.



Цвет отраженного света


Вы можете настраивать цвет отраженного света с помощью функций GetSpecular() и SetSpecular(). По умолчанию цвет отраженного света — белый.



Цвета текстуры


После создания экземпляра интерфейса Direct3DRMTexture можно вызывать функции GetColors() и SetColors() для управления количеством цветов, используемых Direct3D для представления текстуры. Посредством функций GetShades() и SetShades() можно осуществлять более точный контроль процесса визуализации текстуры.

Количество цветов текстуры (контролируемое посредством функций GetColors() и SetColors()) определяет число цветов, присутствующих на самой текстуре. Функции GetShades() и SetShades() позволяют контролировать, сколько оттенков будет использовано для представления каждого из цветов текстуры. Например, для двухцветной текстуры функция GetColors() возвратит значение 2, а функция GetShades() возвратит значение 16 (поскольку 16 — это используемое по умолчанию количество оттенков). В этом примере каждый из двух цветов текстуры будет представляться шестнадцатью различными оттенками.

Сокращение числа цветов или оттенков текстуры ухудшает качество получаемого изображения, но позволяет отдельным текстурам быть полезными для других. Например, если текстура использует много цветов, но применяется так, что ее качество на столь существенно, то количество цветов и оттенков этой текстуры может быть сокращено, благодаря чему резервируется большее количество цветов и оттенков для текстур, чей вид важнее.



Цветовые модели


И программные и аппаратные устройства поддерживают две цветовых модели: RGB и Ramp. Цветовая модель RGB поддерживает цветное освещение, а цветовая модель Ramp — нет. Благодаря этому устройства Ramp превосходят по быстродействию устройства RGB. Устройства Ramp также называют монохромными устройствами из-за их одноцветной обработки источников освещения. Это, однако, может ввести в заблуждение, так как в режиме Ramp монохроматическими являются только источники освещения. Сетки и грани в режиме Ramp используют все цветовые возможности.



D3DCOLOR


Тип D3DCOLOR применяется в Direct3D для представления цветов. Цвет имеет красную, зеленую, синюю и альфа составляющие. Значение каждой составляющей может варьироваться от нуля (отсутствие компонента) до единицы (максимальная интенсивность компонента).

Фактически дело обстоит несколько сложнее. Тип D3DCOLOR представляется типом DWORD, и поэтому, он не может хранить четыре значения с плавающей точкой. При сохранении значения цветовых составляющих умножаются на 255 и помещаются в соответствующие разряды значения типа DWORD. Для этой цели Direct3D предоставляет макроопределение. Присвоить значение переменной типа D3DCOLOR можно с помощью макроопределения D3DRGB или D3DRGBA:

D3DCOLOR color=D3DRGB(1,1,1); // создаем переменную D3DCOLOR // для белого цвета D3DCOLOR color=D3DRGBA(1,1,1,0); // создаем переменную D3DCOLOR // для белого цвета // с нулевой альфа-составляющей

Значения, передаваемые макроопределениям D3DRGB и D3DRGBA должны быть в диапазоне от нуля до единицы. Макроопределение выполняет умножение и преобразование значений. К тому же не требуется выполнять приведение к типу D3DVALUE, поскольку макроопределение осуществляет приведение типа автоматически.

Чтобы извлечь отдельные цветовые составляющие из значения типа D3DCOLOR применяются функции D3DRMColorGetRed(), D3DRMColorGetGreen(), D3DRMColorGetBlue() и D3DRMColorGetAlpha().



D3DRMBOX


Direct3D использует структуру D3DRMBOX для описания размеров объекта. Интерфейсы Direct3DRMMesh и Direct3DRMMeshBuilder предоставляют функцию GetBox(), применяемую для получения размеров объекта. Структура D3DRMBOX содержит две структуры D3DVECTOR:

typedef struct _D3DRMBOX { D3DVECTOR min, max; } D3DRMBOX;

Хотя используются структуры типа D3DVECTOR, данные в них описывают точки, а не векторы. Структуры min и max применяются для указания противоположных углов минимального возможного параллелепипеда, который мог бы содержать рассматриваемый объект.



D3DRMQUATERNION


Кватернионы описывают вращение. В главе2 для описания вращения мы применяли вектор и значение вращения. Вектор определял ось вращения, а значение — угол поворота. Кватернионы инкапсулируют вектор и значение в одной структуре:

typedef struct _D3DRMQUATERNION { D3DVALUE s; D3DVECTOR v; } D3DRMQUATERNION; typedef D3DRMQUATERNION *LPD3DRMQUATERNION;

Для инициализации кватернионов применяется функция D3DRMQuaternionFromRotation(). Функция получает вектор и значение вращения и создает кватернион.

Кватернионы удобны для вычисления промежуточных значений вращения. Например, функция D3DRMQuaternionSlerp() принимает в качестве аргументов два кватерниона и число. Функция вычисляет промежуточный кватернион между двумя векторами. Передаваемое в качестве параметра число определяет, в каком месте между двумя указанными кватернионами будет расположен новый. Например, если указать значение 0.5, новый кватернион будет создан точно между двумя исходными.



D3DRMVERTEX


Структура D3DRMVERTEX применяется в Direct3D для описания вершин сеток и определена следующим образом:

typedef struct _D3DRMVERTEX{ D3DVECTOR position; D3DVECTOR normal; D3DVALUE tu, tv; D3DCOLOR color; } D3DRMVERTEX;

Структура position описывает местоположение вершины. Вектор normal задает нормаль вершины (для метода визуализации Гуро) или вектор грани (при равномерной закраске). Значения tu и tv определяют координаты точки текстуры, которая отображается на данную вершину. Значение color задает цвет вершины.

Структура D3DRMVERTEX используется функциями GetVertices() и SetVertices() интерфейса Direct3DRMMesh. Функция GetVertices() заполняет массив структур D3DRMVERTEX значениями соответствующих вершин. Эти значения можно затем изменить и передать функции SetVertices(), что приведет к изменению характеристик сетки. Этот способ могут использовать такие методы анимации, как анимация вершин и анимация текстур. Также с помощью функций GetVertices() и SetVertices() программа может изменить задаваемые по умолчанию нормали граней или вершин.



D3DVALUE


D3DVALUE— это основной тип данных в Direct3D. Тип D3DVALUE объявлен через тип float и применяется в Direct3D для представления координат вершин, интенсивности освещения, скорости вращения и т.д.

В 32-разрадных версиях C++, таких как Visual C++, числа для которых явно не указан тип, воспринимаются как числа типа int (если десятичная точка отсутствует) или double (если в числе есть десятичная точка). Другими словами, если в вашей программе встречается неспецифицированное число с плавающей точкой, компилятор выделит для него восемь байт (как для числа двойной точности), а не четыре (как для числа с плавающей точкой одинарной точности). По этой причине, а также из-за того, что код, использующий тип D3DVALUE будет более переносимым, в программах Direct3D часто используются значения типа D3DVALUE. Если вы уверены, что программа не будет переноситься на другие платформы, и вам неприятно наличие многочисленных приведений к типу D3DVALUE в коде (что довольно понятно), применяйте для указания компилятору типа значения суффикс «f» (в этом случае вы должны использовать десятичную точку). Приведем ряд примеров:

d3dfunction((D3DVALUE)3) // стандартный и безопасный способ d3dfunction((D3DVALUE)3.0) // стандартный и безопасный способ d3dfunction(3) // воспринимается как int и вызывает // предупреждение компилятора d3dfunction(3.0) // воспринимается как double и // вызывает предупреждение компилятора d3dfunction(3.0f) // допустимо, но уменьшает // переносимость d3dfunction(3f) // недопустимо, должна использоваться // десятичная точка



D3DVECTOR


Структура D3DVECTOR определена следующим образом:

typedef struct _D3DVECTOR { union { D3DVALUE x; D3DVALUE dvX; }; union { D3DVALUE y; D3DVALUE dvY; }; union { D3DVALUE z; D3DVALUE dvZ; }; } D3DVECTOR, *LPD3DVECTOR;

Объединения позволяют использовать переменные x и dvX (для примера) как взаимозаменяемые. Структура D3DVECTOR повсеместно применяется в Direct3D для представления не только векторов, но и точек.



Декалы


Декал (decal) — это текстура, добавляемая к сцене без присоединения к грани или сетке. Декалы визуализируются в сцене в соответствии с их местоположением, благодаря чему они увеличиваются или уменьшаются в зависимости от расстояния до зрителя. Декалы также подчиняются правилам скрытия невидимых поверхностей. Если декал находится позади объекта, он закрывается этим объектом.

Декалы имеют один существенный недостаток: они всегда расположены лицевой поверхностью к зрителю и не могут просматриваться с разных углов. Это значительно ограничивает область их применения. Если их расположить вдалеке от камеры, они могли бы использоваться, например, для представления картин в сцене музея или мониторов в рубке космического корабля.

Несмотря на указанный недостаток, декалы полезны при визуализации двумерных объектов сцены. Эффекты, реализация которых посредством сеток и граней оказывается слишком ресурсоемкой (например, взрывы) часто могут быть эффективно реализованы с применением декалов.

Интерфейс Direct3DRMTexture предоставляет ряд функций для работы с декалами:

GetDecalOrigin()

GetDecalScale()

GetDecalSize()

GetDecalTransparency()

GetDecalTransparentColor()

SetDecalOrigin()

SetDecalScale()

SetDecalSize()

SetDecalTransparency()

SetDecalTransparentColor()


Во всех рассмотренных до данного момента демонстрационных программах текстуры назначались интерфейсу Direct3DRMMeshBuilder, а затем конструктор сеток присоединялся к фрейму функцией AddVisual() интерфейса Direct3DRMFrame.

В этом разделе мы введем и обсудим понятие декала. Декал— это текстура, которая добавляется непосредственно к сцене. Декалы в сцене выглядят точно также, как и в графическом редакторе; они не могут быть обернуты вокруг объектов сцены. Можно перемещать и масштабировать декалы, но их нельзя вращать; декалы всегда повернуты к камере своей лицевой поверхностью. Этот факт ограничивает полезность декалов, но их все равно удобно применять для добавления к сцене двумерных элементов. Например, часто в приложениях с помощью декалов изображают взрывы, поскольку использование для изображения взрывов граней и сеток требует слишком большого количества ресурсов.

СОВЕТ О декалах. Некоторые из функций интерфейса Direct3DRMTexture используют слово «decal», и декалы реализуются с помощью интерфейса Direct3DRMTexture. Интерфейса Direct3DRMDecal не существует.



Демонстрация рассеянного света


Откровенно говоря, рассеянный свет — довольно скучная вещь. Он полезен, особенно когда применяется совместно с источниками света других типов, но сам по себе рассеянный свет весьма невыразителен. После нескольких безуспешных попыток создать демонстрационную программу, в которой использовался бы только рассеянный свет, я отступил и решил, что рассеянный свет не заслуживает собственного примера. Вместо этого давайте применим мастер 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 все равно будут искаться среди включаемых файлов проекта. Предупреждения об их отсутствии можно игнорировать.
Независимо от того, какой объект вы выбрали, он, скорее всего, выглядит не очень хорошо. Это связано с тем, что источник рассеянного света освещает каждую часть сетки с одинаковой интенсивностью.В результате мы видим силуэт объекта. Если наложить на сетку текстуру, результат будет чуть лучше, но изображение все равно останется скучным и плоским.

Мы используем только что созданный проект для демонстрации следующих технологий:

Использование источника рассеянного света

Использование интерфейса Direct3DRMMeshBuilder

Изменение метода визуализации сетки во время работы программы.


Демонстрационное приложение Jade


Демонстрационное приложение Jade отображает сетку, имеющую форму букв D3D и накладывает на эту сетку нефритовую текстуру. Сетка анимирована, чтобы зритель мог рассмотреть ее с разных сторон. Окно приложения Jade показано на рис. 5.3.


Рис. 5.3. Приложение Jade

В приложении Jade для наложения текстуры на сетку используется плоское покрытие. Это означает, что текстура накладывается на сетку прямолинейно, без изгибов или деформирования. Поэтому, когда направление взгляда на сетку совпадает с направлением наложения текстуры, последняя выглядит точно так же, как в графическом редакторе. Текстура, используемая в приложении Jade показана на рис. 5.4.


Рис. 5.4. Текстура, используемая в приложении Jade

Приложение Jade демонстрирует следующие технологии:

Загрузка сетки и текстуры из ресурсов программы. Большинство демонстрационных приложений на CD-ROM во время выполнения загружают сетки и/или текстуры. Чтобы исключить риск того, что требуемый файл будет недоступен, каждый из примеров хранит необходимые файлы как ресурсы. Если вы хотите модифицировать примеры так, чтобы вместо ресурсов использовались файлы на диске, обратитесь к приложению Sample из главы 4 или исследуйте код, создаваемый мастером Direct3D AppWizard.

Использование интерфейса Direct3DRMWrap для наложения текстуры на сетку.

Использование команд меню для изменения параметров визуализации во время выполнения программы.

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



Дерево зависимостей


Простейший способ анализа зависимостей — построение дерева зависимостей. Дерево зависимостей изображает связи каждого интерфейса с другими, представляя каждый интерфейс как узел дерева. Корень дерева представляет интерфейс, который не зависит от существования остальных. Последующие узлы представляют интерфейсы прямо или косвенно зависящие от корневого. Дерево зависимости для интерфейсов Direct3D изображено на рис. 4.2.


Рис. 4.2. Дерево зависимостей для интерфейсов Direct3D

Узлы на рисунке содержат название интерфейса (выделенное полужирным шрифтом) и имя переменной, используемой для идентификации данного интерфейса.

Из рисунка видно, что единственный интерфейс не зависящий от других — это интерфейс Direct3DRM. Линии, идущие от Direct3DRM к узлам DirectDrawClipper и Direct3DRMFrame показывают, что эти интерфейсы зависят от существования интерфейса Direct3DRM.

Система узлов распространяется от корня до узлов интерфейсов Direct3DRMViewport, Direct3DRMLight и Direct3DRMMeshBuilder. Эти интерфейсы косвенно зависят от всех остальных интерфейсов дерева. Обратите внимание, что интерфейс Direct3DRMViewport непосредственно зависит от двух интерфейсов.



Direct3D


Direct3D— это набор интерфейсов модели компонентных объектов Microsoft (Microsoft Component Object Model, COM) представляющих графические конструкции, такие как сетки и грани, и прикладные конструкции, такие как области просмотра и устройства. В этой главе мы скажем несколько слов о каждом из интерфейсов.

Хотя мы и обсудим интерфейсы и их функции, данная глава не является справочным руководством. Мы не будем сосредотачиваться на списках параметров или возвращаемых значениях, а только обсудим обеспечиваемую интерфейсом функциональность.

Перед тем как погрузиться в дискуссию об интерфейсах Direct3D, вам необходимо познакомиться с Microsoft COM. В следующем разделе мы кратко обсудим модель COM. По общепринятому соглашению имена COM-интерфейсов начинаются с префикса «I» (I — сокращение от «Interface»). В нашем обсуждении мы опустим этот префикс.



Direct3DRM: основной интерфейс


Как было упомянуто ранее, Direct3D— это набор COM-интерфейсов. Все интерфейсы, составляющие Direct3D, зависят от одного главного: Direct3DRM (RM обозначает Retained Mode — абстрактный режим). Объект Direct3DRM представляет непосредственно Direct3D и создается функцией Direct3DRMCreate():

LPDIRECT3DRM d3drm; Direct3DRMCreate(&d3drm);

Тип LPDIRECT3DRM — это указатель на интерфейс Direct3DRM. Функция Direct3DRMCreate() создает объект и инициализирует указатель на интерфейс. После того, как функция Direct3DRMCreate() успешно завершится (мы поговорим о кодах возврата в конце этой главы), интерфейс Direct3DRM готов к использованию.



Direct3DRMAnimation: интерфейс анимации


В Direct3D термин анимация (animation) имеет два значения. Анимацией называется перемещение объектов в сцене, кроме того, анимация— это еще и название интерфейса, который поддерживает создание и воспроизведение анимации по ключевым кадрам.



Direct3DRMAnimationSet: интерфейс анимационного набора


Анимационный набор (animation set)— это коллекция объектов анимации, используемая для представления анимированной сцены в целом. Обычно анимационные наборы создаются путем импортирования анимированных сцен из анимационных пакетов, например, 3D Studio. Каждый объект сцены представляется объектом анимации, а все объекты анимации образуют анимационный набор.

Интерфейс Direct3DRMAnimationSet применяется для управления анимационными наборами и создается функцией CreateAnimationSet() интерфейса Direct3DRM.



Direct3DRMDevice: интерфейс устройства


Устройства Direct3D— это объекты, которые создают визуализированное изображение. Direct3D поддерживает несколько типов устройств. Программы могут выбрать устройство из списка доступных устройств или поручить Direct3D выбрать устройство автоматически. Есть два основных типа устройств — программные и аппаратные. Программные устройства позволяют программам работать на компьютерах, которые не оборудованы аппаратными ускорителями трехмерной графики. Аппаратные устройства доступны только на компьютерах, оборудованных соответствующими платами, и позволяют Direct3D полностью использовать возможности, предоставляемые аппаратными средствами.

Устройство представляется интерфейсом Direct3DRMDevice и может быть создано, посредстаом функций интерфейса Direct3DRM. Рассмотрим три способа создания устройства:

Создать объект отсечения (clipper object) DirectDraw, а затем создать устройство функцией CreateDeviceFromClipper(). Это самый простой и наиболее надежный метод создания устройства.

Создать первичную поверхность DirectDraw с вторичным буфером (back-buffer) (позволяющим переключать страницы) и создать устройство функцией CreateDeviceFromSurface(). Этот метод используется для инициализации приложений Direct3D, которые будут работать в полноэкранном режиме.

Инициализировать непосредственный режим Direct3D (Immediate Mode) и создать устройство функцией CreateDeviceFromD3D().

После создания устройства оно используется для определения параметров механизма визуализации и выполнения визуализации сцен.



Direct3DRMFace: интерфейс грани


Сетки представляют собой набор граней, следовательно, создание сеток означает создание граней. В большинстве случаев вы будете модифицировать существующие грани, а не создавать новые. Например, интерфейс конструктора сеток позволяет получить доступ к существующим граням с помощью функции GetFaces(). Также возможно создать грань с нуля, а затем добавить ее к конструктору сеток. В любом случае можно настроить атрибуты грани: цвет, текстуру, вершины ит.д. Грани представлены интерфейсом Direct3DRMFace.

Интерфейс Direct3DRMFace предоставляет функции GetColor() и SetColor(), позволяющие получить и установить цвет грани.



Direct3DRMFrame: интерфейс фрейма


Фреймы (frame) являются важной частью абстрактного режима Direct3D. Использование этого термина может привести к путанице, поскольку общепринятое его значение— «кадр анимации». В Direct3D значение этого термина происходит от словосочетания «система координат» (frame of reference). Фреймы применяются для размещения объектов в трехмерном пространстве. Объекты, такие как сетки, грани, камеры и источники света не имеют определенного местоположения или ориентации. Но если эти объекты присоединяются к фрейму, они получают свое местоположение и ориентацию от этого фрейма. В результате, при перемещении фрейма будут перемещаться все присоединенные к нему объекты.



Direct3DRMLight: интерфейс источника света


Direct3D поддерживает пять различных типов источников света: рассеянный свет, точечный источник света, направленный свет, параллельный свет и зональное освещение, или прожектор. Единственный общий для всех источников света параметр— цвет. Для каждого поддерживаемого Direct3D типа источника света может быть указано местоположение и ориентация, но в некоторых случаях эти параметры игнорируются.

Источники света представлены интерфейсом Direct3DRMLight и создаются функциями CreateLight() и CreateLightRGB() интерфейса Direct3DRM. Обе функции требуют, чтобы вы указали цвет и тип источника света, хотя в дальнейшем эти параметры могут быть в любой момент изменены.

Местоположение и ориентация источников света определяется посредством фреймов, поэтому перед использованием в сцене источник света должен быть присоединен к фрейму. Для этой цели интерфейс Direct3DRMFrame предоставляет функцию AddLight().



Direct3DRMMaterial: интерфейс материала


Материал определяет поведение света, попадающего на грани и сетки. Объект может быть сделан блестящим, матовым и, кроме того, задание свойств материала позволяет имитировать светящиеся объекты.

Экземпляр интерфейса Direct3DRMMaterial создается функцией CreateMaterial() интерфейса Direct3DRM. Интерфейс материала позволяет настраивать три параметра: мощность отраженного света, цвет отраженного света и цвет испускаемого света. Полученный в результате материал может быть назначен грани, сетке или конструктору сеток.



Direct3DRMMesh: интерфейс сетки


Интерфейс Direct3DRMMesh разработан для более быстрой работы с сетками. К сожалению, интерфейс сетки не так прост в использовании, как интерфейс конструктора сеток. Сетки могут быть созданы при помощи функции CreateMesh() интерфейса Direct3DRM и посредством функции CreateMesh() интерфейса Direct3DRMMeshBuilder.



Direct3DRMMeshBuilder: интерфейс конструктора сеток


Интерфейс Direct3DRMMeshBuilder— это высокоуровневый, удобный инструмент для манипуляций с сетками, предоставляющий 38 функций для создания и модификации сеток. Конструктор сеток, как следует из его названия, создает сетки, но сам сеткой не является. Однако, конструктор сеток может использоваться как удобная замена сетки и добавляться в сцену в качестве визуального элемента. Когда конструкторы сеток добавляются в сцену, они создают и используют внутреннюю сетку. Чтобы создать конструктор сеток, воспользуйтесь функцией CreateMeshBuilder() интерфейса Direct3DRM, как показано ниже.

LPDIRECT3DRMMESHBUILDER meshbuilder; d3drm->CreateMeshBuilder(&meshbuilder);



Direct3DRMShadow: интерфейс тени


Библиотека Direct3D разрабатывалась для создания быстродействующих приложений. В связи с этим иногда приходится приносить в жертву производительности точность и реализм. Хорошим примером является поддержка теней в Direct3D, поскольку методы визуализации, применяемые в Direct3D не вычисляют тени. Поддержка теней сводится к добавлению к сцене объектов, иммитирующих эффект отбрасывания тени. Эта имитация является очень упрощенной и имеет насколько ограничений.

Для создания тени требуется задать следующие элементы:

объект, отбрасывающий тень;

источник света, используемый при вычислении параметров тени;

плоскость, на которой отображается тень.

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

Другое ограничение связано с необходимостью указывать плоскость на которую отбрасывается тень. Это значит, что вы не сможете получить тень на сложном объекте. Убедительные тени в Direct3D требуют плоских поверхностей.

Объекты теней являются экземплярами интерфейса Direct3DRMShadow и создаются посредством функции CreateShadow() интерфейса Direct3DRM. Интерфейс теней предоставляет только одну функцию: Init(). Функция Init() используется редко, поскольку функция CreateShadow() выполняет те же действия и проще в использовании.



Direct3DRMTexture: интерфейс текстуры


В Direct3D текстуры могут быть назначены для грани и сетки, либо добавлены непосредственно к сцене, как фоновое изображение или декал (decal). Текстуры могут быть загружены из файлов BMP, файлов PPM, из ресурсов программы или из памяти. Представляются текстуры интерфейсом Direct3DRMTexture.



Direct3DRMTextureWrap: интерфейс наложения текстур


Наложение текстуры — это объект, определяющий способ присоединения текстуры к сетке. Наложение текстуры контролирует стиль наложения (плоский, цилиндрический, сферический), ориентацию и параметры текстуры, такие как масштаб и начало координат.

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

Наложение текстуры представляется интерфейсом Direct3DRMTextureWrap и создается функцией CreateWrap() интерфейса Direct3DRM.



Direct3DRMViewport: интерфейс порта просмотра


Порт просмотра (viewport)— это термин, используемый в Direct3D для камеры. Порт просмотра определяет местоположение зрителя и направление просмотра сцены. Порт просмотра может применяться для настройки параметров поля зрения, переднего и заднего отсечения и перспективных преобразований. Порт просмотра можно анимировать, чтобы имитировать перемещение пользователя в виртуальном окружении.

Порт просмотра представляется интерфейсом Direct3DRMViewport и создается функцией CreateViewport() интерфейса Direct3DRM:

d3drm->CreateViewport(device, camera, 0, 0, device->GetWidth(), device->GetHeight(), &viewport);

Переменная d3drm является указателем на интерфейс Direct3DRM. Переменная device указывает на интерфейс Direct3DRMDevice. Переменная camera — это фрейм, определяющий местоположение и ориентацию порта просмотра. Мы подробнее обсудим фреймы далее в этой главе.



Direct3DWinDevice: интерфейс устройства Windows


Объект, обеспечивающий поддержку устройства Direct3D, является примером COM-объекта, поддерживающего несколько интерфейсов. Мы рассмотрели один интерфейс, через который можно использовать устройство — Direct3DRMDevice. Следующий интерфейс, с которым мы познакомимся — Direct3DWinDevice. Интерфейс Direct3DWinDevice представляет устройства, которые поддерживаются Windows. На рис. 3.2 показан объект устройства Direct3D и его интерфейсы.


Рис. 3.2. Объект устройства и его интерфейсы

Поскольку интерфейс Direct3DWinDevice является альтернативным интерфейсом устройства Direct3D, для получения интерфейса WinDevice приложения используют существующее устройство. Для этого применяется функция QueryInterface(), как показано в следующем примере кода:

LPDIRECT3DRMWINDEVICE windev; device->QueryInterface(IID_IDirect3DRMWinDevice, (void**)&windev);

Переменная device — это заранее инициализированный указатель на интерфейс Direct3DRMDevice. Для получения указателя на интерфейс Direct3DRMWinDevice применяется функция QueryInterface() интерфейса устройства. IID_IDirect3DRMWinDevice — это глобальный уникальный идентификатор (GUID) запрашиваемого интерфейса.

Интерфейс WinDevice поддерживает две функции: HandleActivate() и HandlePaint(). Обе эти функции уведомляют Direct3D о том, что были получены некоторые сообщения Windows. Программа, использующая Direct3D, должна вызывать функцию HandleActivate() при обработке сообщения WM_ACTIVATE. Аналогично, при обработке сообщения WM_PAINT программа должна вызвать функцию HandlePaint().



DirectDraw


DirectDraw— это общецелевой API управления видеокартами, позволяющий определять и использовать любые поддерживаемые видеокартами возможности двумерной графики. Кроме того, DirectDraw может программно эмулировать возможности, отсутствующие в конкретной модели видеокарты.

Приведенное ниже описание DirectDraw не претендует на полноту, но это и не требуется. Мы обсудим только те функциональные возможности DirectDraw, которые потребуются нам для создания полноэкранного приложения.



Добавление обработчика событий


Теперь, когда у нас есть работающее приложение, мы можем его модифицировать с помощью мастера ClassWizard. Если приложение уже загружено, выберите пункт ClassWizard из меню View. На рис. 1.8 изображено диалоговое окно мастера ClassWizard.


Рис. 1.8. Диалоговое окно мастера ClassWizard

В диалоговом окне мастера ClassWizard есть пять вкладок. Выберите вкладку Message Map, как показано на рисунке. В диалоговом окне будут показаны установленные обработчики событий для класса, имя которого вы выбираете в поле со списком Class name. Список Object IDs содержит имя класса и список идентификаторов для меню. Вы добавляете обработчик событий, выбрав один из идентификаторов меню или имя класса и указав требуемое сообщение в списке Messages. Новый обработчик сообщений будет создан после щелчка по кнопке Add Function.

Давайте создадим обработчик событий, который будет сообщать о нажатии на клавиши. Это можно сделать установив обработчик для сообщения WM_KEYDOWN. Система Windows посылает сообщение WM_KEYDOWN при нажатии на любую клавишу.

Убедитесь, что в поле со списком Class name выбран класс CSampleView и этот же класс отмечен в списке Object IDs. В списке Message list отметьте строку WM_KEYDOWN (вам понадобится прокрутить список, чтобы найти строку WM_KEYDOWN). После того, как произведете выбор, щелкните по кнопке Add Function. Мастер AppWizard добавит строку WM_KEYDOWN в список Members functions (в нем отображаются существующие в программе обработчики) и добавит новый обработчик событий в текст программы. Теперь щелкните по кнопке Edit code. Мастер ClassWizard откроет окно с текстом программы. Созданный нами новый обработчик показан на рис. 1.9.


Рис. 1.9. Созданный нами обработчик события WM_KEYDOWN

ЗАМЕЧАНИЕ Обратите внимание на код обработчика событий. Обратите внимание на код, который мастер ClassWizard помещает в новый обработчик событий. ClassWizard отмечает наилучшее место для добавления вашего кода, помещая туда комментарий. Иногда ваш код располагается перед вызовом функции базового класса, иногда — после. В некоторых случаях ClassWizard вообще не будет вызывать функции базового класса.
<
/p> Единственной вещью, которую делает созданный мастером ClassWizard обработчик, является вызов подобной функции базового класса. Мы должны добавить наш код в место, отмеченное ClassWizard с помощью комментариев. Для примера мы будем использовать отладочное макроопределение TRACE, которое будет показывать информацию о поступившем сообщении. Модифицируйте обработчик событий, как показано ниже:

void CSampleView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { TRACE("WM_KEYDOWN message received "); TRACE("keycode=%d repeat=%d flags=%d\n", nChar, nRepCnt, nFlags);

CView::OnKeyDown(nChar, nRepCnt, nFlags); }

Теперь скомпилируйте и запустите приложение. После появления окна приложения нажмите какую-нибудь клавишу. В окне Debug программы Developer Studio будет показано сообщение, напечатанное функцией TRACE.

ЗАМЕЧАНИЕ Этот пример будет работать, только если компилируется в отладочном режиме (Debug mode) и запускается из программы Developer Studio потому что макроопределение TRACE в режиме Release mode не работает.

Добавление поддержки видеорежимов


Полноэкранная версия класса RMWin предоставляет несколько функций, которые облегчают обнаружение и переключение видеорежимов. Вот эти функции:

GetNumDisplayModes()

ActivateDisplayMode()

GetCurDisplayMode()

GetDisplayModeDims()

GetCurDisplayModeDims()

Внутри класса RMWin собирается список поддерживаемых видеорежимов. Количество элементов в этом списке может быть определено с помощью функции GetNumDisplayModes(). Заданный видеорежим может быть активирован функцией ActivateDisplayMode(). Функция GetCurDisplayMode() возвращает включенный в данный момент видеорежим. Функции GetDisplayModeDims() и GetCurDisplayModeDims() возвращают параметры видеорежима (ширина, высота и глубина цвета экрана).



Дополнительные утилиты


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



Доступ к граням


Интерфейс конструктора сеток позволяет также модифицировать и добавлять грани. Например, список созданных конструктором сеток граней может быть получен с помощью функции GetFaces(). Для получения количества граней в сетке применяется функция GetFaceCount(). Чтобы создать и добавить грани вызываются функции CreateFace(), AddFace() и AddFaces().



Доступ к вершинам


Конструктор сеток обеспечивает доступ к вершинам сетки. Число вершин сетки может быть получено с помощью функции GetVertexCount(). Данные вершин могут быть получены или установлены посредством функций GetVertices() и SetVertices(). Возможность доступа к вершинам сетки открывает большие возможности изменения конфигурации сетки с помощью указанных функций.


Для работы с позициями вершин интерфейс Direct3DRMMesh предоставляет функции GetVertices() и SetVertices(). Функция GetVertices() предназначена для получения текущей позиции вершины. После получения параметров вершины можно их изменить и переустановить посредством функции SetVertices(). Это рекомендуемая техника модификации формы сетки в критических по времени ситуациях, таких как анимация вершин и морфинг.

Подобно Direct3DRMMeshBuilder, интерфейс Direct3DRMMesh предоставляет функции Scale() и Translate().



Еще об анимации


Как я уже упоминал в начале главы, анимация— очень обширная тема. Мы закончим эту главу изучением еще одного демонстрационного приложения. Приложение Target, в отличие от других приложений, рассматриваемых в этой книге, разрабатывалось не для того, чтобы показать какую-то отдельную технику. В нем совместно используются три метода анимации.