【WPF】用三角形网格构建三维图形
虽然WPF只能支持部分三维模型,不过从应用功能开发的角度看,也已经够用了(非游戏开发)。WPF 的三维图形,说得简单一点,也就两种而已。
1、把二维对象放到三维空间中,这个应该较为好办,像 Image 控件,Shape 类型等,或者我们常用的一些控件,都可以放进三维空间中,用这种方式构建模型可能更为实用,也好弄(至少不借助专门的建模工具的前提下)。
2、完全利用坐标建模。实际上是用 N 个三角形来组合成三维模型。
虽然你会看到基类型 Visual3D 派生出了好几个类型,但总体上说也就划分为上述两类。Viewport2DVisual3D 类就是专门把二维对象放入三维空间中的,而 ModelVisual3D 和 UIElement3D 差不多,不同的是,UIElement3D 可以捕捉用户输入行为,如鼠标键盘触控等,如果你的三维模型要与用户交互,那么你可以选用这个类。
本文老周重点介绍第二种建模方法,就是用三角形来组建。为啥要用三角形呢?我们以前都学过几何,你想想,要多少个点才能确定一个面?是三个吧。两个点只能确定一条线,一个点就算了,那是无限射线。正是如此,所以三维图形中的所谓面,至少得要三个点才能确定下来,所以,要使用三角形来作为图形基元。
在介绍三角形坐标设置之前,老周先用废话来各大伙补充一点东东。
在WPF中承载三维模型是这样的:首先,你应该声明一个 Viewport3D 对象,它包含我们要建立的模型,前些天,老周写过一篇文章,介绍过三维型中的照相机,就是 Viewport3D 的 Camera 属性,你必须设置一个照相机,这样才能看到三维模型中的东西,它好比我们透过实体相机的取景器来观察客观事物,具体可以点这里查看。
之后呢,Viewport3D会包含一个子元素集合,每个元素必须是 Visual3D 的子类,由于是集合,你可以在其中添加 N 个。
用坐标来构建模型,应当用 ModelVisual3D 类,这个类很好玩,它自身又公开一个集合,可以再添加 ModelVisual3D 对象。也就是说,ModelVisual3D 对象是可以像树形结构一样嵌套,这样做可以方便你建立复杂的三维图形,并且可以有效分组。很多人会觉得,WPF 的三维模型很复杂很难懂,除了三维空间本身的复杂性外,估计可能是因为这种可视化元素之间的可嵌套性,刚开始接触时容易头晕。不过不用害怕,这世界上没有谁天生就会的,不要急,学习的时候,不要建太复杂的模型,这样思路也容易适应,只要你熟悉了就很轻松了,再复杂的东西,也是由各种简单的元素构成的。
ModelVisual3D 类有个Content属性,这个才是真正用来设置模型坐标的,它的类型是Model3D,它是个抽象类,它的派生类有两大块:一是GeometryModel3D,用来定义构成三维图形的坐标点;再者就是Light,表示用于照亮三维模型的灯光,因为三维图形是模拟现实中的事物的,所以会考虑到灯光照射的问题,它的实现类型有比如环境光(AmbientLight),这个像我们房间里的大灯,它可以照亮屋内所有角落;方向光(DirectionalLight),像手电筒的光吧,具有照射方向……
在GeometryModel3D类,也公开了三个属性:
Geometry:设置三维图形的坐标点集合,不仅只有点,还有索引、法向量、纹理映射等。
Material:图形正面的材质,就像金属表面,反射镜面类似的效果。
BackMaterial:有正面就会有背面,这是指图形背面的材质。
下面就是建立模型的重点了,即 Geometry属性,WPF 框架只公开了一个实现类——MeshGeometry3D,就是使用三角形网络来建立物体的表面。其中也有三个属性比要重要。
Positions:这个很重要,组成图形表面的各个坐标点的集合。
TriangleIndices:这个也很重要,它是一组整数集,从0开始,就是指定哪三个点组成一个三角形。
TextureCoordinates:纹理映射。之个有点像你把某妹子的照片贴在一块玻璃上,照片相当于纹理,经的每个点与玻璃的对应关系。比如,通常,我们会把照片的左上角的点对准玻璃左上角的点,右上角的点对准玻璃右上角的点。如果把照片左上角的点对准玻璃右下角的点,照片左上角的点对准玻璃左下角的点,这样贴出来的照片就是倒过来的。
Normals:构成图形的各个坐标点的法向量,默认全是 0,0,1。这个东西还真不知道怎么解释,一般我们可以不指定,用默认值就可以,大概是指面与面之间的关系吧。
咱们来看看演示吧。
<GeometryModel3D> <GeometryModel3D.Geometry> <MeshGeometry3D Positions="-2,2,0 3,-2,2 5,3,3 7,1,-2" TriangleIndices="0,1,2,1,3,2" TextureCoordinates="0,0 1,0 1,0 1,1" Normals="0,0,1 0,0,1 0,0,1 0,0,1"/> </GeometryModel3D.Geometry> <GeometryModel3D.Material> <DiffuseMaterial> <DiffuseMaterial.Brush> …… </DiffuseMaterial.Brush> </DiffuseMaterial> </GeometryModel3D.Material> </GeometryModel3D>
上面代码中,定义了四个坐标点,注意 TriangleIndices 属性的值,它有六个值,表明这四个点可以构成两个三角形。即它有两个面组成。
0-1-2 表示第一个点,第二个点和第三个点,这三个点构成了一个三角形。就像这样。
然后是 1-3-2,即第二个点,第四个点,第三个点又组成了一个三角型。
大伙要注意一个法则—— 组成三角形的点,如果是按逆时针排序的(就像上面例子),表示的是物体的正面;如果是顺时针的,表示的则是物体的反面。
再比如,咱们再写成这样,这回有五个点。
<MeshGeometry3D Positions="0,3,0 -3,-2,0 -1,-3,1 1,-3,1 3,-2,0" TriangleIndices="0,1,2,0,2,3,0,3,4" TextureCoordinates="0,0 1,1 1,1 1,1 1,1" />
第一个点在顶部,其余四个在下方,构成三个三角形。
好了,由于剧组严重缺乏资金,老周今天只能说到这里了。为了弄到更多资金来写后面的文章,老周要去玩《狂野撞车》来赚现金。