3维图形概述

WPF 3-D功能使得开发员能够利用XAML和源代码绘制,转换,和3-D图形的动画效果。开发员能够结合2-D和3-D图形去创造更丰富的控件,提供复杂的数据说明,或者提高应用程序界面的用户体验。WPF支持的3D设计的目的不是为了提供一个全功能的游戏开发平台。这个主题提供了WPF图形系统3D功能的概述。

这个主题包含如下的内容。

3-D in a 2-D Container

WPF 3-D图形的内容被封装进了一个元素:Viewport3D,能够参与2元素的结构。图形系统将Viewport3D处理为2维可视化元素,与其他的WPF元素一样。Viewport3D函数作为一个窗口-一个视口-变成了3维的场景。更准确的说,这是一个被3-D场景投影的表面。

在常规的2-D应用中,使用Viewport3D作为你的另外一个容器,就像Grid和Canvas一样。尽管你可以在Viewport3D里使用其他的2-D绘图对象,你不能在一个Viewport3D里相互渗透2-D和3-D对象。这个话题将集中在Viewport3D的3-D图形。

3-D Coordinate Space

WPF坐标系统的原点在呈现区域(屏幕)的左上角。在二维系统里, x轴的正方向为右手方向,y轴的正方向向下。在3维坐标系统里,然而,原点位于呈现区域的中间,x轴的正方向为右边,但是y轴的正方向向上,z轴的正方向是原点向外,指向观察者。

常规的二维和三维坐标系统图:

在WPF里,通过坐标轴定义的空间是3-D对象静止的参照系。当你在这个空间里创造模型和创造灯光和相机来查看它们时,区别静态的参照系是非常有意义的,或者“世界坐标”。当你从局部的参照系创建每个模型时,你需要进行转换。记住对象在世界坐标系看起来完全不一样,或根本就不可见,依赖于灯光和照相机设置,照相机的位置并没有改变世界坐标对象的位置。

在2-D上开发的程序员习惯了在二维的屏幕上进行定位绘制。当你创建了一个3-D的场景,重要的是必须记住你是创建了2-D来表示3-D对象。因为3-D的场景依赖于观察者观察点,所以你必须规定观察点。Camara类允许你去3-D场景的观察点。

另外一种方法理解在2-D的表面呈现3-D的场景,是通过将场景投影到观察面。ProjectionCamera允许你指定不同的投影和他们的属性去改变3-D模型的外观。Perspectivecamera指定了投影场景透明度,换句话说,Perspective提供了一个消逝点的透视。你可以指定场景里坐标系空间照相机的位置,以及照相机的方向和视角,和一个定义了场景上方的矢量。如下的图表说明了PerspectiveCamara的投影。

ProjectionCamera的NearPlaneDistance和FarPlaneDistance属性限制了照相机的投影范围。因为照相机可以定位到场景的任何地方。照相机实际上可能在模型的内部或者附近定位,使它很难区别对象属性。NearPlaneDistance允许你规定照相机到对象的最小的距离。反过来手,FarPlaneDistance使你规定了对象和照相机最远的距离,以确保对象不会太远以至于场景不能识别它。

照相机的位置

OrthographicCamera规定了3-D和2-D可视表面的垂直投影。像别的照相机一样,它规定了一个位置,观察方向和“向上”方向。不像PerspectiveCamera。然而,OrthographicCamera描述的投影并没有包括透视。换句话说,OrthographicCamera描述了景色盒子,它们的边是平行的,而不是两边相交于一点的相机。如下的图像显示了利用PerspectiveCamera和orthographicCamera观察的相同的模型。

如下的代码是典型的相机设置。

PerspectiveCamera myPCamera = new PerspectiveCamera();

myPCamera.Position = new Point3D (0,0,2)

myPCamera.LookDirection = new Vector3D(0, 0, -1)

myPCamera.FieldOfView = 60;

myViewport3D.Camera = myPCamera;

Model and Mesh Primitives

Model3D是一个抽象的基类来表示一个通用的3-D对象。为了建立一个3-D场景,你需要察看一些对象,这些继承自Model3D的对象组成了场景。目前,WPF支持的模型几何体是GeometryModel3D。这个模型的Geometry属性进行网格原型。

