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

         

Класс MorphPlayWin


Класс MorphPlayWin построен на базе класса MorphWin для создания законченного приложения. Определение класса выглядит следующим образом:

class MorphPlayWin : public MorphWin { public: MorphPlayWin(); BOOL CreateScene(); protected: //{{AFX_MSG(MorphPlayWin) afx_msg void OnFileOpen(); afx_msg void OnMorphForward(); afx_msg void OnMorphReverse(); afx_msg void OnMorphBoth(); afx_msg void OnUpdateMorphForward(CCmdUI* pCmdUI); afx_msg void OnUpdateMorphReverse(CCmdUI* pCmdUI); afx_msg void OnUpdateMorphBoth(CCmdUI* pCmdUI); afx_msg void OnSpeedExtrafast(); afx_msg void OnSpeedFast(); afx_msg void OnSpeedMedium(); afx_msg void OnSpeedSlow(); afx_msg void OnSpeedExtraslow(); afx_msg void OnUpdateSpeedExtrafast(CCmdUI* pCmdUI); afx_msg void OnUpdateSpeedFast(CCmdUI* pCmdUI); afx_msg void OnUpdateSpeedMedium(CCmdUI* pCmdUI); afx_msg void OnUpdateSpeedSlow(CCmdUI* pCmdUI); afx_msg void OnUpdateSpeedExtraslow(CCmdUI* pCmdUI); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: BOOL InitMorphSequence(const CString&); static void UpdateMorph(LPDIRECT3DRMFRAME frame, void*, D3DVALUE); static void UpdateDrag(LPDIRECT3DRMFRAME frame, void*, D3DVALUE); void OnIdle(LONG); private: LPDIRECT3DRMFRAME frame; LPDIRECT3DRMMESH mesh; int morphspeed; D3DVALUE morphtimeinc; D3DVALUE maxmorphtime; static D3DVALUE morphtime;

static BOOL drag; static BOOL end_drag; static int last_x, last_y; };

В классе объявлены две открытые функции: конструктор и функция CreateScene(). Конструктор выполняет инициализацию членов данных класса. Функция CreateScene() инициализирует источники света и порт просмотра приложения.

Для обработки команды Open меню File класс предоставляет функцию OnFileOpen(). Для представления диалогового окна выбора файлов используется функция класса MFC CFileDialog.

Для поддержки меню Morph предназначены три обработчика сообщений: OnMorphForward(), OnMorphReverse() и OnMorphBoth().
Следующие пять обработчиков предназначены для реализации команд меню Speed: OnSpeedExtrafast(), OnSpeedFast(), OnSpeedMedium(), OnSpeedSlow() и OnSpeedExtraslow(). Каждой из этих функций соответствует вспомогательная функция OnUpdate...(). Функции OnLButtonDown() и OnLButtonUp() используются для начала и прекращения операций перетаскивания объекта мышью (для вращения сетки).

Далее расположено объявление функции InitMorphSequence(). Она применяется для загрузки новой последовательности трансформаций. Функция обратного вызова UpdateMorph() контролирует скорость и направление воспроизведения последовательности трансформаций. Функция обратного вызова UpdateDrag() используется для реализации операций перетаскивания объекта мышью, когда сетка вращается в соответствии с перемещениями мыши. Функция OnIdle() останавливает вращение сетки на время перетаскивания.

Далее расположены объявления членов данных класса. Переменные frame и mesh являются соответственно указателем на фрейм трансформируемой сетки и указателем на трансформируемую сетку. Переменные morphspeed, morphtimeinc, maxmorphtime и morphtime применяются для управления последовательностью трансформаций. Переменные drag, end_drag, last_x и last_y используются при вращении фрейма сетки во время операции перетаскивания.


Класс MorphWin


Функциональность класса MorphWin предоставляется следующими функциями:

LoadMorphSequence()

GetNumMorphTargets()

GetMorphMesh()

AddMorphKey()

DeleteMorphKey()

SetMorphTime()

Функция LoadMorphSequence() получает имя MRF или X-файла и создает последовательность трансформаций, основываясь на содержимом файла. Функция GetNumMorphTargets() возвращает число шагов трансформации в текущей последовательности трансформаций. Функция GetMorphMesh() возвращает указатель на интерфейс Direct3DRMMesh, представляющий трансформируемую сетку.

Функции AddMorphKey(), DeleteMorphKey() и SetMorphTime() вдохновлены интерфейсом Direct3DRMAnimation. Ключ трансформации (morph key) — это то же самое, что и шаг трансформации. Функция AddMorphKey() позволяет указать какой шаг трансформации должен вступить в действие в заданный момент последовательности трансформаций. Функция SetMorphTime() позволяет указать момент последовательности трансформаций, который должен быть вычислен.



Объявление класса MorphWin выглядит следующим образом:

