品牌网站建设

psd转xhtml/css 88元/页起

  • 符合W3C标准的XHTML/CSS编码
  • 多浏览器及操作平台支持
  • SEO(搜索引擎)语义代码标准
  • 经过优化的和切片图像
  • 结构良好的XHTML/CSS
  • 转换页面越多,折扣越多
更多优惠

手机13146413981qq393992480msnyibing98@hotmail.com

在组成 Microsoft Windows Presentation Foundation 的类中,System.Windows.Media.Media3D 命名空间中的那些类很突出。这些类的用途是使主流 Windows® 应用程序能够显示三维图形。与 Windows Presentation Foundation 2D 图形一样,通常可以用可扩展应用程序标记语言 (XAML) 非常方便地访问 3D 图形,但二者的相似性非常少。3D 图形编程涉及非常不同的概念和约定。其中,3D 和 2D 相同的部分是画笔的区域:您始终要用 2D 画笔来覆盖 3D 可视区的表面。
图 1 显示了 Hello3D,这是一个 3D 版的传统“Hello, World”程序。如果您运行的是 Windows Vista™ 或者是安装了 Microsoft® .NET Framework 3.0 运行库的 Windows XP,则只需使用 Internet Explorer® 即可启动产生该图形的 XAML 代码,从而可以看到图像(参见图 2)。
图 1 Hello3D 图像 

3D 视区
在 3D 图形编程中,没有线条、Bezier 样条曲线、矩形或椭圆。每个 3D 物体都是三维坐标空间中的三角形的集合。三角形是 3D 编程的基本单位,这是因为每个单独的三角形总是能定义一个平面,而三角形集合可以模仿立体物体,甚至可以模拟曲面。随着您深入了解 3D 编程,您将会用三角形看待生活中的所有事物。
正如 Hello3D.xaml 所示,3D 视图由 Viewport3D 元素组成。3D 场景需要一个或多个 GeometryModel3D 类型的物体、一个或多个光源、以及一个用于控制 3D 物体如何投射到 2D 表面从而控制观看者如何看到图像的摄像机。
GeometryModel3D 元素有三个重要属性:Geometry、Material 和 BackMaterial。Geometry 属性被设置为 MeshGeometry3D 元素,用于根据坐标点和三角形描述可视物体。Material 和 BackMaterial 属性说明物体的前面和背面如何着色。在 Hello3D.xaml 中,这两个属性被设置为 DiffuseMaterial 类型的对象。Material 属性是 VisualBrush,由包含文字“Hello, World”的 TextBlock 组成。BackMaterial 属性只是红色画笔。(如果要看到物体的背面,请将摄像机 Position 属性更改为“0 0 -5”,并将 LookDirection 更改为“0 0 1”。)