为了建立一个模型,从一个基本的或者网格开始建立。一个3-D基本图是单个3-D实体的顶点集。大部分3-D系统提供了简单封闭图形作为模型蓝本:通过三个顶点定义的三角形。因为三角形的三个顶点共面,你可以继续增加三角形来模拟更加复杂的形状,称为网格。

WPF的3-D系统目前提供了MeshGeometry3D类,允许你规定任意几何。它目前不支持预定义的基本3-D图形,例如球体和立方体形式。通过指定一系列三角形顶点作为位置属性开始创建MeshGeomety3D。每个顶点指定为Point3D。(在标记应用扩展语言(XAML),规定这个属性作为一个三个顶点坐标组成的列表),根据它的几何形状,你的网格可能由许多三角形组成,有一些共用顶点坐标。为了正确的绘制网格,WPF需要哪些三角形共用了顶点的信息。通过指定一组具有TriangleIndices属性的列表来提供信息。这个列表指定了哪些点的位置列表将决定三角形。

<GeometryModel3D>

<GeometryModel3D.Geometry>

<MeshGeometry3D

  Positions = “-1 -1 0  1 -1 0  -1 1 0  1 1 0”

  Normals = “0 0 1  0 0 1  0 0 1  0 0 1”

  TextureCoordinates= ”0 1  1 1  0 0  1 0”

  TriangleIndices = “0 1 2  1 3 2”

<GeometryModel3D.Geometry>

<GeometryModel3D.Material>

                <DiffuseMaterial>

                                <DiffuseMaterial.Brush>

                                                <SolidColorBrush Color=”Cyan” Opacity=”0.3” />

                                <DiffuseMaterial.Brush>

</DiffuseMaterial>

</GeometryModel3D.Material>

<GeometryModel3D.Transform>

                <TranslateTransform3D

   OffsetX=”2” OffsetY=”0”  OffsetZ=”-1” >

</TranslateTransform3D>

<GeometryModel3D.Transform>

</GeometryModel3D>

在上面的例子中,Positions列表中指定了8个顶点来定义立方体网格。TriangleIndices属性指定了12个3个索引组。列表中的每个数字在Positions列表里引用了偏移量。例如,这里有三个顶点通过Positions进行指定:(1,1,0), (0,1,0), (0,0,0)。首先,这三个索引通过TriangleIndices指定为0, 2, 1,与Positions列表中的第一个,第三个,第二点对应。结果是,第一个三角形组成的立方模型将由(1,1,0) (0,1,0) 和(0,0,0)组成。剩下的11个三角形将使用相似的方法决定。

你可以通过指定Normals和TextureCoordinates的值连续定义模型。为了呈现模型的表面,图形系统需要信息,关于面对任意给出三角形表面的方向信息。使用这些信息来明白模型的计算:面朝光源的表面看起来比其他远离灯光角度的大。尽管WPF可以通过使用位置坐标来决定默认法向量,你也可以指定不同的法向量来近似曲面的外观。

TextureCoordinates属性指定了一个点集告诉图形系统怎样映射坐标,来决定如何将纹理绘制到网格的定点。TextureCoordinates指定为从0到1。与Normals属性一样,图形系统可以计算默认的纹理坐标,但是你也可以选择设置不同的纹理坐标来控制重复模式纹理的映射,例如,纹理坐标的更多信息可以在随后的话题里找到,或者参考Direct3D SDK。

如下的例子显示了如何在源代码中创建立方体模型的表面。注意到你可以将整个的立方体绘制为单个的GeometryModel3D;这个例子作为独特的模型绘制了立方体表面,将应用到每个面分离的纹理。

MeshGeometry3D  side1Plane = new MeshGeometry3D();

side1Plane.Positions.Add(new Point3D(-0.5, -0.5, -0.5));

side1Plane.Positions.Add(new Point3D(-0.5, 0.5, -0.5));

side1Plane.Positions.Add(new Point3D(0.5, 0.5, -0.5));

