Геометрия трехмерных объектов

37

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

Неудивительно, что объект MeshGeometry3D представляет сетку (mesh). Если раньше вы имели дело с трехмерным рисованием (или читали что-нибудь о технологиях, положенных в основу современных видеокарт), то должны знать, что трехмерные объекты строятся из треугольников (полигонов). Это объясняется тем, что треугольники обеспечивают простейший и наиболее гранулярный способ описания поверхности. Треугольники просты, потому что каждый из них определяется всего тремя точками (вершинами в углах). Дуги и кривые поверхности определенно более сложны. Треугольники гранулярны, потому что все прочие фигуры с прямыми гранями (квадраты, прямоугольники и более сложные многоугольники) могут быть разбиты на коллекции треугольников.

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

Представление о том, как определяется сетка, является одним из ключевых моментов трехмерного программирования. Класс MeshGeometry3D включает четыре свойства, описанные ниже:

Positions

Содержит коллекцию всех точек, определяющих сетку. Каждая точка — это вершина треугольника. Например, если сетка состоит из 10 полностью различных треугольников, в этой коллекции будет содержаться 30 точек. Очень часто некоторые треугольники имеют общие грани, а это означает, что одна вершина может быть вершиной нескольких треугольников. Например, для описания куба требуется 12 треугольников (по два на каждую грань), но только 8 отличающихся точек. Общие вершины могут быть определены несколько раз, что еще более усложнит систему, поэтому лучше управлять текстурированием отдельных треугольников с помощью свойства Normals

TriangleIndices

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

Normals

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

TextureCoordinates

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

Следующий пример демонстрирует простейшую сетку, состоящую из единственного треугольника. Используемые единицы измерения не имеют значения, поскольку можно перемещать камеру ближе или дальше, а также изменять размер или расположение индивидуальных трехмерных объектов, применяя трансформации. Что действительно важно здесь — так это координатная система, показанная на рисунке. Как видите, оси X и Y имеют ту же ориентацию, что и при отображении двухмерной графики. Новой является ось Z. По мере увеличения значения Z точка отдаляется, по мере уменьшения — приближается:

Треугольник в трехмерном пространстве

Ниже показан элемент MeshGeometry3D, который можно использовать для определения этой фигуры внутри трехмерной области. Объект MeshGeometry3D в этом примере не использует свойства Normals или TextureCoordinates, поскольку фигура очень проста и будет нарисована кистью SolidColorBrush:

<MeshGeometry3D Positions="-1,0,0 0,1,0 1,0,0" TriangleIndices="0,2,1" />

Очевидно, что здесь присутствуют только три точки, перечисленные одна за другой в свойстве Positions. Порядок их следования в коллекции Positions не имеет значения, поскольку свойство TriangleIndices ясно определяет треугольник. По сути, свойство TriangleIndices устанавливает только один треугольник, состоящий из точек с номерами 0, 2 и 1. Другими словами, свойство TriangleIndices сообщает WPF, что нужно нарисовать треугольник, проведя линии от (-1,0,0) до (1,0,0) и затем до (0,1,0).

Обратите внимание, что трехмерное программирование подчиняется нескольким тонким правилам, которые легко нарушить. При определении фигуры вы сталкиваетесь с первым из них, а именно: перечислять точки, составляющие фигуру, следует в направлении против часовой стрелки относительно оси Z. Данный пример следует этому правилу. Однако его легко нарушить, изменив TriangleIndices на 0, 1, 2. В таком случае все равно определяется тот же самый треугольник, но вывернутый наизнанку. Другими словами, если посмотреть вниз по оси Z (как на рисунке), то на самом деле будет видна изнанка треугольника.

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

Геометрическая модель и поверхности

После определения соответствующим образом геометрии MeshGeometry3D ее нужно поместить в оболочку GeometryModel3D.

Класс GeometryModel3D имеет только три свойства: Geometry, Material и BackMaterial. Свойство Geometry принимает объект MeshGeometry3D, определяющий фигуру трехмерного объекта. В дополнение можно применять свойства Material и BackMaterial для определения поверхностей, из которых состоит фигура.

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

В WPF определены четыре класса материалов, каждый из которых унаследован от абстрактного класса Material из пространства имен System.Windows.Media.Media3D. Эти материалы перечислены ниже. В данном примере используется DiffuseMaterial, который применяется наиболее часто, поскольку его поведение ближе всего к реальным поверхностям:

DiffuseMaterial

Создает плоскую матовую поверхность, распределяющую свет равномерно во всех направлениях

SpecularMaterial

Создает блестящую обесцвеченную поверхность (типа металла или стекла). Отражает свет в противоположном направлении подобно зеркалу

EmissiveMaterial

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

MaterialGroup

Позволяет комбинировать более одного материала. Комбинируемые материалы накладываются друг на друга в том порядке, в каком добавлялись к MaterialGroup

DiffuseMaterial предоставляет единственное свойство Brush, принимающее объект Brush, который нужно использовать для рисования поверхности трехмерного объекта. (В случае применения кисти, отличной от SolidColorBrush, придется установить свойство MeshGeometry3D.TextureCoordinates для определения способа ее отображения на объект.) Ниже показано, как сконфигурировать треугольник, чтобы он имел желтую матовую поверхность:

<GeometryModel3D>
   <GeometryModel3D.Geometry>
      <MeshGeometry3D Positions = "-1,0,0 0,1,0 1,0,0" TriangleIndices = "0,2,1" />
   </GeometryModel3D.Geometry>
   <GeometryModel3D.Material>
      <DiffuseMaterial Brush="Yellow" />
   </GeometryModel3D.Material>
</GeometryModel3D>

В этом примере свойство BackMaterial не установлено, так что с изнанки треугольник просто не будет виден.

Все, что остается — это применить GeometryModel3D для установки свойства Content объекта ModelVisual3D и затем поместить его в окно просмотра. Но для того, чтобы увидеть объект, понадобятся еще две вещи: источник света и камера.

Пройди тесты
Лучший чат для C# программистов