解析网格几何体
在 本专栏中,我将着重介绍此 Viewport3D 聚合的一个特别重要的部分 — MeshGeometry3D 类,该类用于定义 3D 物体的实际几何表示形式。该类有四个重要属性:Positions、TriangleIndices、TextureCoordinates 和 Normals。Positions 属性是 Point3D 对象的集合,这些对象通过 X、Y 和 Z 坐标定义位置。在此坐标系统中,X 坐标向右增加,Y 坐标沿屏幕向上增加,Z 坐标向屏幕外增加。(这称为右手坐标系统;如果用右手食指指向 X 值增加方向,并用中指指向 Y 值增加方向,则拇指指向 Z 值增加方向。Windows Presentation Foundation 3D 系统还实现了右手旋转定则:如果右手拇指指向任何轴的增加值方向,则其他手指的曲线显示围绕该轴的正向旋转轴的方向。)
Hello3D.xaml 示例中的 Positions 属性有六个以逗号分隔的 Point3D 对象:
Positions="-2 1 -1, 0 2 0, 2 1 -1, -2 -1 -1, 0 -2 0, 2 -1 -1"
前面三个从左到右是物体顶部的坐标,最后三个是对应的底部坐标。注意,中心的 Z 坐标是 0,但在左右边缘是 -1,因此中心是左右边缘的前景。
Positions 属性表示物体的所有顶点。这些顶点在定义物体时肯定是有作用的,但它们不能描述所有信息。任何一组三个顶点都可以组合成一个三角形,这就是 TriangleIndices 集合所说明的内容。TriangleIndices 是三个一组排列的整数集合。每一组的三个整数都定义了一个三角形。整数值是 Positions 集合中的序数。例如,第一个三联数是 0 3 1,它是指 3D 点 (-2, 1, -1)、(-2, -1,-1) 和 (0, 2, 0)。这是位于物体左上角的三角形。
TriangleIndices="0 3 1, 1 3 4, 1 5 2, 1 4 5"
TriangleIndices 集合实际上驱动物体的呈现。TriangleIndices 未引用的任何 Positions 元素都会忽略(如果没有 TriangleIndices,则 Positions 集合中的每个 Point3D 三联数都将解释为一个三角形)。
每个三角形都应有正面和背面。查看三角形的正面时,三联数以反时针方向表示顶点。如果将第一个三联数更改为 0 1 3,将看到左上三角形以红色着色,这是因为查看的是三角形的背面,而不是它的正面。
如 果 3D 物体以实线画笔着色,则 Positions 和 TriangleIndices 属性就已足够。对于其他类型的画笔(渐变画笔或并列画笔),还需要 TextureCoordinates。画笔是像热塑包装一样覆盖 3D 物体的 2D 表面。TextureCoordinates 集合表示 3D 物体的顶点和 2D 画笔的坐标之间的对应关系。此集合包含与 Positions 中的每个 3D 点对应的一个 2D 点。这些 2D 点是以 Y 轴向下为增加方向的相对坐标(0 和 1 之间)。点 (0, 0) 表示画笔的左上角,(1, 1) 是右下角。在 Hello3D.xaml 中,六个 2D 点表示 VisualBrush 的坐标,这些坐标被定义为 GeometryModel3D 元素的 Material 属性:
TextureCoordinates="0 0, 0.5 0, 1 0, 0 1, 0.5 1, 1 1"
实际上,3D 物体的每个三角形均由画笔的三角形覆盖,这些三角形可能需要拉伸或收缩以适合具体大小。
Normals 属性是按与 Positions 集合的一对一对应关系得到的向量的集合。每个顶点均被视为面向特定方向,该方向以该顶点的 Normals 向量表示。每个三角形内的每个点基于在其三个顶点上的向量的内插值,以不同方式反射光线。如果不提供 Normals 集合,则会基于在网格规范中共享的每个顶点上会合的三角形的 Normals 的平均值计算一个该集合。