side1Plane.Positions.Add(new Point3D(0.5, 0.5, -0.5));

side1Plane.Positions.Add(new Point3D(0.5, -0.5, -0.5));

side1Plane.Positions.Add(new Point3D(-0.5, -0.5, -0.5));

side1Plane.TriangleIndices.Add(0);

side1Plane.TriangleIndices.Add(1);

side1Plane.TriangleIndices.Add(2);

side1Plane.TriangleIndices.Add(3);

side1Plane.TriangleIndices.Add(4);

side1Plane.TriangleIndices.Add(5);

side1Plane.Normals.Add(new Vector3D(0, 0, -1));

side1Plane.Normals.Add(new Vector3D(0, 0, -1));

side1Plane.Normals.Add(new Vector3D(0, 0, -1));

side1Plane.Normals.Add(new Vector3D(0, 0, -1));

side1Plane.Normals.Add(new Vector3D(0, 0, -1));

side1Plane.Normals.Add(new Vector3D(0, 0, -1));

side1Plane.TextureCoordinates.Add(new Point(1, 0));

side1Plane.TextureCoordinates.Add(new Point(1, 1));

side1Plane.TextureCoordinates.Add(new Point(0, 1));

side1Plane.TextureCoordinates.Add(new Point(0, 1));

side1Plane.TextureCoordinates.Add(new Point(0, 0));

side1Plane.TextureCoordinates.Add(new Point(1, 0));

Applying Materials to the Model

一个网格看起来像一个三维对象,它必须有纹理来覆盖由点和三角形定义的表面,所以它可以通过照相机投影和点亮。在2-D,你使用Brush类来应用颜色,模式,梯度和其他在屏幕上的可视化内容。3-D对象的表面,然而,是一个发光模型的函数,不仅仅是将颜色和模式应用到它们。真实世界的对象根据表面质量的不同反射不同的光:光泽和光泽表面与粗糙和磨砂表面看起来不一样,并且有些对象吸收光,有些对象发光。你可以为3-D对象应用与2-D上相同的画刷,但是你不能直接应用它们。

为了定义模型表面的特征,WPF使用了Material抽象类。材料具体的子类决定了一些模型表面的外观特征,并且同样提供了画刷属性,,是你可以传递SolidColorBrush, TitleBrush, VisualBrush.。

DiffuseMaterial规定了画刷将被应用到点亮时漫射的模型。使用DiffuseMaterial大部分类似于使用2-D 模型的画刷;模型表面尽管有光泽,但没有反射光。

SpecularMaterial规定了画刷将被应用到模型,尽管模型表面很硬或者光滑。具有反射强光的能力。你可以设置纹理的程度来建议反射的质量或者“发光”,通过指定SpecularPower属性的值。

EmissiveMaterial允许你规定纹理,将应用到具有发出画刷颜色光能力的模型。这并没有使模型变亮;然而,如果纹理是Diffusematerial或者SpecularMaterial,它会在阴影里加入不同的东西。

为了更好的性能,GeometryModel3D的背面(这些面不在视觉范围内,因为它们与照相机是相反的)可以从场景里抹掉。指定模型背面的材料,例如飞机,设置模型的BackMaterial属性

为了获得表面特质,例如发光或者反光效果,你可以在一个模型里面交替使用几种不同的画刷。你可以通过MaterialGroup类应用和反复使用多种材料。MaterialGroup的孩子们被应用到从头到尾多个渲染通道。

如下的代码显示了怎样将固体颜色和画刷应用到3-D模型。

<GeometryModel3D.Material>

                <DiffuseMaterial>

                                <DiffuseMaterial.Brush>

                                                <SolidColorBrush Color=”Cyan” Opacity=”0.3” />

                                </DiffuseMaterial.Brush>

                </DiffuseMaterial>

</GeometryModel3D.Material>

