Silverlight 2 的纹理三角形控件 - 3D 的基本构建块






4.68/5 (12投票s)
本文将介绍如何在 Silverlight 2.0 中实现一个自定义的三角形图元控件,该控件可用于 Silverlight 中的 3D 特效。
引言
与 WPF 不同,Silverlight 2.0 本身不支持 3D。但它内置了 .NET 运行时,所以没有什么可以阻止你在 Silverlight 中构建自己的小型 3D 引擎。虽然不是高端引擎,但足以让你玩转并为你的 RIA 添加一些炫酷的效果。
你只需要一些数学知识和所有 3D 事物的基本图元——三角形图元。具体来说,我指的是一个带有映射图像的纹理三角形。
因此,本文将重点介绍如何在 Silverlight 2.0 中实现一个自定义的三角形图元控件。
使用三角形控件
从 XAML 使用控件
以下代码片段展示了如何在 XAML 中直接使用 ImageTriangle
控件。
<UserControl x:Class="Controls.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xk="clr-namespace:XamlKru.Controls.Primitives;assembly=XamlKru.Controls"
>
<Canvas ...>
<xk:ImageTriangle Point1="0,0" Point2="180,20" Point3="0,200"
TexturePositions="0,0 1,0 0,1"
TextureSource="/Controls;component/checker.jpg"
/>
</Canvas>
</UserControl>
与往常一样,当你在 XAML 中引用自定义类时,必须首先为该控件的程序集添加命名空间引用。
xmlns:xk="clr-namespace:XamlKru.Controls.Primitives;assembly=XamlKru.Controls"
然后,你可以在 XAML 中使用 <xk:ImageTriangle ... />
来引用三角形,并通过属性设置顶点坐标(Point1
、Point2
和 Point3
)以及 TextureSource
。TextureSource
是一个 ImageSource
,用于指定将作为纹理渲染到三角形上的图像。你还可以通过设置 TexturePositions
来设置纹理坐标,例如 TexturePositions="0,0 1,0 0,1"
。
从代码中使用控件
在 XAML 中完成这些操作非常适合尝试控件并了解各种属性如何协同工作。然而,当你想将其用于 3D 或类似用途时,你会在代码中进行操作,无论是 C#、VB 还是 IronPython。
除了上面提到的属性,你还可以通过一个方法调用在命令式代码中一次性设置所有三个顶点,这具有一定的性能优势。
tri1.SetPoints(new Point(0,0), new Point(100,0), new Point(0,100));
TextureSource
可以直接设置为 BitmapImage
,这允许你使用从网上下载的图像,甚至从本地文件系统获取的图像作为纹理。
var texture = new BitmapImage();
texture.SetSource(inputStream);
tri1.TextureSource = texture;
还有一个 IsClockwise
属性,你可能希望将其用于 3D 中的背面剔除。
tri.IsClockwise
它是如何工作的?
如何在 Silverlight 中绘制一个纹理三角形?我能想到两种方法:
- 使用
Image
控件,并用裁剪路径将其一半裁剪掉。 - 使用
Path
绘制三角形形状,并将图像添加为ImageBrush
。
我选择了第二种方法,因为它似乎速度稍快一些。
Point1
、Point2
和 Point3
实现为依赖属性 (Dependency Properties)。依赖属性允许在 PropertyChanged
回调中执行计算,并增加了参与数据绑定和动画的功能。
为了将三角形变换到适应三个顶点,我在 Path
元素的 RenderTransform
中添加了一个 MatrixTransform
。从给定的点计算矩阵元素相当直接。可以在 这里 找到一篇解释其工作原理的文章。
当你查看控件模板和 UpdateCorners
方法时,你会注意到 RenderTransform
中还应用了第二个变换。当我第一次让代码工作并并排放置两个三角形时,我注意到它们之间有一个微小但烦人的接缝。这是由于 Silverlight 中抗锯齿的工作方式。这就是第二个变换的作用。它将三角形稍微放大一点(每边 0.5 - 1 像素),刚好足以覆盖接缝。
添加纹理
太好了,现在我们有了一个可以任意定位的三角形。我们完成了吗?实际上还没有!到目前为止,三角形上的图像是固定的。如果你想创建一个由两个三角形组成的平面(或者更好,4 个或 16 个,正如我下面将解释的那样),你不想为每个半部分加载单独的图像!
我们需要一种方法来拉伸和移动三角形本身的图像位置。我们需要纹理坐标。
同样,这是通过 MatrixTransform
完成的。这次它应用于 ImageBrush
,用纹理图像填充 Path
。不过,在这种情况下,情况会稍微棘手一些。
有效地,纹理坐标表示三角形在纹理图像上的位置。对于应用于 ImageBrush
的矩阵,情况则相反,我们希望从三角形的坐标系来看定位图像。
我们可以通过首先计算将三角形映射到图像的矩阵,然后反转该矩阵以获得将图像映射到三角形的变换来实现这一点。这正是 UpdateTexturePositions
方法中发生的事情。
var m = new Matrix(m11, m12, m21, m22, ox, oy).Invert();
_brushTransform.Matrix = m;
限制
当用两个三角形创建一个平面并将其变换到 3D 时,你可能会注意到它看起来不太对。这是因为深度信息没有被使用。唯一解决这个问题的方法是添加更多的三角形。如果你使用八个三角形而不是两个,纹理在 3D 中会看起来更逼真。你必须在性能和视觉正确性之间找到合适的权衡。
继续前进
本文不解释如何进行 3D,它只提供了一个基本的构建块!但是,项目文件包含了一些 3D 代码供你入门。
我的实验表明,在最近的机器上,渲染大约 100 个三角形可以达到可接受的帧速率。没什么值得太兴奋的,你很快就无法在 Silverlight 中构建 Halo 或 Second Life。至少,在 PC 上不行。但是,这足以实现 Coverflow 式的动画、旋转的 3D 球体和 360° 全景图,甚至可能是一些游戏。
历史
- 第一个版本由 Florian Krüsch 于 2008 年 5 月 3 日发布。
- 于 2008 年 6 月 11 日更新代码,以符合 Silverlight 2 Beta 2。