算法网格几何体
System.Windows.Media Media3D 命名空间中的类没有提供超越 MeshGeometry3D 的任何更高级别的接口。(看完本文后,您可能会理解为什么。)对于诸如金字塔体和立方体这样具有平滑侧面的简单物体来说,可以手工编写 MeshGeometry3D 对象。对于更复杂的图元(特别是那些有曲面的图元)来说,您可能会选择使用符合 .NET 的语言通过算法生成顶点和系数。的确,如果您要求更进一步,则可能想到建立一个由从 MeshGeometry3D 派生的网格几何体组成的完整库。
但您会立即发现这样行不通。MeshGeometry3D 是密封的;它无法被继承。此外,MeshGeometry3D 本身派生于抽象类 Geometry3D,并且无法从 Geometry3D 派生,因为它有独立的内部构造函数。
如果不考虑从 MeshGeometry3D 派生类,则替代的途径是直接定义一个能够生成 MeshGeometry3D 对象的类,并将该对象作为属性公开。然后,可以在 XAML 文件中将此类定义为资源,并通过绑定引用 MeshGeometry3D。
图 3 显示了一个用 C# 编写的名为 SimpleCylinderGenerator 的类。(在这里,我将仅限于圆柱体。)在此专栏的可下载代码中,SimpleCylinderGenerator 支持名为 Petzold.MeshGeometries 的 DLL。
SimpleCylinderGenerator 类有两个属性:使用 Slices 属性可以定义用多少三角形模拟柱状体的曲面。(我从 Direct3D 类库中的静态 Mesh.Cylinder 方法的文档中选用术语“slices”。)沿柱状体长度方向分布的三角形的个数实际上是 Slices 属性的两倍。柱状体顶部和底部各自还需要很多等于 Slices 的三角形。
MeshGeometry 属性基于 Slices 属性创建 MeshGeometry3D 对象。对算法进行硬编码,以创建以一个单位的半径沿正向 Y 轴扩展一个单位的柱状体。注意,可以在随后调整此物体的大小,并使用转换将它移动到任何所需位置。
SimpleCylinderDemo 程序显示了如何使用此 SimpleCylinderGenerator 类。此程序的主体是 SimpleCylinderDemo.xaml 文件,如图 4 所示。根元素包含定义 SimpleCylinderGenerator 类并通过前缀 pmg(表示“Petzold 网格几何体”)与它关联的命名空间和 DLL 的 XML 命名空间声明。
Resources 部分包括键名称为“cylinder”并且 Slices 值为 36 的 SimpleCylinderGenerator 类型的对象。GeometryModel3D 元素将它的 Geometry 属性赋给此资源的绑定及其 MeshGeometry 属性。转换将更改柱状体的大小。在 DiffuseMaterial 上添加了一些有向光线和 SpecularMaterial 对象后,结果如图 5 所示。通过使用应用于该物体的任意转换,可以使此物体运动。
图 5 柱状体 
显 然,SimpleCylinderGenerator 技术是有效的,但我感到它有很多缺点和不足。找出这些问题并解决它们将使您不仅能从几个方面特别地洞察网格几何体,而且还能洞察 Windows Presentation Foundation 编程的某些常规方面。

适应使用者
当我刚开始为诸如柱状体等物体定义网格几何体时,我首先努力实现对称性,SimpleCylinderGenerator 反映了我的偏好:所有三角形都是等腰三角形。但对于沿柱状体纵向分布的三角形来说,实际上这一点就成了问题。
通常,当 Slices 属性很小时,有利的一点在于网格几何算法会产生某些有用的结果。请尝试将 SimpleCylinderGenerator 的 Slices 属性设置为 4,并在图 6 的左侧显示物体。(为了说明目的,边缘已增强。)这很有趣,但不如右侧的物体有用。右侧的物体要求纵向三角形不是等腰三角形,而是直角三角形,以便每一对直角三角形形成一个矩形。
图 6 对柱状体进行切片 
SimpleCylinderGenerator 不生成 TextureCoordinates 属性,如果将只用 SolidColorBrush 覆盖柱状体,则需要该属性。另一方面,如果想使用任何类型的 GradientBrush 或任何类型的 TileBrush,则需要一个与 Positions 属性一起智能地生成的 TextureCoordinates 属性。
用 2D 画笔覆盖柱状体时,您可能希望画笔环绕柱状体,以便画笔的左右边缘在我将其称之为“接缝”的线条上会合。此接缝应当沿柱状体的纵向延伸。之所以使用直角三角形而不是等腰三角形来定义网格,还有另一个原因。
SimpleCylinderGenerator 还反映了我的另一个偏好,经济性:Positions 集合中的所有 Point3D 对象都是唯一的。在生成 TriangleIndices 集合的 for 循环中,使用了模运算符,以便某些随后的三角形使用 Positions 集合中前期存在的点。对我来说,似乎为了“闭合”物体必须要这样做,但这完全是错误的。同样,如果想用画笔覆盖柱状体,需要在接缝处有重复的 Point3D 对象,以便 3D 空间中的相同位置同时映射到画笔的左侧和右侧。
通 过使柱状体有一个单位的长度和半径,SimpleCylinderGenerator 采取了在定义 Positions 集合时可以想像的最容易的途径。然后,它依赖于类的使用者(可能是您或另一个程序员)来实现转换,以正确调整柱状体大小和位置。通过使用转换操作,可以相 当容易地将柱状体的底部转换到另一个位置,但如果要将柱状体的顶部也放到特定位置呢?您将需要为该操作计算旋转转换。
请 让使用者休息一会儿!如果让柱状体生成程序具有属性 Point1 和 Point2 以表示柱状体两端的中心坐标位置,将会更有意义。可能还要考虑 Radius1 和 Radius2 属性,以便单独设置两端的半径。具有单独的半径属性会带来额外的好处:如果一个半径是 0,则算法将生成一个锥体。定义网格几何算法时,“奖励”图元始终是受欢迎的。
迄今为止,对 SimpleGeometryGenerator 的改进已经形成一个很长的列表,但我要使该列表更长。