<DrawingBrush x:Key="patternBrush" Viewport="0,0,0.1,0.1" TileMode="Tile">
  <DrawingBrush.Drawing>
    <DrawingGroup>
      <DrawingGroup.Children>
        <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z"
          Brush="Gray" />
        <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z"
          Brush="Gray" />
        <GeometryDrawing Geometry="M0.25,0.25 L0.5,0.125 0.75,0.25 0.5,0.5z"
          Brush="#FFFF00" />
        <GeometryDrawing Geometry="M0.25,0.75 L0.5,0.875 0.75,0.75 0.5,0.5z"
          Brush="Black" />
        <GeometryDrawing Geometry="M0.25,0.75 L0.125,0.5 0.25,0.25 0.5,0.5z"
          Brush="#FF0000" />
        <GeometryDrawing Geometry="M0.75,0.25 L0.875,0.5 0.75,0.75 0.5,0.5z"
          Brush="MediumBlue" />
      </DrawingGroup.Children>
    </DrawingGroup>
  </DrawingBrush.Drawing>
</DrawingBrush>
DiffuseMaterial side5Material = new DiffuseMaterial((Brush)Application.Current.Resources["patternBrush"]);

 Illuminating the Scene

3-D图像里的灯光与真实世界里的灯关效果是一样的:使表面可见。更重要的一点,灯光确定了场景的哪一部分将被纳入投影。WPF里Light对象创建了各种灯光和阴影效果,仿照各种真实世界的灯光行为。你必须在你的场景里包含一种灯光,或者模型将是不可见的。

如下的灯光继承自Light类:

AmbientLight:提供环境光均匀的照亮所有的对象,无论其位置与方向。

DirectionalLight: 像一个远距离的官员照明一样。定向灯光使用Vector3D来指定灯光方向,但是没有指定的位置。

PointLight: 就像一个附近的光源点亮。PointLight有一个位置,并且从那个位置投射灯管,场景里的对象根据它们的位置和相对光源的距离进行点亮。PointLightBase暴露了一个Range属性,决定了超过了一定距离后将不会被照亮。PointLight同样暴露了衰减属性,决定了灯光随着距离而减少的强度。你可以指定为常数,线性的,或者二次插值灯光衰减。

SpotLight: 继承自PointLight。聚光灯就像点灯一样照亮,并且有位置和方向。投影的光在一个锥形区里,通过InnerConeAngle和OuterConeAngle属性来设置角度。

Lights是Model3D对象,所以你可以转换和动画光的属性,包含位置,颜色方向以及范围。

<ModelVisual3D.Content>

                <AmbientLight Color=”#333333”  />

</ModelVisual3D.Content>

DirectionalLight myDirLight = new DirectionalLight();

myDirLight.Color = Colors.White;

myDirLightColor = new Vector3D(-3, -4, -5);

modelGroup.Children.Add(myDirLight);

Transforming Models

当你创建模型时,它们在场景里有一个特殊的地方。为了在场景周围移动这些模型,去旋转它们,或者改变它们的尺寸,改变顶点去定义模型本身是不实际的。相反的,就像在2-D模型里面一样,你对模型应用转换。

每个模型对象都有一个 Transform属性,你可以移动,重定向或者重新定义模型的大小。当你应用了转换,你有效的偏移了所有的模型里所有点,无论是通过指定向量还是值进行转变。换句话说,你已经转变了定义模型的坐标空间(“模型空间”),但是你并没有改变整个场景坐标系(世界坐标)里组成模型几何的值。

Animating Models

WPF的3D 实现了与2-D图形一样的参与相同时间和动画系统。换句话说,为了制作一个3-D动画场景,需要动画模型属性。可以对动画属性进行直接的初始化,但是它通常很容易进行动画转换,改变模型的外表和位置。因为转变可以应用到Model3DGroup对象和单个对象,它可以应用一组动画到Model3DGroup的子集或另一组子对象集的动画。你也可以通过你场景照明性能的动画属性实现多种视觉效果。最终,你可以通过相机的位置和视野,选择动画自身的投影。

WPF里为了使对象有动画效果,你需要创建一个时间线(某些属性沿着时间而改变),并将这些属性应用到动画中。因为3-D场景里所有的对象都继承于Viewport3D,你想应用动画场景的属性目标就是Viewport3D属性。

