原作者:Rajesh Lal
原文地址:http://msdn.microsoft.com/zh-cn/magazine/hh547098.aspx
在本文中,我将展示如何开发在 Silverlight 中的 3D 对象。开始于 3D 上简要背景知识,再看一些高级的功能,在 Silverlight 中创建和显示 3D 对象。将举一个简单的例子,多维数据集,并演示三种不同的方法来创建 3D 转换。还将介绍在计算机屏幕上显示3D对象需要哪些关键的要素。最后,将探讨如何使用Silverlight 5创建更丰富的3D对象。
Silverlight 支持的右手坐标系,这意味着的正 z 轴指向观察方向 (请参阅图1)。有三个3D 的主要元素所需的对象显示在屏幕上:
- 透视
- 变换
- 光效
图 1 A 引用多维,显示具有透视效果的边
透视意味着对象更近,对我们的某些部分显示大于那些远离。例如,在图 1、 侧 bd看起来比侧大 fh。在现实生活中透视创建消失点中,这意味着如果延伸线条 ae, bf, cg和 dh,在 z 轴上,它们将可以满足多远在任意的单个点。
第二个方面是变换。三维对象,当显示在屏幕上,在所有方向上的 3D 空间中可以移动。它可以在任何单一的轴移动 — 缩放大小 — 同时保持不变的角度来看。它可以在所有轴旋转 360 度: x、 y 和 z。这样,3D 对象在屏幕上呈现所需的灵活性。
3D 的最后一个元素是光效。光在 3D 中的创建底纹,靠近光源更亮并随距离淡入。在 3D 呈现时,两种流行底纹是"平面"底纹和底纹"渐变"。我将解释它们如何更高版本不同,光还将创建阴影。
在即将提供的示例中,将探讨你可以在其中创建 3D 对象在 Silverlight 中的三种不同方式:
- 使用透视图 3D
- 使用多个帧和计时器
- 与 XNA 库使用基元
在第一种方法,使用二维元素,创建一个对象,但它的外观和行为,就好像在 3D 空间中。全景 3D 是一种特殊的变换功能添加允许基本调整 (如旋转、 缩放和平移,在 3D 空间中的 Silverlight 4 中。第二种方法是,您不创建 3D 对象,但而是创建了特定的转变的最终输出帧并显示它们使用计时器的暴力方法。最后的方法是,创建丰富的3D 对象,Silverlight 5可以使用XNA的基元素(三角形的数据集合)。下面现在开始吧。
创建多维数据集使用的角度来看 3D
Silverlight 4 支持 PlaneProjection 类 (请参阅图 2),它可用于任何 UI 元素,将投影属性在类关系图中所示。PlaneProjection 类允许的用户界面元素上的角度来看 3D 转换。尽管它不直接允许创建 3D 对象,可以使用多个"墙壁"创建一个对象并将其转换在 3D 空间中。
图 2 PlaneProjection 类
LocalOffset 和GlobalOffset,用于分别转换对象相对于其自身和相对于另一个元素中的全局空间,允许的PlaneProjection 类。RotationX、RotationY 和RotationZ 允许旋转 x 中的元素,y 和 z 轴,CenterOfRotation 允许相对于元素平面的中心点。
在我的示例中,创建"多维数据集,"我将创建四个边的多维数据集并将其移动 3D 空间中通过设置它们的 PlaneProjection 属性,如中所示图 3。
图 3 设置 PlaneProjection 属性
<Grid x:Name="LayoutRoot" Background="White" Width="800" Height="700">
<Rectangle Fill="#9900FF00" Width="250" Height="250" Visibility="Visible">
<Rectangle.Projection>
<PlaneProjection x:Name=
"projectionFront" CenterOfRotationZ="125" RotationX="-180"/>
</Rectangle.Projection>
</Rectangle>
<Rectangle Fill="#99FF0000" Width="250" Height="250" Visibility="Visible">
<Rectangle.Projection>
<PlaneProjection x:Name=
"projectionBottom" CenterOfRotationZ="125" RotationX="-90" />
</Rectangle.Projection>
</Rectangle>
<Rectangle Fill="#990000FF" Width="250" Height="250" Visibility="Visible">
<Rectangle.Projection>
<PlaneProjection x:Name="projectionBack" CenterOfRotationZ="125" />
</Rectangle.Projection>
</Rectangle>
<Rectangle Fill="#99FFFF00" Width="250" Height="250" Visibility="Visible">
<Rectangle.Projection>
<PlaneProjection x:Name=
"projectionTop" CenterOfRotationZ="125" RotationX="90"/>
</Rectangle.Projection>
</Rectangle>
</Grid>
在图 4,侧面旋转 90-90 和创建多维数据集的顶部、 底部和前投影平面-180 度。125 的 CenterofRotationZ 值创建的所有可旋转平面沿 z 轴的中心点。
图 4 投影来模拟 3D 墙边
使用投影平面创建多维数据集后,我需要它旋转 x、 y 和 z 轴。这是在 Silverlight 中使用演示图板对象位置。如中所示创建三个序列图像板,一个用于每个轴, 图 5。
图 5 旋转该多维数据集使用演示图板
<Storyboard x:Name="storyboardRotateX">
<DoubleAnimation Storyboard.TargetName="projectionFront"
Storyboard.TargetProperty="RotationX" From="-180.0" To="180.0" Duration="0:0:10"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="projectionBottom"
Storyboard.TargetProperty="RotationX" From="-90.0" To="270.0" Duration="0:0:10"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="projectionBack"
Storyboard.TargetProperty="RotationX" From="0.0" To="360.0" Duration="0:0:10"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="projectionTop"
Storyboard.TargetProperty="RotationX" From="90.0" To="450.0" Duration="0:0:10"
RepeatBehavior="Forever" />
</Storyboard>
<Storyboard x:Name="storyboardRotateY">
<DoubleAnimation Storyboard.TargetName="projectionFront"
Storyboard.TargetProperty="RotationY" From="0.0" To="360.0" Duration="0:0:10"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="projectionBottom"
Storyboard.TargetProperty="RotationY" From="0.0" To="360.0" Duration="0:0:10"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="projectionBack"
Storyboard.TargetProperty="RotationY" From="0.0" To="360.0" Duration="0:0:10"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="projectionTop"
Storyboard.TargetProperty="RotationY" From="0.0" To="360.0" Duration="0:0:10"
RepeatBehavior="Forever" />
</Storyboard>
<Storyboard x:Name="storyboardRotateZ">
<DoubleAnimation Storyboard.TargetName="projectionFront"
Storyboard.TargetProperty="RotationZ" From="0.0" To="360.0" Duration="0:0:10"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="projectionBottom"
Storyboard.TargetProperty="RotationZ" From="0.0" To="360.0" Duration="0:0:10"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="projectionBack"
Storyboard.TargetProperty="RotationZ" From="0.0" To="360.0" Duration="0:0:10"
RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetName="projectionTop"
Storyboard.TargetProperty="RotationZ" From="0.0" To="360.0" Duration="0:0:10"
RepeatBehavior="Forever" />
</Storyboard>
对每个演示图板,旋转每个四个投影平面以保持不变的多维数据集结构。请注意,为 x 轴旋转,RotationX 值从平面的原始 RotationX 值开始,对于 ProjectionFront,进入另一个 360 度,以使其起点是-180 度和转到 180 度。正如您可以看到在图 6、 多维数据集可用于沿 x、 y 和 z 轴旋转,它可以沿任意轴移动和它支持的每个边的颜色。
图 6 多维数据集、 旋转的准备
在此示例中,我无法轻松地创建多维数据集并将其转换在 3D 空间中没有大量的代码。这是真正的角度来看 3D 转换强度。对于基本的 3D 操作,应使用此选项。但是,它配备有很多缺点。对于高级 3D 对象,投影平面数所需和它们的设置会明显,增加您不必手动计算出每个投影平面和 CenterOfRotation 之间的角度。这里是第二个问题是旋转 3D 对象的依赖序列图像板,这使它大量占用 CPU 的 ; 它不会呈现该对象使用 GPU。使用这种方法的另一个问题是您还呈现多维数据集后,即使它不可见--不是最佳方法。
所需的显示屏幕上的 3D 对象的第三个主要元素是光的效果。在现实生活中您有指示灯,那么,如何执行您模拟,在屏幕上的 3D 空间中?如所述,两个常见的方式来做到这一点是平面阴影和渐变的底纹。
平淡着色考虑的平面曲面,并沿平面的平均底纹设置。渐变底纹 (高氏着色) 使用渐变着色曲面,并考虑到每个平面的顶点。平面不是平面着色、 但而是"平滑"显示为灰色基于不同顶点颜色。
在此示例中,每个平面允许颜色填充 (单层底纹),以及渐变填充 (渐变阴影),它可用于模拟光效果。我将介绍这些更高版本。模拟光源效果的快速方法是通过使用下面的代码后的 3D 对象的透明度与重叠放射状渐变矩形:
<Rectangle x:Name=
"BulbGradient" Height="700" Width="800" Margin="0 50 0 0" Grid.Row="1"
Visibility="Collapsed">
<Rectangle.Fill>
<RadialGradientBrush RadiusX="0.5" RadiusY="0.5" GradientOrigin="0.25,0.25">
<GradientStop Color="#00000000" Offset="0"/>
<GradientStop Color="#FF000000" Offset="2"/>
</RadialGradientBrush>
</Rectangle.Fill>
</Rectangle>
创建多维数据集使用帧
创建 3D 体验的第二个方法是通过使用的最后一帧。在这里,不要创建 3D 对象本身,但启动具有所需的最终输出并将其导出为单个帧。多个 3D 建模软件程序允许您创建 3D 对象和可以为多个帧导出,然后导入到 Silverlight 的转换。
在此示例中,我将采取一个简单的多维数据集的动画,并将其旋转的 x、 y 和 z 轴导出为多个帧的图像。图 7 x 轴上显示的多维数据集旋转八个不同的帧。对于此示例,我使用最少量的帧创建多维数据集的旋转,但每秒的帧创建更平滑、 无缝地旋转。
图 7 八个不同的帧,模拟在 X 轴的旋转
为了模拟在 Silverlight 中的旋转,我使用计时器,如下面的代码所示:
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 500);
timer.Tick += new EventHandler(Tick);
private void Tick(object o, EventArgs sender)
{
string imageuri = "cube/" + axis + "/" + currentImageIndex + ".png";
bgImage.Source = new BitmapImage(new Uri(imageuri, UriKind.RelativeOrAbsolute));
if (currentImageIndex <= 8)
currentImageIndex++;
else
currentImageIndex = 1;
}
请注意这是通过这种方法可以执行哪些操作的简化的版本。这里我为简单起见,使用导出的图像,但多个 3D 软件允许 XAML 导出,使用颜色和渐变的图像,提供无缝、 较平滑的动画而不是创建多边形。此外可以通过带有动画的演示图板替换计时器。
这种方法非常简单。您有特定的转换要求与特定的 3D 对象。您不必担心如何创建 3D 对象,并且这种技术可以用于创建任何种类的 3D 对象 — 您不再限于简单的多维数据集。此方法可用于所有类型的转换。您可以平移、 旋转和缩放复杂的 3D 对象。它甚至模拟光源,直接从建模软件也将转换为它的效果。
这种方法的主要限制是编程的针对 3D 对象的灵活性。一旦您导出您的 3D 对象,它将生成可能很难对代码的静态代码。Silverlight 应用程序中,您无法移动到其他元素的对象。另一个缺点是与每个转换呈线性增加的所需的帧数。分开的呈现发生在 CPU,因此大较大的数值的帧的动画会有性能命中。
这将导致使用 XNA 库即将 Silverlight 5,其中,如您所见,克服了大部分的前两个方法的问题中的第三种方法。但在此之前,我们来讨论一下如何 3D 对象转换 2D 屏幕在数学上 — 3D 背后的天才逻辑。
了解世界、 视图和投影矩阵
若要显示该对象,您必须了解三个主要概念或"空格"和对象如何从其自己的对象空间映射到屏幕:
- World
- 查看
- 投影
图 8 显示在其中一个对象映射到屏幕的顺序。
图 8 中的 3D 对象映射到屏幕的顺序
第一组为 3D 对象的坐标是 x、 y 和 z 坐标 (也称为模型空间) 的对象空间中。这些坐标是相对于另一个具有其中心 (0,0,0)。请记住,与右侧的 3 维笛卡尔坐标,正 z 轴是对准查看者。
3D 多维数据集,正面的右上角将 (1,1,1),并且背面的左下角会 (-1、-1、-1),如中所示图 9。在对象空间坐标是相对于另一个和它们的位置可以仅范围从-1 到 + 1。我需要为要使用的对象空间的 75%我多维数据集,乘以每个协调.75,因此新 b将 (.75,.75,.75) 和新 g(-.75、-.75、-.75) 将会。
图 9 在 3D 空间中的坐标
当将对象放在世界空间上时,对象本身不是移动,但而是相对于世界坐标映射乘以其坐标与世界矩阵。在世界空间中,您可以通过移动来转换该对象的坐标、 更改来调整大小和更改对象来旋转的角度转换 3D 对象。若要表达世界空间中的对象的坐标,您需要进行乘法运算的世界矩阵与每个顶点位置:
对象世界坐标 = 对象坐标 * 世界矩阵
下一个元素是照相机视图,表示要从中查找对象的点。此时可以更改在 3D 空间中,而不更改对象空间,以及世界空间中的实际对象的坐标。若要计算相机视图相对于对象的坐标,乘视图矩阵与对象的世界矩阵:
对象视图坐标 = 世界坐标 * 视图矩阵
最后,在对象视图具有要呈现在屏幕上 ; 这是您需要计算距离由于创建全景视图的位置。到目前为止,我对象以平行投影 (两侧为并行),但我需要将对象显示在透视投影 (边到消失点合并),因此我乘对象的视图矩阵和投影矩阵与世界矩阵的产品:
对象最终坐标 = 世界坐标 * 视图矩阵 * 投影矩阵
这是在屏幕上,也称为 WorldViewProjection 的 3D 对象的最终位置。
矩阵
在 Microsoft.Xna.Framework,矩阵结构包含在 Silverlight 5。它具有与 16 浮点数 4 x 4 同构矩阵作为字段和多种方法来生成一个转换矩阵 (请参阅图 10)。
图 10 先决条件 Silverlight 5 矩阵结构
按列 (M11-M33) 的前三行用于缩放和旋转转换,和第四行 (M41-M43) 用于翻译 (请参阅图 11)。
图 11 4x4 矩阵
M11 | M12 | M13 | M14 |
M21 | M22 | M23 | M24 |
M31 | M32 | M33 | M34 |
M41 | M42 | M43 | M44 |
为了更好地理解矩阵,让我们看到转换中如何使用它。有个矩阵的五种不同类型: 4x4 矩阵结构、 恒等矩阵、 平移矩阵、 小数位数矩阵和旋转矩阵。
恒等矩阵 (请参阅图 12) 为单位矩阵的大小 4,且这成为世界空间中的 3D 对象的原始位置。如果乘恒等矩阵与任何矩阵,您将获得原始矩阵无任何变化。矩阵结构提供了一个简单的属性返回 Matrix.Identity。
图 12 恒等矩阵
1 | 0 | 0 | 0 |
0 | 1 | 0 | 0 |
0 | 0 | 1 | 0 |
0 | 0 | 0 | 1 |
要缩放的矩阵对象,请提供调用 Matrix.CreateScale 方法。缩放矩阵因此用于对 3D 对象的缩放变换乘法运算使用比例矩阵对象时 (请参阅图 13),生成的矩阵相应地调整大小。
图 13 比例矩阵
Sx | 0 | 0 | 0 |
0 | Sy | 0 | 0 |
0 | 0 | Sz | 0 |
0 | 0 | 0 | 1 |
矩阵对象还提供了世界空间中移动对象的 Matrix.CreateTranslate 方法。当转换矩阵相乘 (请参阅图 14),该对象将转换为在世界空间中。
图 14 平移矩阵
1 | 0 | 0 | 0 |
0 | 1 | 0 | 0 |
0 | 0 | 1 | 0 |
Tx | Ty | Tz | 1 |
旋转,有多个方法。Matrix.CreateFromYawPitchRoll 方法用于每个浮点数字值轴进行旋转。Matrix.CreateRotationX、 Matrix.CreateRotationY 和 Matrix.CreateRotationZ 的方法是为旋转对象沿 x、 y 和 z 轴。相对于角度 theta 旋转矩阵涉及 M11 M33 元素,如中所示图 15。
图 15 旋转矩阵,沿 X、 Y 和 Z 轴
1 | 0 | 0 | 0 |
0 | Cos 则 θ | Sin 则 θ | 0 |
0 | -Sin 则 θ | Cos 则 θ | 0 |
0 | 0 | 0 | 1 |
旋转 X |
Cos 则 θ | 0 | Sin 则 θ | 0 |
0 | 1 | 0 | 0 |
-Sin 则 θ | 0 | Cos 则 θ | 0 |
0 | 0 | 0 | 1 |
旋转 Y |
Cos 则 θ | Sin 则 θ | 0 | 0 |
-Sin 则 θ | Cos 则 θ | 0 | 0 |
0 | 0 | 1 | 0 |
0 | 0 | 0 | 1 |
旋转 Z |
学习 Silverlight XNA 3D 管线
与 XNA 库的 Silverlight 5 提供了一个分步过程,呈现在屏幕上的顶点坐标与创建 3D 对象。这可以分为五个主要步骤 (请参阅图 16) 涉及此处显示的组件:
- 顶点缓冲区
- WorldViewProjection 坐标
- 底纹: 顶点、 像素和纹理
- 图形处理: 栅格化,剪辑,然后挑选
- 最终输出: 帧缓存器
图 16 与 Silverlight 5 XNA 库创建 3D 对象
我将简要介绍每个步骤和其组件。
顶点缓冲区在从顶点集合创建的顶点缓冲区,第一步是使用一组顶点来创建 3D 对象的骨架。每个顶点至少包含 x、 y 和 z 坐标,但通常也有其他属性 (如颜色和纹理。此集合的顶点然后用于创建顶点缓冲区,从而将转到该进程的下一步。
WorldViewProjection 坐标乘以使用世界、 视图和投影矩阵的顶点来计算最终的坐标。在这里,计算与世界空间、 视图和投影的对象的关系,并应用。检查最后两部分的更多详细信息。一旦您具有最终的坐标,实际的底纹过程将会发生。
底纹使用顶点阴影、 像素阴影和纹理底纹。在此步骤中,完成第一个顶点着色,然后逐像素底纹的发生情况。在此步骤中,也会应用纹理底纹。这些最终的着色的坐标则用于创建帧缓存器。
栅格化,剪辑,然后挑选在过程中栅格化图像被转换为像素为单位),然后剪辑和精选被用来删除 object,连同隐藏的和不可见图层的骨架。这是最后呈现在屏幕上。
帧缓冲区图像栅格化后,修剪和 culled,帧缓存器生成的这再发送到屏幕的显示。
创建多维数据集使用基元
与矩阵、 世界、 视图、 投影和 3D 管道在 Silverlight 5 XNA 库的知识,让我们创建一个 3D 的多维数据集,请参阅如何一切汇集一起。
此方法的最重要优势是 GPU 加速,这使硬件加速,释放 CPU 从呈现 3D 对象。这是由 EnableGPUAcceleration 参数设置为 true,则用来配置 Silverlight 插件,如下所示的 HTML 中的对象标记中启用:
<object data="data:application/x-silverlight-2,"
type="application/x-silverlight-2" width="100%" height="100%">
<param name="EnableGPUAcceleration" value="true" />
<param name="source" value="ClientBin/Cube3d.xap"/>
<param name="minRuntimeVersion" value="5.0.60211.0" />
</object>
在 XAML 中,我将添加一个 DrawingSurface 对象,对象内部的网格,用于呈现在 Silverlight 中的 3D 对象的 GraphicsDevice DrawPrimitives 方法的帮助 (请参阅图 17):
<DrawingSurface Loaded="OnLoad" SizeChanged="OnSizeChanged" Draw="OnDraw"/>
图 17 GraphicsDevice 类的 DrawPrimitives 方法
我将在 DrawingSurface 类中使用三种方法,可以创建和呈现该多维数据集。OnLoad 方法用于创建多维数据集并初始化所有着色器和视图矩阵,不会更改此应用程序中。请注意 3D 对象居中对齐 (0,0,0) 在使用对象空间的 75%--从 (.75,.75,.75) 的坐标 (-.75、-.75、-.75)。在这里,我将创建顶点缓冲区容纳顶点集合和初始化的 shaderStream、 pixelStream 和 imageStream 流中,将所有使用稍后在底纹步骤。我还将初始化视图矩阵,它是在使用 cameraPosition 和 cameraTarget 以及 Vector3.Up (这意味着照相机正在向上查找) 的参数的对象正在查找摄像头的角度。此代码如图 18 所示。
图 18 设置 shaderStream、 pixelStream 和 imageStream
VertexBuffer vertexBuffer;
VertexShader vertexShader;
PixelShader pixelShader;
Texture2D texture;
private void OnLoad(object sender, RoutedEventArgs e)
{
vertexBuffer = CreateCube();
Stream shaderStream = Application.GetResourceStream(new
Uri(@"Cube3d;component/shader/shader.vs", UriKind.Relative)).Stream;
vertexShader = VertexShader.FromStream(resourceDevice, shaderStream);
Stream pixelStream = Application.GetResourceStream(new
Uri(@"Cube3d;component/shader/shader.ps", UriKind.Relative)).Stream;
pixelShader = PixelShader.FromStream(resourceDevice, pixelStream);
Stream imageStream = Application.GetResourceStream(new
Uri(@"Cube3d;component/scene.jpg",
UriKind.Relative)).Stream;
var image = new BitmapImage();
image.SetSource(imageStream);
texture = new Texture2D(resourceDevice, image.PixelWidth,
image.PixelHeight, false, SurfaceFormat.Color);
image.CopyTo(texture);
Vector3 cameraPosition = new Vector3(0, 0, 5.0f);
Vector3 cameraTarget = Vector3.Zero;
view = Matrix.CreateLookAt(cameraPosition, cameraTarget, Vector3.Up);
}
下一步是创建 3D 多维数据集的顶点缓冲区。我将创建一个 CreateCube 方法,如中所示图 19 VertexBuffer 返回的。我将在 3D 空间中,创建两个矩形,具有 ABCD 组成的多维数据集和 EFGH 正面背面的多维数据集。我使用的 VertexPositionColor 结构创建的顶点和与每个相关的颜色集合。
图 19 CreateCube 方法中创建的 VertexBuffer
VertexBuffer CreateCube()
{
var vertexCollection = new VertexPositionColor[36];
// Front coordinates
Vector3 cubeA = new Vector3(-0.75f, 0.75f, 0.75f);
Vector3 cubeB = new Vector3(0.75f, 0.75f, 0.75f);
Vector3 cubeC = new Vector3(-0.75f, -0.75f, 0.75f);
Vector3 cubeD = new Vector3(0.75f, -0.75f, 0.75f);
// Back coordinates
Vector3 cubeE = new Vector3(-0.75f, 0.75f, -0.75f);
Vector3 cubeF = new Vector3(0.75f, 0.75f, -0.75f);
Vector3 cubeG = new Vector3(-0.75f, -0.75f, -0.75f);
Vector3 cubeH = new Vector3(0.75f, -0.75f, -0.75f);
// Colors
Color cRed = Color.FromNonPremultiplied(255, 0, 0, 156);
Color cGreen = Color.FromNonPremultiplied(0, 255, 0, 156);
Color cBlue = Color.FromNonPremultiplied(0, 0, 255, 156);
Color cYellow = Color.FromNonPremultiplied(255, 255, 0, 156);
Color cBlack = Color.FromNonPremultiplied(0, 0, 0, 156);
Color cWhite = Color.FromNonPremultiplied(255, 255, 255, 156);
// Front
vertexCollection[0] = new VertexPositionColor(cubeA, cGreen);
vertexCollection[1] = new VertexPositionColor(cubeB, cGreen);
vertexCollection[2] = new VertexPositionColor(cubeC, cGreen);
vertexCollection[3] = new VertexPositionColor(cubeB, cBlue);
vertexCollection[4] = new VertexPositionColor(cubeD, cBlue);
vertexCollection[5] = new VertexPositionColor(cubeC, cBlue);
// Back
vertexCollection[6] = new VertexPositionColor(cubeG, cBlue);
vertexCollection[7] = new VertexPositionColor(cubeF, cBlue);
vertexCollection[8] = new VertexPositionColor(cubeE, cBlue);
vertexCollection[9] = new VertexPositionColor(cubeH, cGreen);
vertexCollection[10] = new VertexPositionColor(cubeF, cGreen);
vertexCollection[11] = new VertexPositionColor(cubeG, cGreen);
// Top
vertexCollection[12] = new VertexPositionColor(cubeE, cRed);
vertexCollection[13] = new VertexPositionColor(cubeF, cRed);
vertexCollection[14] = new VertexPositionColor(cubeA, cRed);
vertexCollection[15] = new VertexPositionColor(cubeF, cYellow);
vertexCollection[16] = new VertexPositionColor(cubeB, cYellow);
vertexCollection[17] = new VertexPositionColor(cubeA, cYellow);
// Bottom
vertexCollection[18] = new VertexPositionColor(cubeH, cRed);
vertexCollection[19] = new VertexPositionColor(cubeG, cRed);
vertexCollection[20] = new VertexPositionColor(cubeC, cRed);
vertexCollection[21] = new VertexPositionColor(cubeD, cYellow);
vertexCollection[22] = new VertexPositionColor(cubeH, cYellow);
vertexCollection[23] = new VertexPositionColor(cubeC, cYellow);
// Left
vertexCollection[24] = new VertexPositionColor(cubeC, cBlack);
vertexCollection[25] = new VertexPositionColor(cubeG, cBlack);
vertexCollection[26] = new VertexPositionColor(cubeA, cBlack);
vertexCollection[27] = new VertexPositionColor(cubeA, cWhite);
vertexCollection[28] = new VertexPositionColor(cubeG, cWhite);
vertexCollection[29] = new VertexPositionColor(cubeE, cWhite);
// Right
vertexCollection[30] = new VertexPositionColor(cubeH, cWhite);
vertexCollection[31] = new VertexPositionColor(cubeD, cWhite);
vertexCollection[32] = new VertexPositionColor(cubeB, cWhite);
vertexCollection[33] = new VertexPositionColor(cubeH, cBlack);
vertexCollection[34] = new VertexPositionColor(cubeB, cBlack);
vertexCollection[35] = new VertexPositionColor(cubeF, cBlack);
var vb = new VertexBuffer(resourceDevice,
VertexPositionColor.VertexDeclaration,
vertexCollection.Length, BufferUsage.WriteOnly);
vb.SetData(0, vertexCollection, 0, vertexCollection.Length, 0);
return vb;
}
绘制表面的 OnSizeChanged 方法用于更新投影和屏幕表面的维度所基于的长宽比:
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
DrawingSurface surface = sender as DrawingSurface;
float sceneAspectRatio = (float)surface.ActualWidth / (float)surface.ActualHeight
projection = Matrix.CreatePerspectiveFieldOfView
(MathHelper.PiOver4, sceneAspectRatio, 1.0f, 100.0f);
}
最后一个方法是 OnDraw,允许动态转换 3D 多维数据集上。这是我应用 Matrix.CreateScale 调整多维数据集,以将其旋转的 Matrix.CreateFromYawPitchRoll 和 Matrix.CreateTranslate 以将其移动到的位置。在这里,worldViewProjection 计算并发送给 vertexShader 方法为底纹顶点,后者反过来将传送到 pixelShader 着色的多维数据集"墙壁"。这反过来也可以发送到 textureShader,它可以基于图像。最后,GraphicDevice 对象调用 DrawPrimitives 方法,以呈现输出帧中,如中所示图 20。
图 20 调用 DrawPrimitives 方法来呈现输出帧
void OnDraw(object sender, DrawEventArgs args)
{
Matrix position = Matrix.Identity;
Matrix scale = Matrix.CreateScale(1.0f);
float xf = 0.0f; float yf = 0.0f; float zf = 0.0f;
if (cubeXAxis) xf = MathHelper.PiOver4 * (float)args.TotalTime.TotalSeconds;
if (cubeYAxis) yf = MathHelper.PiOver4 * (float)args.TotalTime.TotalSeconds;
if (cubeZAxis) zf = MathHelper.PiOver4 * (float)args.TotalTime.TotalSeconds;
Matrix rotation = Matrix.CreateFromYawPitchRoll(xf, yf, zf);
Matrix world;
if (translateZ != 0)
world = rotation * Matrix.CreateTranslation(0, 0, (float)translateZ);
else
world = scale * rotation * position;
// Calculate the final coordinates to pass to the shader.
Matrix worldViewProjection = world * view * projection;
args.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer,
new Microsoft.Xna.Framework.Color(0, 0, 0, 0), 10.0f, 0);
// Set up vertex pipeline.
args.GraphicsDevice.SetVertexBuffer(vertexBuffer);
args.GraphicsDevice.SetVertexShader(vertexShader);
args.GraphicsDevice.SetVertexShaderConstantFloat4(0, ref worldViewProjection);
// Set up pixel pipeline.
args.GraphicsDevice.SetPixelShader(pixelShader);
args.GraphicsDevice.Textures[0] = texture;
args.GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, 12);
args.InvalidateSurface();
}
此动态呈现的绘图图面上的最后一个 3D 多维数据集 (请参阅图 21)。
图 21 绘图图面上的最后一个三维多维数据集
这种方法使用 GPU 加速,并不会呈现隐藏的 3D 对象的侧面或背面,并与我的第一种方法不同。这还使用帧缓冲区内存中呈现最终的输出类似于第二种方法,但具有完全控制和针对对象,编程的灵活性与 OnDraw 方法一样。
现在,您已经了解在 Silverlight,每一个都有其自己的优势和劣势中创建 3D 对象的三种不同的方法。