与 XAML 集成
若 要使用 SimpleCylinderGenerator,必须将该类定义为资源,然后用绑定访问它。如果有一个用 XAML 实例化并直接集成到 Viewport3D 标记中的类,则会是更好的选择。但如何实现?它无法从 MeshGeometry3D 或 Geometry3D 派生。
幸运的是,有另一个方式,但它需要熟悉往往使人糊涂的 Media3D 命名空间和某些在类名称中涉及的术语。首先谈一谈可视效果和模型之间的差异。
可视效果是可以在屏幕上呈现自身的项目。在 Media3D 命名空间中,这是抽象的 Visual3D 类。Visual3D 对象是 Viewport3D 的子对象。
相 比之下,模型是对可视效果可以显示的项目的描述。在 3D 编程中,模型包括 3D 物体自身,还包括照射它们的光线。多个可视效果可以共享相同模型。例如,在 Media3D 命名空间中,模型由抽象 Model3D 类表示。从 Model3D 派生的类包括 GeometryModel3D、Light(它是抽象的)和 Model3DGroup。
将可视效果与模型联系在一起的类是 ModelVisual3D。ModelVisual3D 派生自 Visual3D,因此它一定是可视效果,但它有 Model3D 类型的 Content 属性。模型是可视效果的内容,这一点很像文本或位图是按钮的内容。
令人吃惊的是,ModelVisual3D 是在整个 Media3D 命名空间中唯一既不密封也不抽象的类,这意味着它可以用于继承而不会有明显的问题(有关详细信息,请参阅在 blogs.msdn.com/478923.aspx 的 Daniel Lehenbauer 的博客内容,以及在 blogs.msdn.com/479924.aspx 的 Karsten Januszewski 的博客内容)。
假设您编写了一个从 ModelVisual3D 派生的 Cylinder 类。然后,可以指定诸如 Viewport3D 的子项这样的元素:
<Viewport3D>
    <pmg:Cylinder Point1="0 1 5" Point2="3 2 -4" 
                  Radius1="0.25" Radius2="0.125" /> 
    ...
</Viewport3D>
这 似乎很方便,但它并不像此标记隐含的那样简单。如果 Cylinder 类派生自 ModelVisual3D,那么它继承了类型 Model3D(GeometryModel3D 从中派生而来)的 Content 属性。Cylinder 必须创建一个类型为 GeometryModel3D 的对象,以便设置为它继承的 Content 属性。GeometryModel3D 定义了 Geometry 属性,因此 Cylinder 类也会创建 MeshGeometry3D 对象以便设置为此 Geometry 属性。
迄 今为止,都还不坏,但还有问题。GeometryModel3D 还有 Material 和 BackMaterial 属性。这是材料如何与网格几何体关联的问题。若要使此方案正确工作,Cylinder 类必须重新定义 Material 和 BackMaterial 属性,因此标记将类似于如下所示:
<Viewport3D>
    <pmg:Cylinder Point1="0 1 5" Point2="3 2 -4" 
                  Radius1="0.25" Radius2="0.125">
        <pmg:Cylinder.Material>
        ...
        </pmg:Cylinder.Material>
        <pmg:Cylinder.BackMaterial>
        ...
        </pmg:Cylinder.BackMaterial>
    </pmg:Cylinder>
    ...