假设你想做一个模型在一个地方进行摆动。你可能选择在模型里应用RotateTranform3D属性,并且它旋转的坐标轴是从一个矢量到另外一个。如下的代码示例演示了应用Vector3DAnimation到旋转Rotation3D的轴属性,假设RotateTransform3D是应用到TransformGroup模型中的一个。

RotateTransform3D myRotateTransform = new RotateTransform3D(new AxisAngleRotation3D(new Vector(0, 1, 0), 1));

Vector3DAnimation myVectorAnimation = new Vector3DAnimation(new Vector3D(-1, -1, -1), new Duration(TimeSpan.FromMilliseconds(5000)));

myVectorAnimation.RepeatBehavior = Repeatbehavior.Forever;

myRotateTransform.Rotation.BeginAnimation(AxisAngleRotation3D.AxisPropery, myVectorAnimation);

cube1TransformGroup.Childern.Add(myRotateTransform);

Add 3-D Content to the Window

为了渲染这个场景,增加模型和光到Model3DGroup,然后设置Model3Dgroup为ModelVisual3D的内容,将ModelVisual3D增加到Viewport3D的子集。通过设置Camera 属性将照相机增加到Viewport3D。

最终,增加Viewport3D到窗口。当Viewport3D被包含在布局元素里,例如Canvas,通过设置高度和宽度属性来设置Viewport3D的属性(继承至FrameworkElement)


