Создание куба. Текстурирование

Последнее обновление: 13.03.2016

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

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

<Window x:Class="_3DApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:_3DApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Slider Height="25" Minimum="0" Maximum="360" 
                Value="{Binding ElementName=rotate, Path= Angle}" />
        <Viewport3D Grid.Row="1">
            <Viewport3D.Camera>
                <PerspectiveCamera Position="0.5,0.5,3.5" LookDirection="0,0,-3.5"  />
            </Viewport3D.Camera>
            <Viewport3D.Children>
                <ModelVisual3D>
                    <ModelVisual3D.Content>
                        <DirectionalLight Color="White" Direction="-1,-1,-2" />
                    </ModelVisual3D.Content>
                </ModelVisual3D>
                <ModelVisual3D>
                    <ModelVisual3D.Content>
                        <GeometryModel3D>
                            <GeometryModel3D.Geometry>
                                <MeshGeometry3D Positions="0,0,0 1,0,0 0,1,0 1,1,0 
                      0,0,1 1,0,1 0,1,1 1,1,1"
 TriangleIndices="0,2,1 1,2,3 0,4,2 2,4,6 
                  0,1,4 1,5,4 1,7,5 1,3,7
                  4,5,6 7,6,5 2,6,3 3,6,7"/>
                            </GeometryModel3D.Geometry>
                            <GeometryModel3D.Material>
                                <DiffuseMaterial Brush="Blue" />
                            </GeometryModel3D.Material>
                        </GeometryModel3D>
                    </ModelVisual3D.Content>
                    <ModelVisual3D.Transform>
                        <RotateTransform3D>
                            <RotateTransform3D.Rotation>
                                <AxisAngleRotation3D x:Name="rotate" Axis="0 1 0" />
                            </RotateTransform3D.Rotation>
                        </RotateTransform3D>
                    </ModelVisual3D.Transform>
                </ModelVisual3D>
            </Viewport3D.Children>
        </Viewport3D>
    </Grid>
</Window>

В отличие от ранее рассмотренного нами примера с треугольником мы сделали несколько изменений - во-первых, увеличилось количество вершин. Чтобы создать куб, нам потребовалось определить восемь вершин, которые составили 6 сторон куба. Каждая такая сторона состоит из двух треугольников, которые мы определили в свойстве TriangleIndices, то есть нам потребовалось определить 12 треугольников.

Второе отличие состоит в том, что мы определили трехмерную трансформацию - поворот вокруг оси Y. О трехмерных трансформациях мы погорим дальше, а пока мы создали элемент Slider поверх окна ViewPort3D, привязав его свойство Value к свойству Angle элемента AxisAngleRotation3D. И благодаря этому мы можем повращать куб вперед и назад.

Куб и 3D в WPF

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

Говоря об освещении я сказал, что освещение вычисляется для каждой вершины, а затем цвет интерполируется на весь треугольник. Для треугольника это нормально. Но при создании куба вы можете увидеть, что на стороне куба в том месте, где находится совмещение треугольников (диагональ квадрата, образуемого двумя треугольниками), подобное освещение работает некорректно. И в этом месте мы можем увидеть полосу. Для сглаживания цветов в объекте MeshGeometry3D предназначено свойство Normals.

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

В математическом плане вектор нормали представляет векторное произведение векторов, составляющих две стороны треугольника. Для вычисления вектора нормалей можно воспользоваться средствами самого C#. Так, мы можем использовать следующий код:

private Vector3D CreateNormal(Point3D p0, Point3D p1, Point3D p2)
{
    Vector3D v0 = new Vector3D(p1.X - p0.X, p1.Y - p0.Y, p1.Z - p0.Z);
    Vector3D v1 = new Vector3D(p2.X - p1.X, p2.Y - p1.Y, p2.Z - p1.Z);
    return Vector3D.CrossProduct(v0, v1);
}

Затем полученные значения надо подставить в свойство Normals. Так, в нашем случае объект MeshGeometry3D будет выглядеть следующим образом:

<MeshGeometry3D Positions="0,0,0 1,0,0 0,1,0 1,1,0 
							  0,0,1 1,0,1 0,1,1 1,1,1" 
        TriangleIndices="0,2,1 1,2,3 0,4,2 2,4,6
						 0,1,4 1,5,4 1,7,5 1,3,7
                         4,5,6 7,6,5 2,6,3 3,6,7"
		Normals="0,1,0 0,1,0 1,0,0 1,0,0
                 0,1,0 0,1,0 1,0,0 1,0,0" />

В итоге мы получим более сглаженное изображение куба:

Нормали в WPF

Процесс текстурирования трехмерных объектов предусматривает наложение на их поверхность изображений. Сначала мы в качестве кисти материала указываем ImageBrush, который принимает изображение. Затем сопоставляем координаты текстуры с вершинами объекта. Допустим, в файле bronz.jpg у нас находится изображение, тогда весь измененный код объекта GeometryModel3D будет выглядеть так:

<GeometryModel3D>
    <GeometryModel3D.Geometry>
        <MeshGeometry3D Positions="0,0,0 1,0,0 0,1,0 1,1,0 
                                   0,0,1 1,0,1 0,1,1 1,1,1"
                TriangleIndices="0,2,1 1,2,3 0,4,2 2,4,6 
                                 0,1,4 1,5,4 1,7,5 1,3,7
                                 4,5,6 7,6,5 2,6,3 3,6,7"
                TextureCoordinates="0,1 1,1 0,0 1,0 
                                    0,1 1,1 0,0 1,0"/>
    </GeometryModel3D.Geometry>
    <GeometryModel3D.Material>
        <DiffuseMaterial>
            <DiffuseMaterial.Brush>
                <ImageBrush ImageSource="bronz.jpg" />
            </DiffuseMaterial.Brush>
        </DiffuseMaterial>
    </GeometryModel3D.Material>
</GeometryModel3D>

В данном случае у нас двухмерные координаты текстуры сопоставляются в координатами вершины. Для лицевой стороны куба наложение происходит так:

Текстура в WPF

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

Текстурирование трехмерных объектов в WPF

Чтобы избежать подобного эффекта, нам надо определить по четыре разных точки для каждой стороны куба. То есть всего у нас получится 24 вершины, а не 8, как сейчас. После этого мы можем совместить с каждой точкой текстуры вершину, и изображение будет отображаться на каждой стороне куба, хотя объем кода при этом немного возрастет.

Средства разработки 3D объектов

С помощью одной Visual Studio очень не просто создавать сложные трехмерные объекты. Поэтому в данном случае лучше воспользоваться специальными программами. Одной из таких программ является Blender. Для экспорта трехмерных моделей из Blender в XAML можно использовать проект XAML Exporter for Blender.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850