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