</Viewport3D>
现 在,大问题来了:我已经提到过,Cylinder 类有名为 Point1、Point2、Radius1 和 Radius2 的属性。您是否想让这些属性潜在地成为数据绑定的目标?我想您希望如此。您是否希望它们可以启用?我确信您希望如此。在这两种情况下,这些属性都必须被定 义为依赖性属性。您可能听说过,Windows Presentation Foundation 中的所有东西都是可启用的(但只有当它是依赖性属性时)。

依赖性属性
依 赖性属性已作为 Windows Presentation Foundation 的最重要的创新之一出现。在 Windows Presentation Foundation 中,可以按多种方式设置属性。可以在代码或 XAML 中直接在对象上设置属性。此外,可以通过样式和数据绑定来设置属性。某些属性可以通过父子关系继承,并且属性可以启用。如果根本不设置属性,则它会拥有默 认值。依赖性属性试图使所有这样的多样性能够以可预测的优先级正确工作。
依赖性属性需要您的类中有一些涉及向系统注册属性的非常大的开销,但完成之后,可以几乎不必考虑如何设置该属性。重要的是您的类会在属性已经更改时获得通知,因而它可以对这些更改做出反应。
本 文附带的可下载代码中的 Cylinder 类定义了九个依赖性属性。我没有直接从 ModelVisual3D 派生 Cylinder,而是创建了一个可能是其他类(例如 Sphere、Tube 和 BunnyRabbit)的父类的中间类,名为 ModelVisualBase。ModelVisualBase 定义了 Geometry、Material 和 BackMaterial 依赖性属性,它们向在 GeometryModel3D 中定义的相同属性添加所有者。
作 为如何在代码中实现依赖性属性的示例,让我们看一看 Cylinder 的 Radius1 属性。注册依赖性属性的过程涉及定义一个 DependencyProperty 类型的公用静态只读字段,名为 Radius1Property,它是属性名加上单词 Property:
public static readonly DependencyProperty Radius1Property =
    DependencyProperty.Register(
        "Radius1", typeof(double), typeof(Cylinder),
        new PropertyMetadata(1.0, PositionsChanged),
                             ValidateNonNegative);
注意,PropertyMetadata 构造函数的参数(用于表示默认值)、Cylinder 类中当值发生更改时要调用的方法以及用于验证值的方法。
还包括引用此静态只读字段的传统属性定义(又称为“CLR 属性”):
public double Radius1
{
    get { return (double)GetValue(Radius1Property); }
    set { SetValue(Radius1Property, value); }
}
GetValue 和 SetValue 方法是从 DependencyObject 继承的。任何实现依赖性属性的类都必须派生自 DependencyObject。
请 务必意识到对 Radius1 属性的更改不总是通过 CLR 属性进行的。例如,当启用 Radius1 时,会访问 Radius1Property 字段,而不是 Radius1 属性。因此,除了调用 GetValue 和 SetValue 外,应当避免在 Radius1 属性中进行其他任何操作。
Radius1Property 的定义包括对 Cylinder 类中两个方法的引用。因为这些方法是由静态字段引用的,因此方法自身也必须是静态的。ValidateNonNegative 方法只是检查 Radius1 是否未设置为负数值:
static bool ValidateNonNegative(object value)
{
    return (double)value >= 0;
}
如果某些代码将 Radius1 设置为负数值,将发生异常,但这不是您的类的责任。
其他方法用于通知类:属性已更改。由于可以在不访问 CLR 属性的情况下更改属性,因此该通知方法是对此属性更改做出反应的唯一机会。下面是我称为 PositionsChanged 的方法,当 Radius1 属性已更改时将调用该方法:
static void PositionsChanged(DependencyObject obj, 
    DependencyPropertyChangedEventArgs args)
{
    Cylinder cyl = (Cylinder)obj;
    cyl.GeneratePositions();
}
该 方法是静态的,但第一个参数是其属性发生改变的实际 Cylinder 对象。DependencyPropertyChangedEventArgs 对象具有关于发生更改的特定属性、它的旧值和新值的相关信息,但 Cylinder 将忽略这些信息,而直接调用实例方法:GeneratePositions。此方法负责基于新的半径生成 MeshGeometry3D 对象的 Positions 和 Normals 属性。