class MorphWin : public RMWin { public: MorphWin(); LPDIRECT3DRMMESH GetMorphMesh() { return morphmesh; } DWORD GetNumMorphTargets() { return nummorphtargets; } BOOL LoadMorphSequence(const CString& filename); BOOL AddMorphKey(DWORD target, D3DVALUE time); BOOL DeleteMorphKey(D3DVALUE time); BOOL SetMorphTime(D3DVALUE time); private: BOOL LoadMeshes(const CString& filename); BOOL CreateAnimations(); BOOL PrepareMorphVertices(); BOOL ReleaseAnimations(int count); protected: //{{AFX_MSG(MorphWin) afx_msg void OnDestroy(); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: LPDIRECT3DRMMESH morphmesh; D3DRMVERTEX* morphmeshdata[MAXMORPHTARGETS]; D3DRMVERTEX* morphvertex; DWORD nummorphvertices; DWORD nummorphtargets; LPDIRECT3DRMANIMATION* posanimation; LPDIRECT3DRMFRAME* posframe; LPDIRECT3DRMANIMATION* normanimation; LPDIRECT3DRMFRAME* normframe; BOOL morphing; };

Шесть функций, которые мы обсудили, и конструктор класса объявлены открытыми. Кроме того, в классе объявлены четыре вспомогательные закрытые функции.
Для освобождения ресурсов в классе объявлен обработчик сообщения OnDestroy().

Массив morphmeshdata используется для хранения данных вершин каждого входящего в последовательность шага трансформации. Массив morphvertex используется для хранения данных вершин трансформируемой сетки.

Переменная nummorphvertices хранит число вершин в шаге трансформации. Нам не требуются отдельные переменные для хранения числа вершин в каждом из шагов трансформации, поскольку во всех шагах должно быть одно и то же количество вершин. Переменная nummorphtargets используется для хранения числа шагов трансформации в последовательности.

Массивы posanimation и posframe используются для вычисления новых позиций вершин. Массив posanimation — это массив указателей на интерфейс Direct3DRMAnimation. Позиции вершин определяются путем генерации анимационной последовательности для каждой вершины трансформируемой сетки.

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

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

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


Класс MultiViewWin


Основная (но не вся) функциональность приложения MultiView предоставляется классом MultiViewWin:

class MultiViewWin : public RMWin { public: MultiViewWin(); BOOL CreateScene(); protected: //{{AFX_MSG(MultiViewWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); afx_msg void OnFileOpen(); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: LPDIRECT3DRMMESHBUILDER meshbuilder; LPDIRECT3DRMFRAME meshframe; };

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

Далее объявлены семь защищенных функций. Шесть из них являются обработчиками сообщений меню Render и присутствуют в большинстве других приложений. Седьмая функция является обработчиком сообщений для команды Open меню File. Мы будем использовать ее для отображения диалогового окна выбора файлов и загрузки выбранной сетки с диска.

Две переменных класса являются указателями, используемыми для доступа к единственной сетке приложения и фрейму, к которому эта сетка присоединена.



Класс OrbStarWin


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

class OrbStarWin : public RMWin { public: BOOL CreateScene(); static void MoveSphere(LPDIRECT3DRMFRAME frame, void* arg, D3DVALUE delta); static void MoveStar(LPDIRECT3DRMFRAME frame, void* arg, D3DVALUE delta); protected: //{{AFX_MSG(OrbStarWin) //}}AFX_MSG DECLARE_MESSAGE_MAP() };

В классе объявлены три функции: CreateScene(), MoveSphere() и MoveStar(). Функция CreateScene() конструирует для приложения сцену. MoveSphere() и MoveStar() это функции обратного вызова, которые будут использоваться для анимации двух сеток в сцене.



Класс RMWin


Степень модификации станет ясной, если вы сравните определение класса RMWin из главы 4 с полноэкранной версией класса RMWin, приведенной в листинге 10.1.

Листинг 10.1. Класс RMWin

class RMWin : public CFrameWnd { public: RMWin(); RMWin(int w, int h); BOOL Create(const CString& sTitle, int icon, int menu); void SetColorModel(D3DCOLORMODEL cm) { colormodel = cm; } virtual void Render() = 0; protected: int GetNumDisplayModes() {return totaldisplaymodes; } BOOL ActivateDisplayMode(int index); int GetCurDisplayMode() { return curdisplaymode; } BOOL GetDisplayModeDims(int index, DWORD& w, DWORD& h, DWORD& d ); BOOL GetCurDisplayModeDims(DWORD& w, DWORD& h, DWORD& d); static void CheckResult(HRESULT); static void CheckDirectDrawResult(HRESULT); virtual void OnIdle(LONG) { } static int GetMouseX() { return mousex; } static int GetMouseY() { return mousey; } D3DVALUE ScaleMesh(LPDIRECT3DRMMESHBUILDER, D3DVALUE); void UsePalette(CString filename) { palettefile = filename; } LPDIRECTDRAWSURFACE CreateSurface(DWORD w, DWORD h); BOOL ClearSurface(LPDIRECTDRAWSURFACE surf, DWORD clr); void SaveSurface(LPDIRECTDRAWSURFACE surf, int number); protected: //{{AFX_MSG(RMWin) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnDestroy(); afx_msg void OnMouseMove(UINT state, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: BOOL InitMainSurfaces(); BOOL InitDisplayMode(); BOOL ActivateDisplayMode(DWORD, DWORD, DWORD); void Initvars(); virtual BOOL CreateScene() = 0; BOOL CreateDevice(); GUID* GetGUID(); BOOL InstallPalette(); static HRESULT WINAPI DisplayModeAvailable(LPDDSURFACEDESC, LPVOID); static int CompareModes(const void *arg1, const void *arg2); protected: LPDIRECTDRAW ddraw; LPDIRECTDRAWSURFACE primsurf; LPDIRECTDRAWSURFACE backsurf; LPDIRECTDRAWSURFACE zbufsurf; LPDIRECTDRAWPALETTE palette; static LPDIRECT3DRM d3drm; static LPDIRECT3DRMFRAME scene; static LPDIRECT3DRMFRAME camera; static LPDIRECT3DRMDEVICE device; static LPDIRECT3DRMVIEWPORT viewport; private: static DWORD modewidth, modeheight, modedepth; D3DCOLORMODEL colormodel; CRect winrect; LPDIRECTDRAWCLIPPER clipper; static int mousex; static int mousey; static UINT mousestate; static int totaldisplaymodes; static videomode displaymode[MAXDISPLAYMODES]; static int curdisplaymode; CString palettefile; };

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



Класс RocketWin


Функциональность приложения Rocket сосредоточена в классе RocketWin:

class RocketWin : public RMWin { public: RocketWin(); BOOL CreateScene(); protected: //{{AFX_MSG(RocketWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); afx_msg void OnAnimationLinear(); afx_msg void OnAnimationSpline(); afx_msg void OnUpdateAnimationLinear(CCmdUI* pCmdUI); afx_msg void OnUpdateAnimationSpline(CCmdUI* pCmdUI); afx_msg void OnSpeedFast(); afx_msg void OnSpeedMedium(); afx_msg void OnSpeedSlow(); afx_msg void OnUpdateSpeedFast(CCmdUI* pCmdUI); afx_msg void OnUpdateSpeedMedium(CCmdUI* pCmdUI); afx_msg void OnUpdateSpeedSlow(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: static void UpdateScene(LPDIRECT3DRMFRAME, void*, D3DVALUE); static HRESULT LoadTexture(char*, void*, LPDIRECT3DRMTEXTURE*); private: LPDIRECT3DRMMESHBUILDER meshbuilder; LPDIRECT3DRMANIMATION animation; static D3DVALUE speed; };

Здесь объявлены две открытые функции: конструктор и CreateScene(). Конструктор присваивает переменным класса нулевые значения. Функция CreateScene() создает сцену приложения, включая и анимационную последовательность.

Далее объявлены шестнадцать защищенных функций, являющихся обработчиками событий. Первые шесть предназначены для реализации меню Render, следующие четыре— для меню Animation, а последние шесть функций реализуют меню Speed.

Объявлены и две защищенных функции: UpdateScene() и LoadTexture(). Обе они являются функциями обратного вызова. UpdateScene() применяется для обновления анимационной последовательности, а функция LoadTexture() используется для загрузки и присоединения текстуры к сетке ракеты.

Кроме того, объявлены три переменные:

LPDIRECT3DRMMESHBUILDER meshbuilder; LPDIRECT3DRMANIMATION animation; static D3DVALUE speed;

Указатель meshbuilder используется для загрузки и модификации сетки ракеты. Он применяется в функции CreateScene() и в функциях меню Render. Указатель animation используется для управления объектом Direct3DRMAnimation. Переменная speed применяется для контроля скорости с которой выполняется анимационная последовательность.



Класс ShadowWin


Основная функциональность приложения Shadow предоставляется классом ShadowWin:

class ShadowWin : public RMWin { public: ShadowWin(); BOOL CreateScene(); protected: //{{AFX_MSG(ShadowWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: static void AdjustSpin(LPDIRECT3DRMFRAME frame, void*, D3DVALUE); private: LPDIRECT3DRMMESHBUILDER floorbuilder; LPDIRECT3DRMMESHBUILDER forkbuilder; };

Класс предоставляет две открытых функции: конструктор и функцию CreateScene(). Кроме того, объявлены шесть функций для реализации меню Render, которые уже рассматривались нами при изучении других приложений.

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

Также объявлены две переменных класса. Обе они являются указателями на интерфейс Direct3DRMMeshBuilder. Эти указатели используются при создании сцены, а также в обработчиках событий меню Render.



Класс ShowRoomWin


Функциональность приложения ShowRoom сосредоточена в классе ShowRoomWin. Определение этого класса выглядит следующим образом:

class ShowRoomWin : public RMWin { public: BOOL CreateScene(); static void UpdateTexture(LPDIRECT3DRMFRAME frame, void*, D3DVALUE); protected: //{{AFX_MSG(ShowRoomWin) //}}AFX_MSG DECLARE_MESSAGE_MAP() private: static LPDIRECT3DRMMESH mesh; static LPDIRECT3DRMTEXTURE texture[15]; };

В классе объявлены две открытые функции: CreateScene() и UpdateTexture(). Функция CreateScene() используется для создания сцены, а UpdateTexture() — это функция обратного вызова, применяемая для изменения накладываемой в данный момент на сетку текстуры.

Также объявлены две закрытых переменных: mesh и texture. Переменная mesh это указатель на интерфейс Direct3DRMMesh , используемый для доступа к сетке, на которую накладывается анимируемая текстура. Переменная texture является массивом текстур, которые будут по очереди накладываться на сетку.



Класс SpaceDonutWin


Основная функциональность приложения SpaceDonut сосредоточена в классе SpaceDonutWin, объявление которого выглядит следующим образом:

class SpaceDonutWin : public RMWin { public: SpaceDonutWin(); BOOL CreateScene(); protected: //{{AFX_MSG(SpaceDonutWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: LPDIRECT3DRMMESHBUILDER meshbuilder; };

Подобно всем демонстрационным приложениям на CD-ROM, программа SpaceDonut наследует свой класс окна от класса RMWin. Класс SpaceDonutWin предоставляет две открытых функции: конструктор и функцию CreateScene(). Конструктор отвечает за инициализацию любых членов данных, определенных в классе (в нашем случае, единственной переменной). Функция CreateScene() создает сцену приложения.

Шесть защищенных функций реализуют поддержку приложением меню Render. Для изменения параметров сетки эти функции используют переменную meshbuilder.



Класс SpaceStationWin


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

class SpaceStationWin : public RMWin { public: SpaceStationWin(); BOOL CreateScene(); protected: //{{AFX_MSG(SpaceStationWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: LPDIRECT3DRMMESHBUILDER meshbuilder; };

В классе SpaceStationWin объявлены две открытые функции: конструктор и функция CreateScene(). Конструктор обнуляет переменную класса meshbuilder:

SpaceStationWin::SpaceStationWin() { meshbuilder=0; }

Шесть защищенных функций предоставляют поддержку меню Render приложения SpaceStation. Закрытый указатель meshbuilder используется защищенными функциями для изменения параметров сетки.



Класс SpotlightWin


Приложение Spotlight использует класс RMWin в качестве базового для своего собственного класса SpotlightWin:

class SpotlightWin : public RMWin { public: SpotlightWin(); BOOL CreateScene(); protected: //{{AFX_MSG(SpotlightWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); afx_msg void OnBeamNormal(); afx_msg void OnBeamNarrow(); afx_msg void OnBeamWide(); afx_msg void OnUpdateBeamNormal(CCmdUI* pCmdUI); afx_msg void OnUpdateBeamNarrow(CCmdUI* pCmdUI); afx_msg void OnUpdateBeamWide(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: static void MoveLight(LPDIRECT3DRMFRAME frame, void* arg, D3DVALUE delta); private: LPDIRECT3DRMMESH mesh1, mesh2, mesh3; LPDIRECT3DRMLIGHT spotlight; int beamwidth; };

Определение класса SpotlightWin слегка отличается от определений классов других демонстрационных программ, рассмотренных в этой главе. Первым и основным отличием является большее число защищенных функций. Шесть защищенных функций подобны тем, которые используются в других демонстрационных программах, и предназначены для реализации меню Render. Новые функции, которые мы рассмотрим чуть позже, предназначены для реализации меню Beam.

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

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

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



Класс TargetWin


Функциональность приложения Target реализована в классе TargetWin:

class TargetWin : public RMWin { public: TargetWin(); BOOL CreateScene(); protected: //{{AFX_MSG(TargetWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: static void OrientFrame(LPDIRECT3DRMFRAME frame, void*, D3DVALUE); static void MoveTarget(LPDIRECT3DRMFRAME frame, void*, D3DVALUE); private: LPDIRECT3DRMMESHBUILDER meshbuilder; };

В классе объявлены две открытые функции: конструктор и функция CreateScene(). Конструктор инициализирует единственную переменную класса, а функция CreateScene() создает сцену для приложения.

Следом идет объявление шести защищенных функций. Все они предназначены для реализации меню Render.

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

И в самом конце объявляется единственная переменная класса. Указатель meshbuilder используется для загрузки сетки, изображающей ракету. Эта переменная используется как в функции CreateScene() так и в функциях меню Render.



Класс TextureDriftWin


Все возможности приложения TextureDrift реализованы в классе TextureDriftWin. Определение класса выглядит следующим образом:

class TextureDriftWin : public RMWin { public: BOOL CreateScene(); static void MoveTexture(LPDIRECT3DRMFRAME frame, void* arg, D3DVALUE delta); protected: //{{AFX_MSG(TextureDriftWin) //}}AFX_MSG DECLARE_MESSAGE_MAP() };

В классе объявлены две функции: CreateScene() и MoveTexture(). Функция CreateScene() создает сцену для приложения, а функция MoveTexture() является функцией обратного вызова, используемой для выполнения анимации текстуры.

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



Класс WrapsWin


Большая часть функциональных возможностей приложения Wraps реализуется классом WrapsWin. Давайте взглянем на определение этого класса:

class WrapsWin : public RMWin { public: WrapsWin(); BOOL CreateScene(); protected: //{{AFX_MSG(WrapsWin) afx_msg void OnWrapsFlat(); afx_msg void OnWrapsCylinder(); afx_msg void OnWrapsSphere(); afx_msg void OnWrapsReset(); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: BOOL LoadMeshes(); BOOL LoadTexture(); void ApplyWraps(); void ApplyFlat(LPDIRECT3DRMMESHBUILDER); void ApplyCylinder(LPDIRECT3DRMMESHBUILDER); void ApplySphere(LPDIRECT3DRMMESHBUILDER); private: LPDIRECT3DRMMESHBUILDER box; LPDIRECT3DRMMESHBUILDER cyl; LPDIRECT3DRMMESHBUILDER sphere; D3DRMWRAPTYPE boxwraptype; D3DRMWRAPTYPE cylwraptype; D3DRMWRAPTYPE spherewraptype; };

Также как и класс JadeWin, класс WrapsWin предоставляет две открытые функции: конструктор и функцию CreateScene(). Конструктор используется для инициализации переменных класса. Функция CreateScene() создает сцену приложения. Чуть позже функция CreateScene() будет разобрана более подробно.

В приложении Wraps отсутствует меню Render, но есть меню Wraps. Меню Wraps позволяет пользователю выбрать используемый метод наложения текстуры. Для поддержки функциональных возможностей меню Wraps в классе WrapsWin объявлены четыре защищенные функции: OnWrapsFlat(), OnWrapsCylinder(), OnWrapsSphere() и OnWrapsReset().

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

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



Класс ZoomWin


Функциональные возможности приложения Zoom сосредоточены в классе ZoomWin:

class ZoomWin : public RMWin { public: ZoomWin(); BOOL CreateScene(); protected: //{{AFX_MSG(ZoomWin) afx_msg void OnRenderWireframe(); afx_msg void OnRenderFlat(); afx_msg void OnRenderGouraud(); afx_msg void OnUpdateRenderFlat(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderGouraud(CCmdUI* pCmdUI); afx_msg void OnUpdateRenderWireframe(CCmdUI* pCmdUI); afx_msg void OnAnimationLinear(); afx_msg void OnAnimationSpline(); afx_msg void OnUpdateAnimationLinear(CCmdUI* pCmdUI); afx_msg void OnUpdateAnimationSpline(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: static void AdjustField(LPDIRECT3DRMFRAME frame, void*, D3DVALUE); private: LPDIRECT3DRMMESHBUILDER meshbuilder; static LPDIRECT3DRMFRAME zoomframe; static LPDIRECT3DRMANIMATION animation; };

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

ZoomWin::ZoomWin() { meshbuilder = 0; }

Функция CreateScene() создает сцену приложения. Мы рассмотрим ее код чуть позже.

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

Потом расположено объявление функции обратного вызова AdjustField(). Эта функция используется для изменения параметров поля зрения порта просмотра во время работы программы.

В самом конце объявлены три закрытых члена данных:

LPDIRECT3DRMMESHBUILDER meshbuilder; static LPDIRECT3DRMFRAME zoomframe; static LPDIRECT3DRMANIMATION animation;

Указатель meshbuilder используется для доступа к сетке демонстрационной программы. Он применяется в функции CreateScene() и в шести защищенных обработчиках сообщений меню Render. Переменная zoomframe является указателем на пустой фрейм, а переменная animation— это указатель на интерфейс Direct3DRMAnimation. Мы будем использовать интерфейс анимации и пустой фрейм при «анимации» поля зрения порта просмотра. О том, как это работает, мы поговорим во время обсуждения функции AdjustField().



Ключевые кадры


Термин ключевые кадры (key-framing) пришел из традиционной техники анимации, где определяются только несколько «ключевых» кадров. Оставшиеся кадры создаются как промежуточные позиции между ключевыми.

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

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

Определить количество кадров в анимации (пусть для нашего примера их будет 30).

Указать, что в первом кадре объект располагается в верхнем левом углу экрана.

Указать, что в 15-м кадре объект должен находиться в верхнем правом углу экрана.

Указать, что в 30-м кадре объект должен быть расположен в нижнем правом углу экрана.

В Direct3D имеются два режима для метода ключевых кадров: линейный (linear) и сплайновый (spline-based). В линейном режиме движение между ключевыми кадрами осуществляется линейно, объект перемещается по кратчайшему пути. При сплайновой анимации для перемещения между ключевыми кадрами используются изогнутые траектории.

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

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



Код приложений


Исходный код всех рассматриваемых в книге демонстрационных программ находится в каталоге SRC. Код каждого из приложений находится в собственном отдельном подкаталоге. Эти подкаталоги показаны на рис.A.2.


Рис. A.2. Содержимое каталога SRC

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



Код приложения MultiView


Приложение MultiView написано слегка иначе, чем остальные демонстрационные программы на CD-ROM. Все примеры для своего класса приложения в качестве базового класса используют класс RMWin. Большинство демонстрационных программ использует одну и ту же версию RMWin, но в приложении MultiView применяется модифицированная версия.

Версия класса RMWin, которая используется в приложении MultiView включает поддержку трех портов просмотра. Это потребовало внесения изменений в ряд функций класса RMWin. Таким образом, при обсуждении кода приложения MultiView мы будем рассматривать как функции класса RMWin так и функции класса MultiViewWin.



Конструирование сцен


К этому моменту мы инициализировали стандартные интерфейсы Direct3D. Все обсужденные ранее этапы выполнялись классами RMApp и RMWin. Теперь настало время создать сцену.



MAK


Файлы MAK — это текстовые файлы, которые могут быть использованы для компиляции приложений. Эти файлы автоматически создаются Visual C++ и обычно игнорируются. Тем не менее, они могут быть полезны, если файлы MDP по каким-либо причинам недоступны. Файлы MAK позволяют скомпилировать проект либо с помощью компилятора с интерфейсом командной строки (cl), либо в Visual C++. Также файлы MAK могут быть использованы для создания файлов MDP. Выберите пункт Open Workspace в меню File и в диалоговом окне выбора файла укажите файл MAK.



Масштабирование


Выполнение операции масштабирования над сеткой или гранью изменяет ее размер и местоположение. Сначала давайте посмотрим, как с помощью операции масштабирования можно изменить размеры объекта. Если мы возьмем наш куб, размерами 1x1x1 и произведем операцию масштабирования с коэффициентом 1/2, длина каждой грани нашего куба станет равна 1/2 (получим куб размером 1/2x1/2x1/2). Если же мы используем коэффициент масштабирования, равный 2, то мы удвоим размеры нашего куба, получив куб с размером каждой грани 2единицы (то есть куб размерами 2x2x2). На рис. 2.13. показаны результаты рассмотренных операций масштабирования.


Рис. 2.13. Результаты масштабирования куба размерами 1x1x1 с коэффициентами 1/2 и 2

Как упоминалось ранее, операция масштабирования также изменяет местоположение объекта. На рис. 2.13 центр куба совпадает с началом координат. Если бы этого не было, то операция масштабирования повлияла и на местоположение куба и на его размер. Операция масштабирования не увеличивает непосредственно объект: она проводится над вершинами объекта. Когда коэффициент масштабирования больше 1, вершины удаляются от начала координат. Когда коэффициент масштабирования меньше 1, вершины приближаются к началу координат. На рис. 2.14 показан результат операции масштабирования с теми же коэффициентами, что и на рис. 2.13, но исходный куб был сдвинут вправо по оси Y и куб, полученный в результате операции масштабирования, располагается дальше от начала координат.


Рис. 2.14. Перемещение и изменение размеров куба в результате операции масштабирования

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

Мы можем задавать различные коэффициенты масштабирования для каждой из осей. Это позволяет растягивать и сжимать объекты. Коэффициент масштабирования, равный 1, не оказывает влияния на вершины объекта, таким образом, коэффициент 1 может использоваться для оси, вдоль которой размер объекта не должен изменяться. Если мы выполним с нашим исходным кубом операцию масштабирования с коэффициентами <2, 1, 1>, то мы в два раза увеличим ширину куба, не меняя остальных его размеров. Результат этой операции можно увидеть на рис. 2.15.



Рис. 2.15. Результат операции масштабирования с коэффициентами <2, 1, 1>


Масштабирование текстуры


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


Рис. 2.18. Пример масштабирования текстуры



Мастер Direct3D AppWizard


В главе1 вы познакомились с мастером Direct3D AppWizard. Напомню, Direct3D AppWizard — это нестандартный инструмент Visual C++, позволяющий создавать завершенные приложения Direct3D. Мастер AppWizard предоставляет ряд диалоговых окон, применяемых для задания параметров нового приложения. Можно выбрать объекты, которые будут отображаться приложением, используемые источники освещения, и даже имена классов.

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



Мастер создания классов ClassWizard


Карты сообщений представляют собой мощный инструмент, значительно облегчающий обработку событий, но Visual C++ делает эту задачу еще более легкой, предоставляя для использования мастер создания классов ClassWizard. ClassWizard — это инструмент, входящий в состав Visual C++ Developer Studio, который предоставляет возможность добавлять, удалять и редактировать функции обработки событий. На рис. 1.2 показан внешний вид диалогового окна мастера ClassWizard.


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

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

Код, который ClassWizard добавляет в ваш проект немного отличается от рассмотренного ранее кода для карты сообщений. Например, если вы создадите предыдущий фрагмент программы с помощью ClassWizard, он будет выглядеть следующим образом:

BEGIN_MESSAGE_MAP(OurClass, BaseClass) //{{AFX_MSG_MAP(OurClass) ON_WM_PAINT() ON_WM_SIZE() ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP()

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

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

Далее в этой главе мы рассмотрим пример использования мастера ClassWizard.



Мастер создания приложений AppWizard


В то время, как мастер ClassWizard делает легкой модификацию проектов, мастер AppWizard позволяет легко создавать новые проекты. Visual C++ содержит, например, мастер позволяющий создать новый проект с использованием библиотеки MFC. Если вы запустите этот мастер, вам будет предложен ряд диалоговых окон с вопросами о том, какое MFC приложение вы хотели бы создать. Visual C++ содержит несколько различных мастеров для создания приложений: один для создания приложения с использованием библиотеки MFC, другой для создания консольного приложения, третий для создания библиотек динамической компоновки (DLL) и т.д.

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



Мастер создания приложений Direct3D AppWizard


Visual C++ позволяет вам писать свои собственные мастера для создания приложений. Пользовательский мастер AppWizard может быть написан для генерации базового кода практически любого типа приложений. CD-ROM, прилагающийся к этой книге содержит пользовательский мастер Direct3D AppWizard, позволяющий создать полностью функционирующее приложение, использующее Direct3D.

Пользовательский мастер представляет собой специальную динамическую библиотеку (DLL) с расширением AWX. Для установки мастера необходимо скопировать файл с расширением AWX в папку Visual C++ с шаблонами (обычно это c:\msdev\template). Для установки мастера Direct3D AppWizard вам необходимо либо скопировать файл Direct3DappWiz.AWX из папки Direct3DappWiz\Release, расположенной на CD-ROM, либо воспользоваться программой инсталяции на CD-ROM.

После установки мастера, запустите Visual C++ Developer Studio, выберите пункт New из меню File, отметьте пункт Project Workspace и щелкните по кнопке OK. В окне New Project Workspace, в списке Type отметьте строку Direct3D AppWizard, как показано на рис. 1.10. Также необходимо ввести имя проекта. Для нашего примера мы используем имя Sample3D. После ввода имени щелкните по кнопке Create. Теперь вам будет показано первое диалоговое окно мастера Direct3D AppWizard, изображенное на рис. 1.11.


Рис. 1.10. Выбор мастера Direct3D AppWizard


Рис. 1.11. Первое диалоговое окно мастера Direct3D AppWizard

Первое диалоговое окно показывает краткое описание используемого мастера. Щелкните по кнопке Next для перехода ко второму диалоговому окну, изображенному на рис. 1.12.


Рис. 1.12. Диалоговое окно Object selection для выбора объекта

Во втором диалоговом окне мастера вам необходимо выбрать трехмерный объект, который будет отображаться создаваемым вами Direct3D-приложением. По умолчанию выбран переключатель Swirl object (завиток), но вы можете выбрать любой другой объект, отметив переключатель Let me choose an object и указав имя файла с отображаемым объектом в поле Object.
Вы можете также воспользоваться кнопкой Browse для поиска объектов (файлы с объектами Direct3D имеют расширение X). Для примера мы используем предлагаемый по умолчанию объект Swirl. Щелкнем по кнопке Next для перехода к следующему диалоговому окну, изображенному на рис. 1.13.



Рис. 1.13. Диалоговое окно Lighting selection для выбора источников света

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

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



Рис. 1.14. Диалоговое окно с именами классов

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

Теперь мастер покажет нам заключительное диалоговое окно (рис. 1.15) где мы можем увидеть выбранные параметры.



Рис. 1.15. Заключительное диалоговое окно мастера Direct3D AppWizard

Новый проект будет создан после щелчка по кнопке OK. Скомпилируем проект, нажав клавишу F7, и выполним новое приложение, нажав клавишу F5. На рис. 1.16 показано окно созданного нами нового приложения.



Рис. 1.16. Новое приложение с использованием Direct3D


Материалы грани


Внешний вид грани (блестящая, матовая ит.д.) может быть изменен с помощью функций GetMaterial() и SetMaterial(). Мы поговорим о материалах подробнее в разделе, посвященном интерфейсу Direct3DRMMaterial.



Метод Фонга


Закраска по методу Фонга (Phong shading) является усовершенствованием метода Гуро. Подобно методу Гуро, в методе Фонга используются нормали вершин, однако, вместо интерполяции вычисленных для вершин значений, производится вычисление нормалей для каждой точки грани. Эта дополнительная работа обеспечивает более точный результат. Как вы можете догадаться, закраска по методу Фонга медленнее, чем закраска по методу Гуро. Иллюстрация для этого метода не приводится, поскольку на момент написания книги Direct3D не поддерживал закраску по методу Фонга.



Метод Гуро


Закраска методом Гуро (Gouraud shading) подобна равномерной закраске, за исключением того, что нормали рассчитываются для каждой вершины вместо того, чтобы рассчитывать их для каждой грани. Грань закрашивается с использованием интерполяции значений освещенности в вершинах. Это приводит к сглаживанию изображения сеток, и отдельные грани становятся неразличимыми. Закраска методом Гуро позволяет получить реалистичные изображения, но может сделать объекты неотчетливыми и смутными. Поскольку используются нормали к вершинам и усредненные значения для грани, метод Гуро требует большего объема вычислений, чем равномерная закраска. Закраска по методу Гуро использовалась для рис. 2.25.


Рис. 2.25. Закраска по методу Гуро



Методы наложения текстуры


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



Методы визуализации


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



Методы визуализации и цветовые модели


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

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

Цветовая модель (RGB или Ramp) не зависят от метода визуализации. Цветовая модель RGB поддерживает цветные источники света, а модель Ramp — нет. Модель Ramp обычно дает выигрыш в быстродействии по сравнению с моделью RGB.

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

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

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

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

Многие демонстрационные программы на CD-ROM предоставляют команды меню, позволяющие изменять метод визуализации во время работы приложения. Это позволяет легко увидеть различия между методами.

Большинство демонстрационных программ на CD-ROM для увеличения быстродействия используют цветовую модель Ramp. Тем не менее, в некоторых приложениях из этой главы применяется цветовая модель RGB, чтобы продемонстрировать работу с цветными источниками света.



Модификация демонстрационных программ


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

В главе 4 объяснялось, что большинство демонстрационных программ используют одни и те же версии классов С++ RMWin и RMApp. Это верно, но в каждом приложении используется собственная копия файлов с исходным кодом этих классов. Фактически в 21 из 23 демонстрационных программ используются идентичные копии файлов RMWin.cpp, RMWin.h, RMApp.cpp и RMApp.h. Исключение составляют приложения FullScreen и MultiView.



Модификация класса RMWin


В главе4 мы обсуждали стратегию построения структуры классов, используемой для написания рассматриваемых в этой книге приложений. Наша стратегия состояла в создании двух унаследованных от MFC классов, которые предоставляли поддержку Direct3D. Мы назвали эти классы RMWin и RMApp. В каждом демонстрационном приложении были еще два класса: один производный от RMWin, а другой производный от RMApp. Эти специфичные для конкретного приложения классы наращивали и модифицировали функциональность базовых классов. На рис. 10.1 показано дерево наследования классов, полученное нами в результате этой работы.


Рис. 10.1. Используемая в книге иерархия классов

При переходе к полноэкранным приложениям мы сохраним показанную на рисунке архитектуру. Таким образом, любые новые возможности, которые мы добавим в классы RMWin и RMApp, будут автоматически унаследованы производными классами. Такой подход упрощает написание последующих приложений.

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



Мощность отраженного света


Отраженный свет (specular light)— это свет, который отражается от объекта и создает блики (солнечные зайчики). Параметры бликов влияют на вид объекта. Небольшие, яркие блики заставляют объект казаться блестящим. Большие блики придают объектам вид сделанных из пластика. Маленькие блики или их отсутствие приводят к тому, что объект кажется матовым.

Поведение бликов зависит от мощности отраженного света. Меньшие значения создают большие блики, а большие значения вызывают небольшие блики. Для контроля данного параметра интерфейс материала предоставляет функции GetPower() и SetPower().



Начало системы координат


Точка, в которой пересекаются все три координатных оси, называется началом системы координат (origin). Для этой точки координаты X, Y и Z равны нулю: <0, 0, 0>. Чем больше координата точки отличается от нуля, тем дальше точка от начала координат. У точек расположенных справа от начала координат значение X положительное, у точек, расположенных слева — отрицательное. Подобным образом, точки расположенные выше начала координат характеризуются положительным значением Y, а точки расположенные ниже — отрицательным. На рис. 2.2 показаны несколько точек с их координатами.


Рис. 2.2. Точки с соответствующими координатами

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


Рис. 2.3. Точки вдоль оси Z



Надвигающаяся трехмерная революция


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

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



Наглядный пример наложения текстур


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

Одним из способов поэкспериментировать с наложением текстур является использование программы Xpose, утилиты для просмотра X-файлов, расположенной на CD-ROM. Вы можете применять Xpose для вращения сетки, изменения ее параметров, и сохранения сетки после всех преобразований. Окно программы Xpose показано на рис. 5.1.


Рис. 5.1. Программа Xpose

Xpose позволяет наложить текстуру на сетку. Для наложения новой текстуры выберите команду Load из меню Texture. На экран будет выведено диалоговое окно Open File. В этом окне вы можете выбрать файл BMP или PPM, но размеры текстуры должны быть степенью двойки (16, 32, 64, 128...).

По умолчанию Xpose использует сферическое наложение текстур, но этот параметр можно изменить в диалоговом окне Texture Wrap Settings. Для доступа к этому диалоговому окну выберите команду Wrap Settings в меню Texture. Вид диалогового окна Texture Wrap Settings представлен на рис. 5.2.


Рис. 5.2. Диалоговое окно Texture Wrap Settings

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

Приложение Xpose весьма полезно и для экспериментов с другими возможностями Direct3D.



Наложение текстур


Наложению текстур стали уделять большое внимание после выхода программы DOOM, выпущенной ID Software. Конечно, DOOM не первая программа, в которой применялось наложение текстур, но эта программа стала наиболее популярной.

Наложение текстур (texture mapping) определяет способ, которым при получении изображения текстура соединяется с гранью или набором граней. Наложение зависит от положения объекта в пространстве. Мы не можем просто налепить текстуру на поверхность, не учитывая расстояние до объекта и его ориентацию относительно зрителя. Такая сцена будет плохо выглядеть. Дальше в этой главе мы поговорим подробнее о текстурах и перспективе. А пока запомните, что способ наложения текстур на удаленные объекты, должен отличаться от способа наложения текстур на объекты, расположенные ближе к зрителю.



Наложение текстуры на сетку


Первое, что нам следует сделать— наложить текстуру на объект. Наша стратегия состоит в следующем: возьмем демонстрационную программу, аналогичную проекту, который создает по умолчанию мастер Direct3D AppWizard, и наложим текстуру на объект, отображаемый демонстрационным приложением.



Направленный свет


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

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



Неосвещенный метод


Неосвещенный метод визуализации (unlit rendering) получил свое название из-за того, что при его применении наличие источников света игнорируется. Грани рисуются с использованием назначенных им цветов и текстур без учета значений освещенности или ориентации грани. Сцены с применением неосвещенного метода создаются быстро, но объекты в этих сценах оказываются похожими на силуэты. Рис. 2.23 показывает результат использования данного метода визуализации.


Рис. 2.23. Неосвещенный метод визуализации



Перед завершением работы над книгой


Перед завершением работы над книгой я выложил мастер Direct3D AppWizard на несколько форумов CompuServe. Я получил множество положительных откликов, но некоторые пользователи Visual C++ 4.2 не смогли откомпилировать создаваемые мастером проекты.

Эта проблема возникает потому, что частью Visual C++ 4.2 является DirectX версии 1. Компилятор находит файл ddraw.h из DirectX 1. Но в DirectX 1 отсутствует компонент Direct3D, поэтому компиляция приложения завершается неудачей.

Простейшее решение этой проблемы состоит в следующем: в меню Tools выберите пункт Options и перейдите на вкладку Directories. Это диалоговое окно позволяет вам поместить каталог, где расположена новая версия DirectX перед стандартными каталогами. В результате компилятор будет находить правильную версию ddraw.h.


Нормали


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

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


Рис. 2.9. Нормали к граням куба

Нормали к вершине — это векторы, которые присваиваются каждой вершине в сетке. Ориентация каждого вектора зависит от ориентации и размера соединяющихся в вершине граней. Рис. 2.10 показывает нормали к вершинам куба.


Рис. 2.10. Нормали к вершинам куба

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

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



Обработчики событий


Обработчик событий — это функция, которую вы пишете сами и регистрируете в системе вместе с окном приложения. После регистрации вашего обработчика событий каждое сообщение переданное функцией DispatchMessage попадет к вашему обработчику. Типичный обработчик событий содержит инструкцию switch с инструкциями case для каждого события, которое необходимо обработать. События, которые не нуждаются в специальной обработке, обычно передаются внутреннему обработчику событий системы Windows. Простейший обработчик событий выглядит следующим образом:

long WINAPI WndProc(HWND hWnd, UINT msg, UINT wParam, LONG lParam) { switch (msg) { case WM_KEYDOWN: // Здесь располагается код для обработки нажатия клавиши break; case WM_MOUSEMOVE: // Здесь располагается код для обработки перемещения мыши break; default: // Если событие не нуждается в обработке, передадим // его системе Windows return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; }

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

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



Обучайтесь экспериментируя


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

Поэкспериментируйте с кодом примера Sample. Поэкспериментируйте с другими демонстрационными программами на CD-ROM. Попытайтесь добавить к сцене еще несколько сеток. Поэкспериментируйте с цветными источниками света (не забывайте об использовании цветовой модели RGB).

Когда вы закончите эксперименты, настанет время для чтения главы 5, в которой вы узнаете о текстурах и их наложении.



Организация книги


Книга состоит из десяти глав. Первая глава представляет собой краткий обзор Visual C++ и описание мастера создания приложений Direct3D AppWizard. В главе2 описываются базовые концепции трехмерной графики и применяемые в книге термины. Третья глава — это введение в интерфейс программирования приложений (API) Direct3D. Глава 4 описывает структуру приложения, использующего Direct3D. С четвертой по десятую главы последовательно разбираются возможности Direct3D и приводятся 23 демонстрационных программы. В большинстве случаев каждая тема сопровождается отдельной демонстрационной программой. В приложении А приведено краткое описание демонстрационных программ и содержимого CD-ROM, прилагаемого к книге.



Освещение


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



От трех измерений к двум


Переход от трех измерений к двум требует, разделения трехмерного пространства на части, таким образом, чтобы двухмерное изображение могло быть легко получено. Это значит, что нам необходимо определить, где будет располагаться зритель, или камера, и какую часть сцены он должен видеть. Все эти параметры определяют область видимого пространства (viewing frustum). Мы будем представлять область видимого пространства как пирамиду. Зритель располагается в вершине пирамиды и смотрит в центр ее основания. Если увеличить основание пирамиды, будет видна большая часть сцены, но сами объекты станут более мелкими. Если размер основания уменьшить, объекты будут казаться более крупными, но при этом зритель увидит меньшую часть сцены. Размер основания пирамиды определяется углом зрения или параметрами поля зрения (FOV).

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


Рис. 2.21. Область видимого пространства и полученное изображение

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



Отсечение


Порт просмотра управляет передней и задней плоскостями отсечения. Для определения области перед камерой, в которой объекты будут видимы, можно использовать функции SetFront() и SetBack(). Объекты, расположенные за этой областью, не будут визуализироваться. Заданные по умолчанию значения для передней и задней плоскостей отсечения равны 1 и 100 соответственно.