WPF 3D API позволяет создавать реалистичные трехмерные сцены со сложностью незначительно превышающей построение 2D интерфейса. Трехмерную графику можно легко встроить в любой двухмерный элемент приложения, будь то контейнер или кнопка. Из 3D объектов WPF можно создавать интерфейсы приложений с красивыми объемными эффектами и игры не требующие высокой производительности в реальном времени.
В настоящей статье описывается исходник класса Cube3D для создания трехмерных кубиков. Один объект класса Cube3D это один трехмерный кубик. Создание кубика сводится к настройке всего лишь нескольких свойств. В приложении WPF 3D визуализируются три трехмерных кубика различных размеров с применением к ним трансформации неравномерного вращения вокруг 3-х осей координат.
Вывод 3D объектов в окно приложения WPF происходит через контейнер Viewport3D. Дочерними элементами Viewport3D могут быть только объекты классов унаследованных от Visual3D. Viewport3D является потомком FrameworkElement, благодаря этому трехмерное изображение можно вывести практически в любом 2D элементе, например: Grid, Canvas, Button, Label, TextBlock и т.д. При размещении Viewport3D в двухмерных элементах в некоторых случаях необходимо определять размеры Viewport3D.Width и Viewport3D.Height, иначе трехмерное изображение может быть невидимо.
Чтобы увидеть 3D содержимое Viewport3D необходимо создать камеру с помощью свойства Viewport3D.Camera. В нашем примере определена камера проекции перспективы PerspectiveCamera. Минимально необходимо определить свойства камеры: Position - позиция камеры, LookDirection - направление взгляда камеры, UpDirection - угловая ориентация камеры в пространстве.
Position и LookDirection взаимосвязанные между собой величины посредством точки в пространстве на которую должна смотреть камера. Изменяя позицию камеры, необходимо корректировать направление взгляда и наоборот. Методом вычитания векторов точки в пространстве и позиции камеры определяется вектор направления взгляда камеры LookDirection. Вектор LookDirection начинается от центра координат и указывает только направление взгляда камеры.
Пример вычисления вектора направления LookDirection (см. рисунок выше):
UpDirection - по умолчанию направление вектора вдоль оси Y (0, 1, 0). Вектор поворота камеры вправо на 90 градусов имеет значения (1, 0, 0), влево - (-1, 0, 0). Аналогичные повороты на 45 градусов соответственно: (1, 1, 0) и (-1, 1, 0).
Viewport3D.Children - объекты трехмерного пространства, коллекция объектов типа Visual3D, к ним же относятся и источники освещения. Для того чтобы определить собственный класс создания трехмерных объектов, необходимо чтобы он был наследником классов Visual3D. В прикрепленном исходнике 3D куб визуализирует класс Cube3D наследующий от ModelVisual3D.
Цель класса Cube3D - создать простой и удобный интерфейс пользователя для визуализации объемного куба. Внутри себя класс инкапсулирует методы построения сложного трехмерного объекта из группы примитивных треугольников. Как было описано выше, содержимым Viewport3D могут быть только потомки абстрактного класса Visual3D. Для возможности добавления объектов класса к коллекции Viewport3D.Children Cube3D наследует от ModelVisual3D. Открытые свойства класса используются в исходнике для настройки трехмерного куба в файле разметки XAML.
Закрытый метод AddFace(...) формирует прямоугольную грань из двух треугольников. Метод принимает четыре координаты вершин и материал грани. При создании треугольников вершины упорядочиваются, поэтому индексы вершин определять нет необходимости. Метод возвращает готовую грань куба с материалом в виде объекта типа GeometryModel3D. Использование в приложении метода AddFace(...) избавляет от излишнего повторяющегося кода: на шесть граней куба программный код только одного метода.
Создание грани из треугольников происходит в два этапа:
Создание координатной сетки из вершин треугольников в объекте класса MeshGeometry3D.
Создание трехмерного предмета по координатам объекта MeshGeometry3D из указанного материала в объекте класса GeometryModel3D.
Программный код метода:
private static GeometryModel3D AddFace(
Point3D point1,
Point3D point2,
Point3D point3,
Point3D point4,
Material material)
{
// Строит трехмерный объект из сетчатой геометрии
// и указанного материала.
GeometryModel3D geometryModel3D = new()
{
// Координаты построения геометрии объекта.
Geometry = new MeshGeometry3D()
{
Positions = new()
{
// Координаты вершин граней.
point1,
point2,
point3,
point3,
point4,
point1
}
},
Material = material
};
return geometryModel3D;
}
Закрытый метод DrawCube(...) формирует из граней готовый куб, определяет положение куба в пространстве и цвет каждой грани в отдельности. Внутри метода создаются вершины для всех углов трехмерного куба. Затем, упорядочивая вершины против часовой стрелки, грани объединяются в группу представляющую 3D куб как единое целое.
Чтобы визуализировать объединённую 3D геометрию, группа присваивается свойству ModelVisual3D.Content класса Cube3D. И только после этого трехмерные кубики становятся готовыми к рендерингу в области просмотра Viewport3D.
Прорисовка и трансформация трехмерных кубиков определена в файле XAML приложения. Запуск вращения происходит по нажатию кнопки мыши на области окна приложения.
Для использования программного кода собственного класса Cube3D, в разметке XAML приложения необходимо сопоставить для него пространство имён текущей сборки с префиксом для имени класса в разметке XAML: xmlns:mycode="clr-namespace:Wpf3DCube.PashaCode". После этой процедуры можно создавать трехмерные кубы настраивая внешний вид с помощью интерфейса пользователя класса Cube3D.
Кубики освещаются точечным источником, по свойствам напоминающим свет от электрической лампочки. При вращении кубиков интенсивность освещения граней меняется, тем самым усиливая иллюзию трехмерного пространства. Если освещать кубики только одним точечным источником, неосвещаемые грани будут полностью черного цвета. Для устранения этого недостатка в трехмерный мир добавлен ещё источник рассеянного света <AmbientLight Color="#FF444444"></AmbientLight>. Чтобы был виден цвет неосвещенных граней и в то же время не терялась реалистичность, интенсивность рассеянного света понижена до серого цвета. Если же сделать рассеянный свет белым иллюзия трехмерного света исчезнет.
Листинг разметки XAML приложения создания и вращения трехмерных кубиков: