Для 8-разрядных видеорежимов необходима палитра. Для оконных приложений Direct3D автоматически создает и устанавливает палитру. В полноэкранных приложениях предоставить палитру должна наша программа.
СОВЕТ | Захват палитры. Простейший способ получения палитры для вашего приложения — создание оконной версии приложения и последующее использование программы захвата изображений (такой, как Paint Shop Pro) для захвата содержимого окна. Полученный в результате файл будет содержать цвета, необходимые для отображения полноэкранной версии. В этой главе мы узнаем, как извлечь палитру из файлов BMP. |
Палитры представлены интерфейсом DirectDrawPalette и создаются с помощью функции CreatePalette() интерфейса DirectDraw.
Источник параллельно-точечного света подобен источнику направленного света, но испускает лучи в двух направлениях. На параллельно-точечный источник света оказывают влияние и его местоположение, и его ориентация. На быстродействие приложения такой источник света влияет так же как источник направленного света.
Параллельный источник света является вариацией направленного. Параллельный источник света также испускает параллельные световые лучи, но они распространяются не в одном, а в двух противоположных направлениях.
На источник параллельного света оказывает влияние и его местоположение и ориентация. Ориентация устанавливает два направления, в которых распространяется свет, а местоположение задает испускающую свет плоскость.
Функции GetOptions() и SetOptions() применяются, чтобы определить поведение анимированных объектов. С их помощью можно задать следующие параметры:
линейная или сплайновая анимация;
открытая или закрытая анимация;
использование позиций;
использование масштабирования и вращения.
Использование линейной анимации означает, что интерфейс анимации перемещает объекты между ключевыми фреймами по кратчайшему расстоянию. Сплайновая анимация использует для вычисления позиции объекта кривые, обеспечивая более плавное движение.
Параметр открытия/закрытия определяет, как объект анимации будет интерпретировать передаваемые функции SetTime() значения времени, которые выходят за заданный диапазон. В закрытой анимации время движется по замкнутому кругу. Это означает, что из выходящего за диапазон значения времени вычитается максимальное значение времени ключа, и закрытая анимация повторяется, даже если значение времени продолжает увеличиваться. В открытой анимации значения времени, выходящие за диапазон, игнорируются. Этот параметр добавлен исключительно для удобства, поскольку открытая анимация может быть повторена посредством перезапуска счетчика времени анимации с нуля.
Параметр использования позиций активизирует позиционные ключи анимации. Когда данный параметр отключен, позиционные ключи не оказывают эффекта. Деактивация позиционных ключей полезна, если необходимо, чтобы анимация управляла масштабированием и вращением объекта, но не его позицией.
Параметр использования масштабирования и вращения управляет активацией ключей масштабирования и вращения в анимации. Когда параметр отключен, ключи масштабирования и вращения не оказывают эффекта. Отключение параметра позволяет вам контролировать масштабирование и вращение объекта, в то время как его позиция определяется объектом анимации.
Один из простейших способов анимации— задание атрибутов движения (motion attributes). Атрибуты движения — это операции перемещения, вращения и масштабирования, которые применяются к объекту или набору объектов при каждом обновлении изображения. Атрибуты движения используются для простых, повторяющихся движений. Как только объекту будут назначены атрибуты движения, он будет перемещаться в соответствии с ними без всякого дополнительного вмешательства.
Устройства могут применяться для изменения параметров визуализации. Например, функции GetQuality() и SetQuality() позволяют задать режим визуализации (Гуро, равномерная закраска и т.д.) используемый устройством при визуализации сцены. Отдельные объекты могут переопределять параметры визуализации только если задаваемая схема визуализации более простая, чем схема, заданная для устройства. По умолчанию для визуализации устанавливается режим равномерной закраски.
Функции GetShades() и SetShades() позволяют контролировать количество оттенков заданного цвета, используемых устройством при визуализации сцены. Параметр часто изменяется в зависимости от глубины цвета используемого видеорежима. По умолчанию используется 32 оттенка, но этого недостаточно для 24- и 32-разрядных видеорежимов. Можно поэкспериментировать с различными значениями параметра (значения должны быть степенями двойки), чтобы определить наилучшее для вашего приложения значение.
Функции GetDither() и SetDither() позволяют включить или выключить передачу полутонов (dithering). Передача полутонов — это техника, позволяющая эмулировать большее количество цветов, чем фактически доступно, и полезная для видеорежимов с малым количеством цветов (обычно 256 или меньше, но и для видеорежимов, которые поддерживают шестьдесят четыре тысячи цветов, эта методика также может быть полезна). По умолчанию передача полутонов разрешена.
Одной из важнейших возможностей DirectDraw является переключение страниц. Переключением страниц называется способ анимации при котором изображение подготавливается во внеэкранном буфере, а затем отображается на экране. Этот метод полезен по двум причинам. Во-первых, подготовка изображения в скрытом буфере позволяет устранить мерцание изображения во время анимации, поскольку перед тем как стать видимым, новый кадр уже полностью собран. Вторая причина— скорость. Переключение страниц осуществляется аппаратурой видеокарты, поэтому обновление экрана происходит практически мгновенно.
Для переключения страниц необходимы два буфера — первичный (primary buffer) и вторичный (back buffer). Первичный буфер содержит данные, которые в текущий момент времени отображаются на экране. Вторичный буфер содержит изображение, которое может быть перемещено в первичный буфер за одну операцию. Позже мы создадим и первичный, и вторичный буфер. Мы будем копировать трехмерные и двухмерные изображения во вторичный буфер, а затем выполнять переключение страниц, чтобы сделать новое изображение видимым.
Давайте попробуем переместить наш куб вверх от начала координат на две единицы. Мы можем выполнить это, используя перемещение <0, 2, 0>. Результат изображен на рис. 2.12.
Рис. 2.12. Куб после перемещения <0, 2, 0>
Объекты могут быть перемещены по нескольким осям одновременно с использованием одной операции, так что нет необходимости выполнять отдельные операции для сдвига объекта вдоль каждой из осей. Например, перемещение <2, 2, 0> передвинет куб на две единицы вправо и на две единицы вверх.
Интерфейс фрейма предоставляет функции для пошаговой корректировки позиции фрейма. Эти функции задают атрибуты движения, которые будут применяться к фрейму при каждом обновлении сцены.
Функции GetRotation() и SetRotation() используются для управления атрибутами вращения фрейма. Вращение, заданное функцией SetRotation(), оказывает эффект при каждом обновлении сцены.
Возможность размещать фреймы и назначать атрибуты движения очень важна, но ее недостаточно. Интерактивная графика требует, чтобы объекты в сцене могли взаимодействовать друг с другом и реагировать на действия пользователя. Интерфейс фрейма позволяет осуществлять коррекцию во время выполнения с помощью функций обратного вызова. Функции обратного вызова (callback) — это написанные вами функции, которые устанавливаются, или регистрируются в Direct3D. После регистрации Direct3D будет выполнять функцию обратного вызова всякий раз, когда рисуется новая сцена, что позволяет вам внести последние изменения в объекты сцены.
Функции обратного вызова устанавливаются с помощью функции AddMoveCallback(). Каждый объект фрейма может установить несколько функций обратного вызова и эти функции будут вызываться в порядке установки. Чтобы удалить функцию обратного вызова, используйте функцию DeleteMoveCallback().
Местоположение объектов определяется фреймами, но можно определить расположение сетки относительно присоединенного фрейма. Функция конструктора сеток Translate() позволяет задать смещение местоположения сетки. Благодаря этому можно присоединить к одному фрейму несколько сеток так, чтобы они не занимали одно и то же пространство.
Функция Scale() задает набор коэффициентов масштабирования для сетки. Масштабирование осуществляется в локальной системе координат сетки, как если бы центр сетки находился в начале координат. Можно задавать различные коэффициенты масштабирования для каждой из осей, чтобы сетка растягивалась и сжималась.
В начале этой главы я упомянул, что целью трехмерной графики является создание двухмерного представления трехмерной сцены. Мы обсудили совершение различных действий в невидимом, трехмерном пространстве; теперь настало время обсудить получение двухмерной версии трехмерного мира.
Переход от трех измерений к двум требует перспективного преобразования. Перспективное преобразование гарантирует, что полученное в результате изображение будет выглядеть и вести себя корректно. Корректное поведение подразумевает, что объекты, расположенные ближе к зрителю будут казаться больше, чем объекты, расположенные вдалеке. Это также значит, что объекты или части объектов, расположенные вне пределов видимости не будут рисоваться. Также нам необходимо решить, когда объект находится слишком близко или слишком далеко, чтобы рисовать его.
Плоскость (plane) это плоская поверхность, простирающаяся до бесконечности. Плоскость не является квадратом или прямоугольником, поскольку и квадрат и прямоугольник имеют грани и углы. Размер плоскости не определен. Простейший способ задать плоскость — указать ось и координату точки пересечения плоскости с этой осью. Например, плоскость, определяемая значением Y равным -3 расположена на три единицы ниже начала координат и распространяется до бесконечности вдоль осей X и Z. Эта плоскость пересекает ось Y перпендикулярно в точке с координатой -3. Часть такой плоскости показана на рис. 2.5.
Рис. 2.5. Часть плоскости пересекающей ось Y в точке -3
Заметьте, что, определяя плоскость с помощью оси и координаты, мы не можем задать плоскость, которая не была бы параллельна двум другим осям. Если мы хотим определить плоскость, которая, например, пересекает ось Y под углом 45 градусов, нам необходимо более сложное представление.
Мы можем использовать вектор, чтобы определить ориентацию плоскости. Если нам необходима плоскость, пересекающая ось Y под углом 45 градусов, мы можем использовать вектор <0, 1, -1>, чтобы определить наклон плоскости по отношению к зрителю (вектор перпендикулярен определяемой плоскости). Одного вектора недостаточно, чтобы определить плоскость, он указывает только ориентацию. Мы нуждаемся еще в указании местоположения плоскости. Можно снова использовать координату Y равную -3, чтобы указать, что плоскость пересекает ось Y на три единицы ниже начала координат. Часть этой плоскости изображена на рис. 2.6.
Рис. 2.6. Плоскость, расположенная в точке Y=-3 и ориентированная вектором <0, 1, -1>
Работающие в 8-разрядных видеорежимах полноэкранные приложения требуют, чтобы к каждой поверхности была присоединена созданная программой палитра. Класс RMWin получает палитры из файлов BMP. Для этой цели предназначена функция UsePalette().
Внутри класса RMWin для извлечения данных палитры из файла BMP и создания палитры DirectDraw используется функция InstallPalette(). Затем новая палитра присоединяется к поверхностям.
Параметры поля зрения (field-of-view, FOV) могут настраиваться посредством функции SetField(). Значение FOV по умолчанию равно 0.5. Меньшие значения сужают угол зрения, подобно длиннофокусному объективу. Большие значения увеличивают угол зрения, имитируя эффект широкоугольного объектива. Отрицательные значения не допускаются.
Во всех демонстрационных программах, которые мы рассмотрели в предыдущих главах, мы не задавали параметры поля зрения или угол обзора камеры. Это означает, что мы использовали устанавливаемое по умолчанию значение поля зрения, равное0.5.
Для изменения поля зрения порта просмотра используется функция SetField() интерфейса Direct3DRMViewport. Меньшие значения уменьшают поле зрения порта просмотра и оказывают эффект, сравнимый с использованием телеобъектива. Большие значения увеличивают поле зрения порта просмотра, подобно широкоугольному объективу.
Если у вас возникли вопросы, комментарии или замечания, можете написать автору по адресу 75233.1506@compuserve.com.
1 Графический интерфейс пользователя был разработан в компании Xerox. Часто создание графического интерфейса связывают с компанией Apple поскольку компьютер Apple Macintosh был первым широко распространенным компьютером, использующим графический интерфейс пользователя.
2 Большая часть программ, приведенных в этой книге, создавалась без применения плат с аппаратным ускорителем трехмерной графики.
Дерево зависимостей определяет порядок создания интерфейсов. Например, объект Direct3DRM располагается на вершине дерева, поэтому все остальные объекты зависят от его существования. Это значит, что объект Direct3DRM должен создаваться первым. После того, как он создан, можно создать фрейм сцены или объект отсечения. Процесс продолжается до тех пор, пока не будут созданы все объекты. На рис. 4.4 показан порядок создания объектов, соответствующий требованиям дерева зависимостей.
Рис. 4.4. Порядок создания интерфейсов
Из рисунка видно, что стандартные интерфейсы создаются в первую очередь, а после них создаются специализированные интерфейсы приложения. Это не единственный возможный порядок создания, поскольку некоторые пары интерфейсов, например, интерфейсы meshbuilder и meshframe, можно поменять местами, однако рассматриваемый далее код придерживается именно этого порядка.
В DirectDraw поверхностью (surface) называется область памяти, предназначенная для хранения изображений. Используемая поверхностью память может быть расположена как на видеокарте, так и в системной памяти. Однако отображаться на экране могут только те поверхности, которые размещены в памяти видеокарты.
Поверхности представлены интерфейсом DirectDrawSurface и могут быть созданы функцией CreateSurface() интерфейса DirectDraw. Функции CreateSurface() передаются аргументы, которые задают тип, размер и возможности создаваемой поверхности.
Существует три основных типа поверхностей: переключаемые поверхности (flipping surfaces), внеэкранные поверхности (off-screen surfaces) и Z-буферы (Z-buffers). Переключаемые поверхности используются для выполнения переключения страниц. Первичный и вторичный буфер в нашем коде будут представлять собой переключаемые поверхности. Внеэкранные поверхности используются для хранения изображений, таких как фоновые картинки и спрайты, и различных манипуляций с ними. Z-буферы мы обсудим в следующем разделе.
Интерфейс Direct3DRMFrame предоставляет ряд функций, используемых для позиционирования фреймов. К их числу относятся:
AddRotation()
AddScale()
AddTranslation()
GetOrientation()
GetPosition()
LookAt()
SetOrientation()
SetPosition()
Функции GetPosition() и SetPosition() позволяют получить или задать местоположение фрейма. Аналогично, функции GetOrientation() и SetOrientation() позволяют задать ориентацию фрейма. Корме того, интерфейс предоставляет функцию LookAt() которая ориентирует фрейм, чтобы он был направлен (указывал) на другой фрейм. Функция LookAt() удобна в ситуации, когда камера или источник света следуют за другим объектом.
Функции SetPosition(), SetOrientation() и LookAt() устанавливают новые параметры объекта фрейма независимо от предыдущих. Кроме них интерфейс фрейма предоставляет функции AddTranslation(), AddRotation() и AddScale(), которые корректируют параметры фрейма, а не заменяют их. Функция AddTranslation() корректирует местоположение фрейма, согласно указанному перемещению. AddRotation() корректирует ориентацию фрейма, добавляя новое вращение к существующему. Функция AddScale() изменяет размеры фрейма относительно текущего размера.
Итак, мы узнали, как разместить объекты в трехмерном пространстве. Мы можем задать вершины, используя вершины определить грани, а из граней создать сетку. Теперь нам необходимо узнать способы изменять положение существующих объектов. Есть три основных операции, которые могут использоваться для изменения положения объекта в пространстве: перемещение (translate), масштабирование (scale) и вращение (rotate). Используя эти три операции, мы можем переместить объект в любое место и придать ему любую ориентацию.
Прежде чем мы продолжим, давайте создадим простую сцену, которая будет применяться при обсуждении операций перемещения, масштабирования и вращения. Мы будем использовать простую сетку— куб. Центр нашего куба будет совпадать с началом координат, а размеры всех его граней будут равны 1. Такой куб показан на рис. 2.11.
Рис. 2.11. Куб для изучения преобразований
Заметьте, что поскольку центр куба совпадает с началом координат, стороны куба отстоят от центра на 1/2 единицы в каждом направлении вдоль каждой из осей координат. Также отметим, что каждая грань (каждая сторона куба) перпендикулярна пересекающей ее оси.
Для представления цветов Windows использует значения типа COLORREF. В Direct3D для той же цели применяется тип D3DCOLOR. Эти два типа несовместимы, и поэтому класс RMWin предоставляет функции преобразования типов. Функция COLORREF_2_D3DCOLOR() преобразует тип, используемый в Windows, в тип, понятный Direct3D. Функция D3DCOLOR_2_COLORREF() выполняет обратное преобразование. Код этих функций выглядит так:
inline D3DCOLOR RMWin::COLORREF_2_D3DCOLOR(COLORREF cref) { D3DVALUE r = D3DVALUE(GetRValue(cref))/D3DVALUE(255); D3DVALUE g = D3DVALUE(GetGValue(cref))/D3DVALUE(255); D3DVALUE b = D3DVALUE(GetBValue(cref))/D3DVALUE(255); return D3DRMCreateColorRGB(r, g, b); }
inline COLORREF RMWin::D3DCOLOR_2_COLORREF(D3DCOLOR d3dclr) { D3DVALUE red = D3DVALUE(255) * D3DRMColorGetRed(d3dclr); D3DVALUE green = D3DVALUE(255) * D3DRMColorGetGreen(d3dclr); D3DVALUE blue = D3DVALUE(255) * D3DRMColorGetBlue(d3dclr); return RGB((int)red, (int)green, (int)blue); }
Для улучшения производительности обе функции объявлены как inline.
СОВЕТ | ?Вопросы быстродействия. В общем случае, беспокойство о производительности приложений, использующих такие пакеты, как Direct3D подобно перестановке кресел на Титанике. Девяносто девять процентов работы в использующей Direct3D программе, выполняется непосредственно Direct3D. Код приложения составляет незначительную часть общей картины, и чтобы он сильно ухудшил производительность, его надо действительно очень плохо написать. |
В приложении Cube анимация вершин применяется для изменения формы куба. Поскольку при написании программы во главу угла ставилась простота примера, а не красота результата, анимируются только две вершины. Вид окна приложения Cube показан на рис.8.1.
Рис. 8.1. Приложение Cube
Кроме анимации вершин в приложении выполняется вращение куба путем назначения случайных векторов вращения. Это позволяет скрыть излишнюю простоту анимации вершин. Приложение Cube также предоставляет пользователю стандартное меню Render, позволяющее изменять метод визуализации сетки во время работы программы.
Приложение Cube демонстрирует нам использование следующих технологий:
Создание сетки с самого начала (сетка не загружается с диска, а собирается после запуска приложения).
Использование интерфейса Direct3DRMMesh для выполнения анимации вершин.
Изменение метода визуализации сетки с помощью команд меню.
Поддержка декалов в Direct3D представлена в приложении Decal. Приложение Decal анимирует два декала, перемещая их вокруг сцены. Окно приложения Decal показано на рис. 5.7.
Рис. 5.7. Приложение Decal
Приложение Decal демонстрирует следующие технологии:
Поддержка декалов в Direct3D.
Использование пустых фреймов для анимации объектов.
Перед тем, как перейти к рассмотрению кода приложения Decal, следует поговорить о пустых фреймах. Пустой фрейм (dummy frame) — это фрейм, используемый только для анимации других фреймов. К пустому фрейму не присоединено никаких видимых объектов, таких как сетки или декалы, и он используется как родительский фрейм для других фреймов к которым присоединены видимые объекты.
В приложении Decal выполняется анимация двух декалов, перемещающихся по орбите вокруг начала координат. Это осуществляется путем помещения в начало координат двух пустых фреймов и назначения каждому из них своих параметров вращения. Декалы присоединяются к двум другим (не пустым) фреймам. Каждый из этих фреймов с декалами является дочерним для одного из пустых фреймов и размещается на некотором расстоянии от начала координат. Поскольку дочерние фреймы имитируют движение родителя, фреймы с декалами будут перемещаться по орбите вокруг своих родительских фреймов.
Приложение FacePick отображает на экране сетку и позволяет выбирать отдельные ее грани. Выбранная грани меняет свой цвет на тот, который указан в меню Colors. Сетку можно поворачивать, выбрав любую часть порта просмотра, которая не занята сеткой и перемещая мышь. Это позволяет менять ориентацию сетки и дает возможность менять цвет любой грани сетки. Кроме того, меню File позволяет загружать и сохранять сетки. Внешний вид окна приложения FacePick показан на рис.9.3.
Рис. 9.3. Приложение FacePick
Приложение FacePick демонстирирует использование следующих технологий:
Использование функции Pick() интерфейса Direct3DRMViewport для операции выбора граней.
Использование мыши для манипуляций с видимыми объектами.
Загрузка и сохранение файлов сеток.
Использование класса MFC CFileDialog.
Использование класса MFC CColorDialog.
В приложении Firefly точечный источник света применяется для освещения чаши. Чаша помешена в начало координат, а источник света перемещается вокруг нее. Вместе с источником света перемещается небольшая сферическая сетка, что создает иллюзию, будто именно эта сфера (светлячок) испускает лучи и освещает чашу. Помните, что сфера добавлена только ради визуального эффекта. Вы не можете увидеть точечный источник света (как и любой другой источник света)— вы можете видеть только испускаемый им свет. Вид окна приложения Firefly показан на рис. 6.4.
Рис. 6.4. Приложение Firefly
Если вы запустите приложение Firefly, то увидите, что местоположение источника света влияет на освещение чаши. С помощью меню Render можно изменить метод визуализации. Заметьте, что при использовании каркасного режима визуализации источник света не оказывает никакого влияния на изображение сцены.
В приложении Firefly демонстрируются следующие технологии:
Использование точечного источника света.
Представление источника света с помощью сетки.
Использование нескольких методов визуализации в одной сцене.
Выполнение анимации с помощью пустых фреймов.
Рассматривая код приложения Firefly мы подробнее обсудим каждую из этих технологий.
В оставшейся части главы мы изучим код приложения FullScreen. FullScreen— это приложение Direct3D, работающее в полноэкранном режиме. Приложение использует полноэкранную версию класса RMWin, чтобы получить возможность переключать видеорежимы и выполнять переключение страниц. Вид экрана приложения показан на рис. 10.2.
Рис. 10.2. Приложение FullScreen
Приложение FullScreen осуществляет анимацию знакомой нам сетки завитка в полноэкранном режиме, используя интерфейс Direct3DRMAnimation. Более интересен тот факт, что приложение отображает список всех обнаруженных видеорежимов и позволяет активировать любой из них. Для выбора видеорежима используются клавиши управления курсором, выбранный режим включается при нажатии на клавишу ENTER.
Когда вы запускаете приложение FullScreen, на экране отображаются все видеорежимы, поддерживаемые вашей видеокартой. Меню, изображенное на рис. 10.2 показывает видеорежимы, поддерживаемые видеокартой ATI Mach 64.
СОВЕТ | Ограничения, накладываемые монитором. Чтобы видеорежим работал правильно, его должны поддерживать и видеокарта, и монитор. Приложение FullScreen отображает видеорежиы, поддерживаемые видеокартой. Они могут поддерживаться, а могут и не поддерживаться монитором. Если вы выбрали видеорежим, и экран остается темным более 10 секунд, нажмите клавишу ESC, чтобы завершить работу приложения и вернуться к рабочему столу Windows. |
Приложение также отображает индикатор скорости работы — счетчик частоты кадров (FPS).
Приложение FullScreen демонстрирует применение следующих технологий:
Создание полноэкранных приложений Direct3D.
Переключение страниц.
Добавление двумерных поверхностей к трехмерной сцене.
Использование функций Win32 для вывода текста на поверхности DirectDraw.
Вычисление частоты кадров (FPS).
Альтернатива традиционному меню Windows.
Приложение MeshPick отображает девять сферических сеток и позволяет использовать мышь для их выбора и перетаскивания. Сетки могут быть размещены одна поверх другой, что позволяет проверить точность работы механизма выбора объектов. Внешний вид окна приложения MeshPick показан на рис. 9.2.
Рис. 9.2. Приложение MeshPick
Приложение MeshPick демонстрирует использование следующих техник:
Использование функции Pick() интерфейса Direct3DRMViewport.
Использование мыши для манипуляций с видимыми объектами сцены.
Мы подробно обсудим каждую из этих техник при изучении кода приложения MeshPick.
Приложение Molecule использует иерархию фреймов для моделирования структуры гипотетической молекулы. Оно конструирует иерархию фреймов и присоединяет к каждому фрейму в иерархии сферическую сетку. Размер и цвет присоединяемой сетки зависит от местоположения фрейма в иерархии. Приложение Molecule предоставляет команды меню, позволяющие настраивать сложность структуры молекулы. Внешний вид окна приложения Molecule показан на рис. 7.3.
Рис. 7.3. Приложение Molecule
Меню Depth позволяет настраивать глубину иерархии фреймов. Значение глубины можно изменять в диапазоне от одного до шести. Если значение равно единице, молекула будет состоять всего лишь из одной сферы. Если значение равно шести, то в иерархии будет шесть уровней дочерних фреймов.
Меню Children позволяет задать количество дочерних фреймов. По умолчанию у каждого фрейма в иерархии есть два потомка. Команды меню Children позволяют изменять число потомков каждого фрейма от одного до четырех.
Эти два меню позволяют изменять иерархию фреймов приложения Molecule в широких пределах. Например, если установить значение глубины равным единице, иерархия будет состоять всего из одного фрейма. Значения по умолчанию (глубина = 4, количество потомков = 2) приведут к созданию 15 фреймов, а если задать максимально возможные значения (глубина = 6, количество потомков = 4), то будет создано 1365 фреймов.
Приложение Molecule демонстрирует следующие технологии:
Использование интерфейса Direct3DRMFrame и рекурсивных функций для создания иерархии фреймов.
Использование нескольких экземпляров сетки.
Использование команд меню для настройки параметров иерархии фреймов.
Приложение MorphPlay является универсальным проигрывателем последовательностей трансформаций. Оно позволяет загрузить и воспроизвести файл трансформации (.MRF). На идущем вместе с книгой CD-ROM вы можете найти несколько файлов MRF в каталоге MESHES.
Файлы MRF — это X-файлы, отвечающие следующим требованиям:
Файл содержит не менее двух сеток.
все сетки в файле имеют одинаковое количество вершин.
Все сетки последовательно именуются буквами латинского алфавита в нижнем регистре, начиная с буквы «a».
Расширение MRF является необязательным. Приложение MorphPlay может загружать как файлы с расширением MRF, так и файлы с расширением X. После загрузки файла, содержащиеся в нем сетки становятся шагами трансформации. Файл может содержать до 26 шагов трансформации — по числу букв алфавита. Шаги трансформации используются в алфавитном порядке.
В приложении есть меню Morph, состоящее из трех пунктов: Forward, Reverse и Both. При выборе пункта Forward процесс трансформации начинается с первой сетки (сетки a), которая преобразуется во вторую сетку (сетку b) и т.д. Процесс трансформации продолжается пока не будет достигнута последняя из содержащихся в файле сеток. Затем воспроизведение начинается заново с самой первой сетки. При выборе пункта Reverse трансформация идет в обратном направлении — от последней сетки к первой. Выбранный по умолчанию пункт, Both, вызывает попеременную трансформацию в прямом и обратном направлениях.
Меню Speed позволяет изменить скорость трансформации. В приложении можно выбирать одну из пяти возможных скоростей. Меньшее значение скорости означает более медленное приращение времени, и в результате получается более плавное движение.
С помощью мыши можно поворачивать и наклонять трансформируемую сетку. Нажмите и удерживайте левую кнопку мыши, после чего перемещайте мышь — это позволит вам рассмотреть процесс трансформации с различных углов.
Внешний вид окна приложения MorphPlay показан на рис. 8.4.
Рис. 8.4. Приложение MorphPlay
Приложение MorphPlay демонстрирует использование следующих технологий:
Создание последовательности трансформаций с использованием в качестве шагов трансформации одной или нескольких сеток.
Использование интерфейса Direct3DRMAnimation для выполнения анимации вершин.
Использование заголовка окна для отображения различной информации.
Использование класса MFC CFileDialog.
Приложение MultiView отображает единственную вращающуюся сетку, но для ее показа используются три порта просмотра. Приложение предоставляет меню, позволяющее настроить или отключить любой из портов просмотра. Кроме того, приложение позволяет загружать другие сетки. Вид окна приложения MultiView показан на рис.9.4.
Рис. 9.4. Приложение MultiView
Приложение MultiView демонстрирует использование следующих технологий:
Использование в одном приложении нескольких портов просмотра.
Использование команд меню для настройки местоположения порта просмотра.
Использование класса MFC CFileDialog для реализации команды Open меню File.
Приложение OrbStar использует прозрачность текстуры при анимации звезды, помещенной внутри сферы. И сетка звезды, и сетка сферы расположены в начале координат. Сфера масштабируется таким образом, чтобы она была больше звезды, поэтому если наложенная на сферу текстура не имеет прозрачных участков, звезда не видна зрителю. Окно приложения OrbStar показано на рис.5.8.
Рис. 5.8. Приложение OrbStar
Текстура, которая будет наложена на сферу в данном приложении, изображена на рис. 5.9.
Рис. 5.9. Текстура, используемая в приложении OrbStar
Приложение OrbStar демонстрирует следующие технологии:
Использование прозрачных текстур.
Использование нескольких функций обратного вызова.
Использование функции D3DRMVectorRandom() для генерации случайного вектора.
Книги по программированию интересны своим разнообразием. Большинство из них продаются вместе с дискетой или CD-ROM. Некоторые, особенно те, которые комплектуются CD-ROM, включают различные бесплатные, условно-бесплатные и пробные версии программ, каким-либо образом связанных с темой книги. Такие дополнения великолепны, но наиболее ценной частью содержимого диска остается код примеров. В конце концов, программы зачастую можно раздобыть и где-нибудь в другом месте.
Качество примеров, поставляемых вместе с книгами, также весьма различается. Примеры из некоторых книг даже невозможно скомпилировать. Иногда примеры компилируются, но не работают. А в некоторых книгах вы найдете высококачественный профессиональный код, который окажется ценным подспорьем для многих читателей.
На сопроводительном CD-ROM к этой книге вы не найдете бесплатных и условно-бесплатных программ. Честно говоря, у меня просто не было времени для их поиска, поскольку большую часть моего времени заняла работа над демонстрационными программами для книги. Я предположил, что вы предпочтете получить хорошие примеры кода, а не какие-то условно-бесплатные программы, и соответствующим образом потратил свое время. Прогулки по Интернету в поиске дополнительных инструментальных средств я оставляю вам.
В приложении Rocket ключевые кадры используются для анимации ракеты. Анимационная последовательность определена с помощью нескольких ключевых кадров, а оставшаяся часть последовательности вычисляется экземпляром интерфейса Direct3DRMAnimation. Приложение предоставляет меню Animation, позволяющее изменять параметры анимации во время работы программы. В этом меню два пункта: Linear и Spline. Корме того, предоставляется меню Speed, позволяющее регулировать скорость анимационной последовательности.
Окно приложения Rocket изображено на рис. 7.4.
Рис. 7.4. Приложение Rocket
Приложение Rocket демонстрирует следующие технологии:
Использование интерфейса Direct3DRMAnimation для создания и выполнения анимационной последовательности с ключевыми кадрами.
Применение текстуры к сетке с помощью параметров текстуры сетки (вместо использования наложения текстуры).
Использование функции обратного вызова для изменения анимационной последовательности.
Приложение Shadow создает сцену, в которой вилка висит над прямоугольной площадкой. Источник света освещает вилку и площадку, и вилка отбрасывает тень (или, кажется, что отбрасывает). Окно приложения Shadow показано на рис. 6.10.
Рис. 6.10. Приложение Shadow
Приложение Shadow демонстрирует следующие технологии:
Использование интерфейса Direct3DRMShadow.
Использование функции обратного вызова для изменения параметров анимации во время выполнения программы.
Генерация случайных векторов с помощью функции D3DRMVectorRandom().
В приложении ShowRoom анимация текстуры выполняется путем последовательного наложения на одну и ту же сетку нескольких текстур из массива. Используемая в приложении ShowRoom сетка представляет собой простой куб. На используемых текстурах представлено изображение автомобиля с разных направлений. При каждом обновлении экрана используется новая текстура, поэтому зритель видит анимированное изображение автомобиля. Сетка, на которую накладываются текстуры, вращается, так что анимированы и сетка, и текстура. Вид окна приложения ShowRoom показан на рис.5.11.
Рис. 5.11. Приложение ShowRoom
В приложении ShowRoom демонстрируется применение следующих технологий:
Выполнение анимации текстуры с использованием нескольких текстур.
Использование интерфейса Direct3DRMMesh.
Анимация с использованием атрибутов вращения.
Подобно приложению TextureDrift, приложение ShowRoom использует интерфейс Direct3DRMMesh, чтобы избежать снижения быстродействия, вызванного выполняемой интерфейсом Direct3DRMMeshBuilder лишней работой.
В приложении SpaceDonut параллельно-точечный источник света размещен между двумя пончиками с малиновой (а может быть черничной) глазурью. Окно приложения SpaceDonut показано на рис.6.7.
Рис. 6.7. Приложение SpaceDonut
Приложение SpaceDonut демонстрирует применение следующих технологий:
Использование параллельно-точечного источника света.
Использование атрибутов вращения для анимации объектов.
Использование функции SetColorRGB() интерфейса Direct3DRMMeshBuilder.
Присоединение одной сетки к нескольким фреймам.
Использование команд меню для изменения параметров визуализации во время работы программы.
Поскольку направленный свет хорошо подходит для представления удаленных источников света, следующая демонстрация будет проведена в космосе. Приложение SpaceStation отображает космическую станцию, освещенную источником направленного света. Вид окна этого приложения показан на рис.6.5.
Рис. 6.5. Приложение SpaceStation
Приложение SpaceStation демонстрирует следующие технологии:
Использование источника направленного света.
Анимация с использованием атрибутов вращения.
Изменение метода визуализации сетки во время работы приложения.
Изменение верхнего вектора камеры для ее наклона.
В приложении Spotlight анимированный прожектор освещает три сферических сетки. Угол светового пятна и угол зоны освещенности прожектора могут быть изменены во время работы программы с помощью команд меню Beam. Окно приложения Spotlight изображено на рис. 6.9.
Рис. 6.9. Приложение Spotlight
Приложение Spotlight демонстрирует использование следующих технологий:
Применение источника зонального света (прожектора).
Использование функции обратного вызова для анимации источника света.
Использование интерфейса Direct3DRMMesh чтобы избежать падения быстродействия, связанного с лишней работой, выполняемой при использовании интерфейса Direct3DRMMeshBuilder.
Изменение параметров визуализации объекта Direct3DRMMesh во время работы программы.
Изменение во время работы программы угла светового пятна и угла зоны освещенности прожектора.
Чтобы продемонстрировать альтернативный способ, отличный от использовавшегося в других демонстрационных программах, в приложении Spotlight вместо интерфейса Direct3DRMMeshBuilder используется интерфейс Direct3DRMMesh. В этом нет ничего необычного, так как приложение Spotlight требует повышенного быстродействия, что и достигается использованием интерфейса Direct3DRMMesh.
Приложение Target создает сцену, в которой группа ракет или реактивных снарядов следит за целью. Движение цели определяется анимационной последовательностью, подобной той, которая использовалась для анимации сетки ракеты в приложении Rocket. Анимируется каждая ракета. Все они следят за движением цели. Кроме того, в приложении Target используется анимация камеры. Камера облетает сцену по орбите и всегда направлена на ракеты. Окно приложения изображено на рис. 7.5.
Рис. 7.5. Приложение Target
Приложение Target демонстрирует следующие технологии:
Использование функции LookAt() интерфейса Direct3DRMFrame для изменения ориентации фрейма.
Использование интерфейса Direct3DRMAnimation для создания и выполнения анимационной последовательности.
Анимация камеры с помощью пустого фрейма.
Использование нескольких экземпляров сетки.
Мы поговорим о каждой из этих техник в ходе обсуждения кода приложения Target.
В приложении TextureDrift используется одна сетка и одна текстура. При каждом обновлении экрана текстура заново накладывается на сетку со слегка измененным значением начала координат, что вызывает эффект скольжения текстуры по сетке. Окно приложения TextureDrift показано на рис. 5.10 (чтобы увидеть анимацию текстуры, вам необходимо запустить приложение TextureDrift на своем компьютере).
Рис. 5.10. Приложение TextureDrift
Приложение TextureDrift демонстрирует следующие технологии:
Анимация отдельной текстуры путем изменения параметров наложения текстуры на сетку.
Применение интерфейса Direct3DRMMesh чтобы увеличить быстродействие, благодаря отказу от дополнительной работы, выполняемой интерфейсом Direct3DRMMeshBuilder.
Использование функции GetVisuals() интерфейса Direct3DRMFrame для получения указателя на присоединенный к фрейму объект.
В приложении TextureDrift используется техника, которая слегка отличается от той, которая применялась в рассмотренных ранее приложениях. Ранее для создания и отображения сеток использовался интерфейс Direct3DRMMeshBuilder (за исключением приложения Decal в котором вообще не использовались сетки). В приложении TextureDrift интерфейс Direct3DRMMeshBuilder применяется для создания сетки, но не для ее отображения. Вместо этого интерфейс Direct3DRMMeshBuilder используется для создания экземпляра интерфейса Direct3DRMMesh. Это делается из-за соображений быстродействия. В главе 3 говорилось, что всякий раз, когда выполняется модификация параметров сетки, экземпляр интерфейса Direct3DRMMeshBuilder создает внутри себя экземпляр интерфейса Direct3DRMMesh. Чтобы приложение TextureDrift не выполняло лишней работы, мы используем интерфейс Direct3DRMMesh непосредственно.
Это не означает, что предыдущие приложения были плохо спроектированы. Если во время работы приложения не выполняется частой модификации параметров сеток, интерфейс Direct3DRMMeshBuilder обеспечивает хорошее быстродействие. В приложении TextureDrift мы применили интерфейс Direct3DRMMesh из-за того, что характеристики сетки меняются при каждом обновлении экрана.
Приложение Wraps отображает три сетки: куб, цилиндр и сферу. На каждую сетку накладывается единственная текстура, но метод наложения текстуры можно изменять. По умолчанию для куба используется плоское наложение, для цилиндра — цилиндрическое и для сферы — сферическое. Чтобы изменять тип наложения текстуры на сетку, приложение предоставляет соответствующие команды меню. Окно приложения Wraps показано на рис. 5.5.
Рис. 5.5. Приложение Wraps
В приложении Wraps используется слегка модифицированная версия текстуры из приложения Jade. Линии, нарисованные поперек текстуры, делают видимой деформацию, происходящую при наложении текстуры. Измененная текстура показана на рис. 5.6.
Рис. 5.6. Текстура, используемая в приложении Wraps
Приложение Wraps демонстрирует следующие технологии:
Загрузка и отображение нескольких сеток.
Применение произвольного способа наложения текстуры на сетку во время выполнения программы. Меню Wraps позволяет изменить метод наложения текстуры на объект во время работы программы.
В фотографии линза, используемая для изменения поля зрения, называется трансфокатором. Трансфокаторы позволяют получить крупный план удаленных объектов и общий план для просмотра больших пейзажей.
В приложении Zoom мы воспользуемся функцией SetField() для настройки поля зрения порта просмотра. Приложение размещает сетку в начале координат, а потом демонстрирует результаты трансфокации, используя размещенную в фиксированной позиции камеру. Внешний вид окна приложения Zoom показан на рис. 9.1 (учтите, что на рисунке вы не увидите результаты изменения угла зрения).
Рис. 9.1. Приложение Zoom
Если вы запустите приложение Zoom, вам покажется, что сетка перемещается. Но, хотя сетка и вращается, ее местоположение остается неизменным. Иллюзия перемещения вызывается изменениями параметров поля зрения порта просмотра.
Приложение Zoom поддерживает обычное меню Render, позволяющее во время работы программы изменять используемый метод визуализации. Кроме того, в приложении есть меню Animation. Оно позволяет изменять используемый способ анимации (линейную или сплайновую).
Приложение Zoom демонстрирует следующие технологии:
Использование функции SetField() интерфейса Direct3DRMViewport.
Использование интерфейса Direct3DRMAnimation для выполнения универсальной анимации (не анимации фреймов).
Изменение метода визуализации сетки во время работы приложения.
Использование интерфейса Direct3DRMMaterial для изменения внешнего вида сетки.
Текстура представляет собой двухмерное цветное изображение. Часто текстуры хранятся в файлах типа BMP, PCX или GIF. Практически любое двухмерное изображение можно использовать как текстуру. Пример текстуры вы можете увидеть на рис.2.17.
Рис. 2.17. Пример текстуры
Прежде чем вы начнете использовать различные изображения в качестве текстур, обратите внимание на одну особенность — не все изображения будут хорошими текстурами. Хорошие текстуры обычно не предназначены для отдельного просмотра, но они придают реализм объектам. Изображения, на которых представлены законченные сцены, обычно не годятся для использования в качестве текстур, поскольку вы не ожидаете увидеть законченную сцену, глядя на единственную грань объекта. Такие трехмерные объекты, как зеркала и картины, являются исключением из этого правила.
Мы используем мастер Direct3D AppWizard для создания проекта, который назовем Sample. В главе 1 был приведен краткий обзор действий, необходимых для создания нового проекта, так что здесь мы повторять их не будем. Мы примем все, предлагаемые мастером значения по умолчанию, но с одним исключением: вместо применяемого по умолчанию источника направленного освещения, мы создадим анимированный источник зонального освещения. На рис. 4.1 показано диалоговое окно Lighting мастера Direct3D AppWizard.
Рис. 4.1. Диалоговое окно выбора источника освещения в мастере Direct3D AppWizard
Очистите флажок Directional, установите флажок Spotlight, а затем установите флажок Animate spotlight. Рисунок показывает выбранные параметры.
В остальных диалоговых окнах примите предлагаемые значения по умолчанию. Мастер создаст готовый к компиляции проект.
Программы для Windows имеют необычный вид. Программирование, управляемое событиями это не единственная вещь к которой необходимо приспособиться. Одна из причин отличия внешнего вида программ для Windows — частое использование венгерской нотации. Венгерская нотация использует имена переменных с префиксами, определяющими тип каждой переменной и предназначена для языков в которых отсутствует строгая проверка совпадения типов.
В то же время строгая проверка соответствия типов является одной из ключевых особенностей C++. Это значит, что в C++ уже решена проблема, для которой разрабатывалась венгерская нотация. Проверку соответствия типов в С++ выполняет компилятор, а не программист. Это позволяет свободно сконцентрироваться на более важных вещах (например, на трехмерной графике). В программах, рассматриваемых в этой книге, венгерская нотация не используется.
Другим отличием от общепринятой традиции, является то, что в этой книге классы написаны таким образом, что открытые (public) функции-члены расположены в начале класса. Это объясняется тем, что открытый интерфейс обычно представляет наибольший интерес для пользователей класса. Закрытые (private) данные и функции-члены класса располагаются ближе к концу, поскольку они не могут использоваться порождаемыми или внешними классами.
У вас будет множество возможностей применить полученные в этой главе знания, поскольку вся остальная часть книги посвящена созданию различных приложений, использующих возможности Direct3D.
Наиболее фундаментальным различием между Windows и DOS является то, что Windows представляет собой операционную систему, управляемую событиями (event-driven). Это означает, что практически все, происходящее в Windows, является ответом на сообщение или событие (в этой книге мы будем использовать термины сообщение и событие как синонимы).
В DOS вы указываете точку входа — место, с которого DOS начинает выполнение вашей программы. Точкой входа для DOS является функция main(). DOS вызывает вашу функцию main() и программа делает то, для чего предназначена. Вы не должны выходить из функции main(), пока программа не завершит свою работу. Как только будет выполнен возврат из функции main(), DOS прекратит выполнение вашей программы.
Благодаря предоставляемым мощности и простоте использования интерфейса Direct3DRMMeshBuilder, конструкторы сеток являются оптимальным выбором для большинства задач. Однако в некоторых ситуациях применение интерфейса конструктора сеток может привести к снижению производительности. Если вы используете конструктор сетки для загрузки данных, настройки параметров и размещения полученной сетки в сцене, не предусматривающей частого изменения параметров, применение интерфейса конструктора сеток будет оптимальным вариантом. Однако, если параметры сетки (цвет, текстуры, позиции вершин и т.д.) будут часто изменяться во время выполнения, конструктор сеток снизит производительность вашего приложения. Чуть позже мы обсудим альтернативные варианты работы с сетками.
Прожектор излучает свет в форме конуса. Местоположение источника света влияет на местоположение вершины конуса, а ориентация источника света влияет на местоположение основания светового конуса.
В действительности, свет, испускаемый прожектором, лучше описать в терминах двух конусов: внутреннего и внешнего. Внешний конус определяет область, освещаемую прожектором. За пределами внешнего конуса свет отсутствует. Внутренний конус определяет область с максимальной интенсивностью света. В области между этими двумя конусами освещенность постепенно уменьшается от максимальной интенсивности во внутреннем конусе до отсутствия света за пределами внешнего конуса.
Внутренний конус называется конусом светового пятна (umbra cone) и определяется углом светового пятна (umbra angle). Внешний конус называется конусом зоны освещенности (penumbra cone) и определяется углом зоны освещенности (penumbra angle). Эти конусы показаны на рис.6.8.
Рис. 6.8. Анатомия прожектора
Интерфейс Direct3DRMLight предоставляет следующие функции для настройки и контроля угла светового пятна и угла зоны освещенности:
GetPenumbra()
GetUmbra()
SetPenumbra()
SetUmbra()
Интерфейс Direct3DRMTexture позволяет вам указать цвет текстуры, который будет восприниматься Direct3D как прозрачный. Сквозь прозрачные участки текстуры можно видеть сетки и текстуры, которые в ином случае были бы скрыты от глаз зрителя.
По умолчанию прозрачность текстур отключена. Для разрешения и запрещения прозрачности текстур может применяться функция SetDecalTransparency() интерфейса Direct3DRMTexture. После разрешения прозрачности любые черные точки текстуры будут считаться прозрачными. Цвет, который считается прозрачным, можно изменить с помощью функции SetDecalTransparencyColor().
Каждое из демонстрационных приложений, рассмотренных в этой главе к данному моменту, было предназначено, чтобы ознакомить вас с определенным типом источников света. Чтобы ясно показать особенности, в каждом приложении использовался только один источник света.
Это не означает, что в своих программах вы не можете использовать более одного источника света. Источники света могут применяться в любой комбинации, и, кроме того, в программе можно использовать несколько экземпляров источника света одного и того же типа.
Простейший источник света— это рассеянный свет (ambient light). Источник рассеянного света не имеет местоположения и освещает все объекты сцены с одинаковой интенсивностью. Это освещение удобно, потому что его просто использовать. Часто рассеянный свет используется в комбинации с более сложными источниками света.
В реальном мире рассеянный свет является отраженным светом. Рассеянный свет— это свет, который был отражен и рассеян в окружающем пространстве. Например, вечером рассеянный свет позволяет нам видеть окружающие предметы уже после того, как солнце скрылось. Лучи солнца рассеиваются в атмосфере и обеспечивают ровное, слабое освещение не имеющее направления и видимого источника.
Свет, который действительно не имеет направления и источника, в реальном мире не существует. Отраженные и рассеянные в атмосфере солнечные лучи приблизительно похожи на рассеянный свет — в действительности солнечный свет распространяется в конкретном направлении и у него есть неявный источник (солнце). В Direct3D рассеянный свет не имеет ни направления, ни источника.
С технической точки зрения рассеянный свет подобен остальным источникам света. Он представляется интерфейсом Direct3DRMLight и, чтобы стать видимым, должен быть присоединен к фрейму. Однако источник рассеянного света игнорирует местоположение и ориентацию того фрейма, к которому он подсоединен.
Рассеяный свет является самым простым типом источника света, поскольку имеет всего один параметр: цвет. Хотя источник рассеяного света и должен быть перед помещением в сцену присоединен к фрейму, он игнорирует местоположение и ориентацию фрейма. Поэтому источник рассеяного света может быть присоединен к любому фрейму сцены.
В сцене может быть любое количество источников рассеяного света. Получаемое в результате освещение будет определяться суммой параметров всех источников рассеяного света. Например, добавление в сцену трех источников рассеяного света с красным, зеленым и синим цветом, аналогично добавлению единственного источника рассеяного света с белым цветом.
Равномерная закраска (flat shading) позволяет получить более реалистичное изображение, чем каркасный или неосвещенный методы визуализации. При равномерной закраске учитывается освещенность грани. Для каждой грани вычисляется нормаль, которая применяется для вычисления параметров освещенности всей грани. Равномерная закраска требует большего объема вычислений, чем каркасный и неосвещенный методы. Рис.2.24 показывает сцену, визуализированную с применением равномерной закраски. Обратите внимание, как выделяется каждая грань.
Рис. 2.24. Изображение, полученное методом равномерной закраски
Прежде чем перейти к детальному обсуждению используемых функций, разделим их на четыре категории. Каждая категория соответствует стадии, или фазе, выполнения программы.
Инициализация Direct3D.
Создание сцены.
Управление сценой.
Завершение работы.
Большая часть материалов этой главы посвящена первому этапу, потому что он наиболее сложный (по крайней мере, пока). После инициализации работа с Direct3D не представляет сложностей. Второй и третий этапы детально обсуждаются в следующих пяти главах. Завершающая стадия представляет собой просто удаление всех созданных интерфейсов.
Давайте отвлечемся от COM-интерфейсов Direct3D и поговорим о классах C++. Мы будем применять классы C++ для управления сложностью программ. Эти классы не являются заменой интерфейсов Direct3D. Классы C++ будут содержать интерфейсы Direct3D и управлять ими.
Программы из этой книги используют библиотеку MFC. Стратегия состоит в использовании функциональности MFC без поддержки архитектуры документ/представление. Мы воспользуемся двумя классами MFC: CWinApp и CFrameWnd. Класс CWinApp представляет приложение Windows, а класс CFrameWnd представляет окно приложения. Класс CWinApp будет применяться как базовый для класса конкретного приложения, а класс CFrameWnd будет базовым для класса окна конкретного приложения Direct3D. Класс, производный от CWinApp мы назовем RMApp (RM от Retained Mode), а класс, производный от CFrameWnd соответственно назовем RMWin.
Однако, мы не станем помещать всю функциональность приложения в эти два класса. Вместо этого в них мы поместим стандартную функциональность Direct3D — те функции, которые остаются неизменными от приложения к приложению. Затем мы создадим еще два класса, в которые поместим код, специфичный для данного приложения. Имена этих классов вы можете задать сами (мастер создания приложений предложит вам стандартные варианты названий, но вы можете изменить их). Для рассматриваемого в этой главе приложения Sample мы назовем эти классы SampleApp и SampleWin. Рис. 4.5 представляет дерево наследования для классов нашего приложения.
Рис. 4.5. Функциональность классов и наследование
На рисунке изображены четыре класса, о которых еще ничего не говорилось: CObject, CCmdTarget, CWinThread и CWnd. CObject — это базовый класс MFC. Почти каждый класс MFC является производным от CObject. Класс CCmdTarget реализует большую часть предоставляемых MFC возможностей обработки сообщений. Производные от CCmdTarget классы наследуют возможность использования карт сообщений. Класс CWinThread предоставляет поддержку многопоточности. Класс CWnd — это класс окна в MFC, обеспечивающий поддержку большинства функций окна.
Сетка (mesh) — это набор соединенных граней. Обычно сеть описывает один объект в сцене. Сетка может объединять одну или несколько граней, и может быть очень сложной. На рис. 2.8 показан пример сетки. Если вы внимательно посмотрите на рисунок, то сможете выделить отдельные грани.
Рис. 2.8. Визуализированная сетка
Второе действие, выполняемое функцией CreateScene(),— создание источника зонального света:
LPDIRECT3DRMLIGHT slight; d3drm->CreateLightRGB(D3DRMLIGHT_SPOT, D3DVALUE(1.00), D3DVALUE(1.00), D3DVALUE(1.00), &slight);
Функция CreateLightRGB() интерфейса Direct3DRM применяется для создания источника света slight (s — сокращение от spotlight, т.е. прожектор). Константа D3DRMLIGHT_SPOT задает тип создаваемого источника света. Другие возможные значения — D3DRMLIGHT_AMBIENT, D3DRMLIGHT_DIRECTIONAL, D3DRMLIGHT_PARALLELPOINT и D3DRMLIGHT_POINT.
Затем создается фрейм с именем slightframe:
LPDIRECT3DRMFRAME slightframe; d3drm->CreateFrame(scene, &slightframe);
Этот фрейм, аналогично фрейму конструктора сеток, использует в качестве родителя фрейм scene. Затем, посредством функции SetPosition() задается местоположение нового фрейма:
slightframe->SetPosition (scene, D3DVALUE(0),D3DVALUE(20),D3DVALUE(-20));
Первый аргумент задает систему координат, а остальные указывают новое местоположение фрейма. Функция SetPosition() для определения нового местоположения фрейма использует систему координат указанного фрейма. В данном примере в качестве системы координат используется корневой фрейм сцены, поэтому задающие местоположение значения указывают абсолютную позицию. Корневой фрейм расположен в начале координат, поэтому источник света будет помещен на 20 единиц выше начала координат и на 20 единиц позади. Если бы, например, мы указали те же самые значения, но в качестве системы координат задали фрейм, расположенный в точке <0, 100, 0>, позиция нового фрейма была бы <0, 120, –20>.
Далее, с помощью функции SetOrientation() задается ориентация фрейма slightframe:
slightframe->SetOrientation(scene, D3DVALUE(0), D3DVALUE(-20), D3DVALUE(20), D3DVALUE(0), D3DVALUE(1), D3DVALUE(0));
Подобно функции SetPosition(), функция SetOrientation() требует, чтобы в качестве первого аргумента передавалась ссылка на фрейм, однако, SetOrientation() требует наличия шести дополнительных аргументов.
Эти шесть значений определяют два вектора, задающих новую ориентацию фрейма. Первый вектор задает направление для лицевой грани фрейма. По умолчанию фрейм расположен вдоль оси Z и направлен от зрителя. По этой причине первый вектор называют вектором оси Z. Второй вектор называют вектором оси Y потому что по умолчанию он направлен вверх параллельно оси Y.
Возможно, более интуитивно понятными для этих векторов будут названия передний (forward) и верхний (up) вектор. Передний вектор указывает, куда направлена лицевая сторона фрейма. Верхний вектор указывает куда направлена верхняя грань фрейма (этот вектор иногд называют небесным (sky) вектором).
В нашем коде мы используем передний вектор <0, –20, 20>. Это значит, что лицевая грань фрейма направлена к точке <0, –20, 20>, таким образом, лицевая грань фрейма будет на 20 единиц ниже и на 20 единиц ближе (относительно начала координат). В качестве верхнего вектора мы оставим предлагаемый по умолчанию вектор <0, 1, 0>. Мы подробнее изучим фреймы и их ориентацию в главе 7.
Дальнейшая часть кода функции CreateScene() осуществляет создание и настройку параметров источника зонального света.
slightframe->AddLight(slight); slightframe->AddMoveCallback(MoveLight, NULL); slight->Release(); slight = 0; slightframe->Release(); slightframe = 0;
Функция AddLight() применяется для присоединения источника света к только что настроеному фрейму.
Затем вызывается функция AddMoveCallback(). Эта функция применяется для установки функции обратного вызова, которая будет использоваться для изменения позиции и ориентации фрейма во время выполнения программы. Мы поговорим об обратных вызовах и о функции AddMoveCallback() позже в этой главе.
В конце происходит освобождение указателей slight и slightframe чтобы уведомить объекты, что эти указатели больше не будут использоваться (их область видимости ограничена функцией CreateScene()).
Интерфейс Direct3DRMAnimation позволяет конструировать анимационные последовательности на основе ключей (keys). Каждый ключ представляет собой коэффициенты перемещения, вращения или масштабирования, применяемые к объекту в заданной точке анимации. Затем интерфейс анимации может выполнить анимацию целиком, основываясь на нескольких ключах и автоматически вычисляя параметры объекта между ключевыми кадрами.
Для создания ключевых кадров в интерфейсе анимации применяются следующие функции:
AddPositionKey()
AddRotateKey()
AddScaleKey()
Каждая функция добавления ключа требует указания времени и преобразования. Время определяет момент анимации, в который ключ окажет действие. Преобразование — это позиционирование, поворот или масштабирование, которое должно произойти в ключевом кадре. Общая длина анимации определяется ключом с самым большим значением времени. Для удаления ключей из анимации применяется функция DeleteKey().