<UserControl x:Class="HostingWpfUserControlInWf.UserControl1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
 
    <Grid>
 
      <!-- Place a Label control at the top of the view. -->
      <Label 
                                   HorizontalAlignment="Center" 
                                   TextBlock.TextAlignment="Center" 
                                   FontSize="20" 
                                   Foreground="Red" 
                                   Content="Model: Cone"/>
 
      <!-- Viewport3D is the rendering surface. -->
      <Viewport3D Name="myViewport" >
 
        <!-- Add a camera. -->
        <Viewport3D.Camera>
          <PerspectiveCamera 
                                                    FarPlaneDistance="20" 
                                                    LookDirection="0,0,1" 
                                                    UpDirection="0,1,0" 
                                                    NearPlaneDistance="1" 
                                                    Position="0,0,-3" 
                                                    FieldOfView="45" />
        </Viewport3D.Camera>
 
        <!-- Add models. -->
        <Viewport3D.Children>
 
          <ModelVisual3D>
            <ModelVisual3D.Content>
 
              <Model3DGroup >
                <Model3DGroup.Children>
 
                  <!-- Lights, MeshGeometry3D and DiffuseMaterial objects are added to the ModelVisual3D. -->
                  <DirectionalLight Color="#FFFFFFFF" Direction="3,-4,5" />
 
                  <!-- Define a red cone. -->
                  <GeometryModel3D>
 
                    <GeometryModel3D.Geometry>
                      <MeshGeometry3D 
    Positions="0.293893 -0.5 0.404509  0.475528 -0.5 0.154509  0 0.5 0  0.475528 -0.5 0.154509  0 0.5 0  0 0.5 0  0.475528 -0.5 0.154509  0.475528 -0.5 -0.154509  0 0.5 0  0.475528 -0.5 -0.154509  0 0.5 0  0 0.5 0  0.475528 -0.5 -0.154509  0.293893 -0.5 -0.404509  0 0.5 0  0.293893 -0.5 -0.404509  0 0.5 0  0 0.5 0  0.293893 -0.5 -0.404509  0 -0.5 -0.5  0 0.5 0  0 -0.5 -0.5  0 0.5 0  0 0.5 0  0 -0.5 -0.5  -0.293893 -0.5 -0.404509  0 0.5 0  -0.293893 -0.5 -0.404509  0 0.5 0  0 0.5 0  -0.293893 -0.5 -0.404509  -0.475528 -0.5 -0.154509  0 0.5 0  -0.475528 -0.5 -0.154509  0 0.5 0  0 0.5 0  -0.475528 -0.5 -0.154509  -0.475528 -0.5 0.154509  0 0.5 0  -0.475528 -0.5 0.154509  0 0.5 0  0 0.5 0  -0.475528 -0.5 0.154509  -0.293892 -0.5 0.404509  0 0.5 0  -0.293892 -0.5 0.404509  0 0.5 0  0 0.5 0  -0.293892 -0.5 0.404509  0 -0.5 0.5  0 0.5 0  0 -0.5 0.5  0 0.5 0  0 0.5 0  0 -0.5 0.5  0.293893 -0.5 0.404509  0 0.5 0  0.293893 -0.5 0.404509  0 0.5 0  0 0.5 0  " 
    Normals="0.7236065,0.4472139,0.5257313  0.2763934,0.4472138,0.8506507  0.5308242,0.4294462,0.7306172  0.2763934,0.4472138,0.8506507  0,0.4294458,0.9030925  0.5308242,0.4294462,0.7306172  0.2763934,0.4472138,0.8506507  -0.2763934,0.4472138,0.8506507  0,0.4294458,0.9030925  -0.2763934,0.4472138,0.8506507  -0.5308242,0.4294462,0.7306172  0,0.4294458,0.9030925  -0.2763934,0.4472138,0.8506507  -0.7236065,0.4472139,0.5257313  -0.5308242,0.4294462,0.7306172  -0.7236065,0.4472139,0.5257313  -0.858892,0.429446,0.279071  -0.5308242,0.4294462,0.7306172  -0.7236065,0.4472139,0.5257313  -0.8944269,0.4472139,0  -0.858892,0.429446,0.279071  -0.8944269,0.4472139,0  -0.858892,0.429446,-0.279071  -0.858892,0.429446,0.279071  -0.8944269,0.4472139,0  -0.7236065,0.4472139,-0.5257313  -0.858892,0.429446,-0.279071  -0.7236065,0.4472139,-0.5257313  -0.5308242,0.4294462,-0.7306172  -0.858892,0.429446,-0.279071  -0.7236065,0.4472139,-0.5257313  -0.2763934,0.4472138,-0.8506507  -0.5308242,0.4294462,-0.7306172  -0.2763934,0.4472138,-0.8506507  0,0.4294458,-0.9030925  -0.5308242,0.4294462,-0.7306172  -0.2763934,0.4472138,-0.8506507  0.2763934,0.4472138,-0.8506507  0,0.4294458,-0.9030925  0.2763934,0.4472138,-0.8506507  0.5308249,0.4294459,-0.7306169  0,0.4294458,-0.9030925  0.2763934,0.4472138,-0.8506507  0.7236068,0.4472141,-0.5257306  0.5308249,0.4294459,-0.7306169  0.7236068,0.4472141,-0.5257306  0.8588922,0.4294461,-0.27907  0.5308249,0.4294459,-0.7306169  0.7236068,0.4472141,-0.5257306  0.8944269,0.4472139,0  0.8588922,0.4294461,-0.27907  0.8944269,0.4472139,0  0.858892,0.429446,0.279071  0.8588922,0.4294461,-0.27907  0.8944269,0.4472139,0  0.7236065,0.4472139,0.5257313  0.858892,0.429446,0.279071  0.7236065,0.4472139,0.5257313  0.5308242,0.4294462,0.7306172  0.858892,0.429446,0.279071  "                   TriangleIndices="0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 " />
                    </GeometryModel3D.Geometry>
 
                    <GeometryModel3D.Material>
                      <DiffuseMaterial>
                        <DiffuseMaterial.Brush>
                          <SolidColorBrush 
                            Color="Red" 
                            Opacity="1.0"/>
                        </DiffuseMaterial.Brush>
                      </DiffuseMaterial>
                    </GeometryModel3D.Material>
 
                  </GeometryModel3D>
 
                </Model3DGroup.Children>
              </Model3DGroup>
 
            </ModelVisual3D.Content>
 
          </ModelVisual3D>
 
        </Viewport3D.Children>
 
      </Viewport3D>
    </Grid>
 
</UserControl>
 

 引用:http://msdn.microsoft.com/en-us/library/ms747437(VS.100).aspx

原文地址:https://www.cnblogs.com/longcloud/p/3159038.html