正确处理集合
为 了定义 MeshGeometry3D 对象的四个集合,Cylinder 类定义了三个单独的方法。GeneratePositions 负责 Positions 和 Normals 集合;其他两个方法名为 GenerateTriangleIndices 和 GenerateTextureCoordinates。Cylinder 的构造函数将调用所有三个方法以初始化对象;此后,将在响应依赖性属性的更改时调用它们。对于大多数属性(Point1、Point2、Radius1、 Radius2),只有 Positions 和 Normals 集合需要重新计算。但对于 Slices 属性,所有四个集合都需要重做。类似于 Slices 属性的是另一个名为 Stacks 的属性,该属性控制柱状体纵向的细分。在很多情况下,可以将 Stacks 设置为等于 1,这是默认值。但如果使用 PointLight 或 SpotLight 照射柱状体,则需要生成小很多的三角形来使光线的照射正确。
当诸如 Point1 和 Radius1 这样的属性启用时,可以非常频繁和尽可能快速地调用像 GeneratePositions 这样的方法。下面是一些潜在的问题和解决方案。
特 别是,方法应当避免分配内存。您希望的最后一件事是让 CLR 垃圾收集器处理您的动画,清理您留下的垃圾。如果全都有可能,则任何新的表达式都应当引用结构,而不是类。例如,在 Cylinder 类中,我让构造函数创建了 RotateTransform3D 和 AxisAngleRotation3D 类型的对象,它们作为字段进行存储并且可在每次调用 GeneratePositions 时重用。
您 设置为 MeshGeometry3D 属性的集合属于 Point3DCollection、Vector3DCollection、Int32Collection 和 PointCollection 类型。所有集合都派生自 Freezable,并且包括在集合的任何元素更改时所触发的 Changed 事件。这提供了功能强大的工具:例如,这些集合的单个成员可以启用,以创建 3D 物体的变体。但如果您全部重新计算整个集合,则只是想让通知在您完成时才发生。因此,应当将集合与 MeshGeometry3D 对象分离,并在您完成时再重新连接它。下面显示了 GeneratePositions 如何开始和结束:
void GeneratePositions()
{
    MeshGeometry3D mesh = (MeshGeometry3D)Geometry;
    Point3DCollection points = mesh.Positions;
    mesh.Positions = null;
    points.Clear();

        ...

    mesh.Positions = points;
}
第 一个语句访问由 ModelVisualBase 定义并由 Cylinder 继承的 Geometry 属性。注意,该方法重用 Point3DCollection,而没有重新分配一个新的。四个集合均由 Cylinder 的构造函数创建,创建时将使用它们将基于默认 Slices 属性而包含的元素的个数。如果 Slices 增加,则集合大小可能不足,并且必将发生内存分配,但内存分配的发生不应非常频繁。
另一方面,如果需要用大量元素填充集合,则应当用表示元素个数的构造函数创建该集合。通过分配所需内存,一开始会有助于在向集合添加元素时避免频繁的内存分配。感谢 Tim Cahill 在 2006 年 8 月 31 日的博客Daniel Lehenbauer 在 2006 年 11 月 19 日的总结所提供的这些提示。

