Отображение текстур

66

До сих пор для рисования объектов использовалась кисть SolidColorBrush. Однако WPF позволяет нарисовать объект DiffuseMaterial, применяя для этого любую кисть. Это означает, что можно закрашивать его градиентами (LinearGradientBrush и RadialGradientBrush), векторными или растровыми изображениями (ImageBrush) или же содержимым двухмерного элемента (VisualBrush).

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

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

Отображение ImageBrush

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

<Grid>
    <Grid.RowDefinitions>
      <RowDefinition></RowDefinition>
      <RowDefinition Height="Auto"></RowDefinition>      
    </Grid.RowDefinitions>

    <Border BorderBrush="Yellow" BorderThickness="1" Margin="5">
      <Viewport3D>
        <Viewport3D.Camera>
          <PerspectiveCamera
          FarPlaneDistance="100"
          LookDirection="2,-1,-1"
          UpDirection="0,1,0"
          NearPlaneDistance="1"
          Position="-20,15,15"
          FieldOfView="60"
            >

          </PerspectiveCamera>
        </Viewport3D.Camera>
        <ModelVisual3D>
          <ModelVisual3D.Content>
            <AmbientLight Color="#888888"></AmbientLight>
          </ModelVisual3D.Content>
        </ModelVisual3D>

        <ModelVisual3D>
          <ModelVisual3D.Content>
            <DirectionalLight
             Color="White"
             Direction="2,3,1" />
          </ModelVisual3D.Content>

        </ModelVisual3D>

        <ModelVisual3D>
          <ModelVisual3D.Content>
            <GeometryModel3D x:Name="cubeGeometry">
              <GeometryModel3D.Geometry>
                <MeshGeometry3D
                  Positions="0,0,0 10,0,0 0,10,0 10,10,0
                             0,0,0 0,0,10 0,10,0 0,10,10
                             0,0,0 10,0,0 0,0,10 10,0,10
                             10,0,0 10,10,10 10,0,10 10,10,0
                             0,0,10 10,0,10 0,10,10 10,10,10
                             0,10,0 0,10,10 10,10,0 10,10,10"
                 TriangleIndices="0,2,1 1,2,3
                                   4,5,6 6,5,7                                   
                                   8,9,10 9,11,10                                   
                                   12,13,14 12,15,13
                                   16,17,18 19,18,17
                                   20,21,22 22,21,23"
                                    TextureCoordinates="0,0 0,1 1,0 1,1
                             1,1 0,1 1,0 0,0
                             0,0 1,0 0,1 1,1
                             0,0 1,0 0,1 1,1
                             1,1 0,1 1,0 0,0
                             1,1 0,1 1,0 0,0"
                            
                                />
                                                <!--    TextureCoordinates="0,0 1,0 0,1 1,1
                             0,0 1,0 0,1 1,1
                             0,0 1,0 0,1 1,1
                             0,0 1,0 0,1 1,1
                             0,0 1,0 0,1 1,1
                             0,0 1,0 0,1 1,1"-->
              </GeometryModel3D.Geometry>
              <GeometryModel3D.Material>
                <DiffuseMaterial>
                  <DiffuseMaterial.Brush>
                    <ImageBrush ImageSource="Tree.jpg"></ImageBrush>
                  </DiffuseMaterial.Brush>
                </DiffuseMaterial>
              </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>
    </Border>

    <Slider Margin="3" Grid.Row="1"  Minimum="0" Maximum="360" Orientation="Horizontal"
              Value="{Binding ElementName=rotate, Path=Angle}" ></Slider>

  </Grid>
Текстурированный куб

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

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

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

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

Видео и VisualBrush

Обычные изображения — не единственный вид содержимого, которое можно отобразить на трехмерную поверхность. Допускается также отображать содержимое, которое изменяется, такое как градиентные кисти, имеющие анимированные значения.

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

Достичь такого необычного эффекта неожиданно легко. Фактически, можно отображать видеокисть на поверхности куба с разной ориентацией, используя тот же набор координат TextureCoordinates, что и в предыдущем примере с отображением статичного изображения. Все, что потребуется для этого — заменить ImageBrush более совершенной кистью VisualBrush и применить в ней MediaElement. С помощью триггера событий можно даже запустить циклическое воспроизведение видео без необходимости написания кода.

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