netlib.narod.ru | < Назад | Оглавление | Далее > |
После небольшого экскурса в геометрию давайте займемся написанием программы, создающей несколько простых фигур. Данное приложение называется Shapes и находится в одноименном каталоге. Возможно, во время чтения стоит запустить приложение и посмотреть, как оно работает. На первый взгляд приложение Shapes ничем не отличается от Stage из главы 2, однако стоит щелкнуть по меню, как вы сразу увидите новые команды. Мы рассмотрим их назначение и реализацию. Весь интересующий нас код находится в файле MainFrm.cpp.
Начнем с исключительно простой фигуры — прямоугольника, который имеет четыре вершины, определяющие одну грань. Запустив приложение Shapes, вы найдете в меню Edit несколько команд с названиями фигур. Выберем команду Flat Face (то есть «плоская грань»). Вот как выглядит ее программная реализация:
void CMainFrame::OnEditInsface() { // Вставить простую плоскую грань // и задать список вершин D3DVECTOR vlist [] = { {-2, -2, -2}, {-2, -2, 2}, { 2, -2, 2}, { 2, -2, -2} }; // Получить количество вершин в массиве int nv = sizeof(vlist) / sizeof(D3DVECTOR); // Определить вершины, входящие в каждую грань, // в формате: // количество, вершина1, вершина2 и т. д. int flist [] = {4, 0, 1, 2, 3, // 4 вершины, ... 0 // Конец списка данных грани }; // Создать для фигуры новый макет NewScene(); // Создать фигуру по списку вершин // и списку данных грани m_pShape = new C3dShape; m_pShape->Create(vlist, nv, flist); // Присоединить фигуру к макету m_pScene->AddChild(m_pShape); }
Вершины грани содержатся в массиве векторов vlist. Каждый вектор определяет одну из четырех вершин, лежащих в плоскости y = –2. Прямоугольник имеет размеры 4 x 4 единицы. Первый вектор определяет ближнюю левую вершину, второй — дальнюю левую, третий — дальнюю правую и четвертый — ближнюю правую. Другими словами, мы обходим вершины по часовой стрелке.
Для определения фигуры используется целочисленный массив с информацией о грани, flist. Каждая грань в списке описывается количеством входящих в нее вершин, за которыми следует индекс каждой вершины в массиве vlist. Список граней завершается нулем. Он может содержать информацию о нескольких гранях, но в нашем случае определяется всего одна. Обратите внимание на то, что вершины пронумерованы в порядке 0, 1, 2, 3, что соответствует их обходу по часовой стрелке, начиная с ближней левой, если смотреть на грань сверху. Поскольку грань находится в плоскости y = –2, она расположена ниже камеры (точки 0, 0, –10) и, следовательно, попадает в кадр.
Чтобы создать фигуру, следует вызвать функцию Create объекта C3dShape и передать ей в качестве аргументов список вершин, их общее количество и список данных грани. Не обращайте внимания на то, что происходит внутри объекта C3dShape, там нет ничего интересного — векторы и данные грани передаются в функцию механизма визуализации, которая и создает фигуру. Затем объект C3dShape присоединяется к макету, чтобы появиться в окне приложения. Если запустить приложение Shapes и выполнить команду Edit | Flat Face, вы увидите что-нибудь похожее на рис. 4.12.
Рис. 4.12. Приложение Shapes с одной плоской гранью
Обратите внимание на то, что грань выглядит плоской — это означает, что нормали ко всем вершинам имеют одинаковое направление. Вы можете проверить это командой View | Normals, которая рисует возле каждой вершины небольшую стрелку, показывающую направление нормали. На рис. 4.13 изображена грань вместе с нормалями.
Рис. 4.13. Плоская грань с нормалями к вершинам
Теперь давайте с теми же самыми данными создадим новую грань, но на этот раз укажем набор нормалей, направленных к центру грани. Ниже приведена функция для создания вогнутой грани, наподобие изображенной на рис. 4.11:
void CMainFrame::OnEditDishface() { // Вогнутая грань D3DVECTOR vlist [] = { {-2, -2, -2}, {-2, -2, 2}, { 2, -2, 2}, { 2, -2, -2} }; int nv = sizeof(vlist) / sizeof(D3DVECTOR); D3DVECTOR nlist [] = { { 1, 1, 1}, { 1, 1, -1}, {-1, 1, -1}, {-1, 1, 1} }; int nn = sizeof(nlist) / sizeof(D3DVECTOR); int flist [] = {4, 0, 0, 1, 1, 2, 2, 3, 3, 0 }; NewScene(); m_pShape = new C3dShape; m_pShape->Create(vlist, nv, nlist, nn, flist); m_pScene->AddChild(m_pShape); }
Как видите, мы пользуемся теми же самыми данными вершин, за исключением того, что к ним добавился еще один массив векторов, nlist. В этом массиве содержатся данные нормалей. Следует отметить, что векторы нормалей не являются единичными — их модули были взяты по моему усмотрению. Механизм визуализации нормирует эти векторы перед использованием.
Список данных грани (flist) изменился и теперь содержит количество вершин, за которым следуют пары индексов для вершин и нормалей. В этом случае используется другая версия функции Create объекта C3dShape, которая получает массивы вершин и нормалей, а также данные граней.
Результат выполнения этой функции изображен на рис. 4.14. Вы можете увидеть его на экране, для этого следует запустить приложение Shapes и выполнить команду Edit | Dished Face.
Рис. 4.14. Грань с неперпендикулярными нормалями
Вы обратили внимание на то, что грань кажется вогнутой в середине? (Этот эффект особенно четко проявляется, если привести грань во вращение.) На случай, если вы забыли, напомню, что источник света находится в левом верхнем углу макета. С помощью приложения Shapes можно также убедиться в том, что нормали, направленные за пределы грани, создают иллюзию выпуклости. Для того чтобы увидеть этот эффект, достаточно выполнить команду Edit | Bulging Face.
netlib.narod.ru | < Назад | Оглавление | Далее > |