接缝和结束
在本专栏前面,我讨论过让画笔左端与右端会合的接缝。此接缝应当在哪里?我决定它应当在柱状体的“背面”,在通常情况下它指向负向 Z 轴。此逻辑接缝的唯一位置位于柱状体的 Z 轴,在此情况下 Cylinder 类将它放在底部。
可 以让 ImageBrush 环绕柱状体,如果柱状体的长度和周长与位图的长宽之比相等,则不会有任何扭曲。但有一个问题涉及柱状体的两端。柱状体的两端应当怎样?我决定一定要让图像 的组成部分环绕顶端和底端。图像环绕的比例受其他两个属性控制,它们名为 Fold1 和 Fold2,其默认值分别是 0.1 和 0.9。它们表示沿柱状体纵向向两端折叠图像的相对画笔坐标。
柱状体的两端与共享中心中公用点的三角形圆环非常接近。如果“展开”定义柱状体的三角形,并利用矩形画笔展开它们,可以让网格几何体与画笔之间的对应关系可视化。图 7 显示了 12 片柱状体和渐变画笔的这种对应关系。此对应关系似乎是合理的,如果正确设置画笔维数、柱状体和“折叠”设置,则画笔可能根本无法拉伸。
图 7 映射到柱状体的画笔 
但对于位图,您可能希望有尽可能少的接缝。您已经有一个让位图的两端会合的接缝。通过使用图 8 所示的布局,可能避免在柱状体的两端出现其他接缝。当然,这将会涉及一些拉伸。(在这两种情况下,不会在柱状体上使用所有画笔,但除非创建更细的三角形,否则这是不可避免的。)
图 8 更好的画笔映射 
我 无法决定最想要什么,所以我二者都实现。第一个选项似乎适合于 DrawingBrush,而第二个适合于 ImageBrush,因此我定义了 TextureType 属性和有三个成员的 TextureType 枚举:Drawing、Image 和 None。None 选项将完全阻止生成 TextureCoordinates。

展示柱状体
现在是展示 Cylinder 类的时间。ImageOnCylinder 项目用 XAML 定义了一个柱状物体,并在它的表面上放置了一个位图:
<pmg:Cylinder x:Name="cyl" Slices="32" 
              TextureType="Image" Fold1="0.25" Fold2="0.75">
  <pmg:Cylinder.Material>
    <DiffuseMaterial>
      <DiffuseMaterial.Brush>
        <ImageBrush
         ImageSource="http://www.charlespetzold.com/PetzoldTattoo.jpg" 
        />
      </DiffuseMaterial.Brush>
    </DiffuseMaterial>
  </pmg:Cylinder.Material>
  ...
ImageOnCylinder.xaml 文件启用 Point1、Radius1 和 Radius2 属性,并且柱状体还会围绕它的轴缓慢旋转。图 9 显示了一张快照。程序还提供了一个滚动条,用于绕 X 轴旋转摄像机。(运行任何这些示例时,如果无法看到整个图像,请尝试使窗口变窄。)PolkaDottedCylinder 项目定义了 DrawingBrush,它的尺寸经过仔细计算,以避免扭曲,如图 10 所示。
图 9 ImageOnCylinder 
图 10 Polka 点 
最后示例回到 SolidColorBrush,但总共有 10 个柱状体,并显示了精确定义两端位置的便捷性。当两个 3D 物体会合时,它们合并得非常好,如图 11 所示。提供的两个滚动条可以从不同角度查看坐椅(但还不尝试坐在它上面)。
图 11 SteelChair 

记录第一个
提 供一个编写软件的老建议:在您完成之后,请记录下您已完成和重新开始的工作,以便将编写第一个版本时学到的所有东西合并起来。当然,这个方法不是始终实用 的,但它对我很有用。我编写的 Cylinder 类是我编写生成网格几何体的类的第五次或第六次尝试,现在我觉得我已经可以转到球体上了。

代码下载 (165 KB)

Comments

Add comment


 

biuquote
  • Comment
  • Preview
微笑得意调皮害羞酷大笑惊讶发呆喜欢可怜尴尬闭嘴噘嘴皱眉伤心抓狂呕吐坏笑漫骂发怒
Loading



订阅新易网博客

  • 订阅到抓虾
  • 哪吒提醒
  • pageflakes
  • Add to My Yahoo!
  • Add to Google
  • 鲜果阅读器订阅图标
  • 订阅到有道阅读
  • 用QQ邮箱阅读空间订阅我的博客。
专业设计 量身定制 品牌网站建设 体验价只需999元
.me 我要我的域名 新网域名 260元/年 再送空间100M
印彩色名片,每盒仅5元
免费推广您的网站或产品 互换广告位、友情连接、软文

Recent comments