增强现实 - XNA 模型





5.00/5 (8投票s)
可通过照相机查看的地理坐标定位的 Xna 模型。
引言
该应用结合了运动 API、Geocoordinatewatcher、Xna 3D 模型和 Silverlight 的 PhotoCamera 类。
放置你的模型,校准手机,然后四处走动,从各个侧面和角度欣赏你的 3D 作品……
这些技术都不是什么真正新的东西。它们只是以一种可以轻松为我们(希望很快就能看到)提供一些有趣应用起点的方式组合在一起。
背景
在一些古老的遗址中漫步,通过手机看到那个辉煌时期它会是什么样子的,这难道不好吗?或者,在这个例子中,在空中漂浮着一些茶壶,营造出一种诡异的感觉……但说实话,它们看起来有些不合时宜,而且脱离了上下文。一如既往,天空是极限,可能性是无限的……
先决条件
至少你需要一种带有 Windows Phone 软件开发工具包 (SDK 7.1) 的 Visual Studio 2010。
如何操作……
你首先从模板“Windows Phone Silverlight and XNA application”创建一个新项目。你可以将你的项目命名为 SlXnaGeoMotion、SlXnaTeapots 或其他任何名称。
如果你不打算使用自己的 *.fbx 或 *.x 类型的 3D 模型,则可以删除模板创建的两个额外的“SlnaGeoMotionContent”和“SlXnaGeoMotionLib”项目。请务必从剩余的项目中删除对“SlXnaGeoMotionLib”的引用。
创建一个名为 Primitives3D 的目录,并添加现有的项目,可以从附加的 SlXnaGeoMotion.zip 文件或从
http://xbox.create.msdn.com/en-US/education/catalog/sample/primitives_3d
你将使用这些来稍后创建你的飞行茶壶或任何你想要的 3D 几何 XNA 模型。
现在,在这个小项目中,我们唯一还会关注的文件是 GamPage.xaml 及其“代码隐藏”GamePage.xaml.cs。
XAML
让我们从 xaml 开始
<Grid x:Name="LayoutRoot" Background="Transparent"> <Canvas x:Name="viewfinderCanvas" Height="800" Margin="-110,0,-110,0"> <Canvas.Background> <VideoBrush x:Name="videoBrush"> <VideoBrush.RelativeTransform> <CompositeTransform x:Name="viewfinderTransform" Rotation="90" CenterX="0.5" CenterY="0.5"> </CompositeTransform> </VideoBrush.RelativeTransform> </VideoBrush> </Canvas.Background> </Canvas> <TextBlock x:Name="Info" Text="" Style="{StaticResource PhoneTextLargeStyle}" Grid.Row="0"> </TextBlock> </Grid>
使用 VideoBrush 将 Microsoft.Devices.PhotoCamera 类中的输出显示在容器类型的控件中,这是从 Windows Phone 7.5 Mango 设备显示相机输出的常用且首选的方式。
这里需要注意的是 90 度旋转,以便以纵向模式查看。您还应该注意到,我们使用了 -110 的左右边距,这将使我们的 Canvas(命名为 viewfinderCanvas)的总宽度为 110 + 480 + 110 = 600。这给了我们 600 / 800 或 3/4 的纵横比,这正是我们手机中物理摄像头实际提供的。因此,我们将保持所见真实物体比例的正确性。
关于 xaml 方面,我们只需要做这么多。
C# - 代码隐藏
我喜欢删除所有解释显而易见内容的注释,并将模板生成的语句(以区域的形式)隔离出来。每次实现新功能时,将有关不同主题的语句分组在不同的“#region”标签内也很有用。
在此程序中,主要主题是
- Silverlight
- PhotoCamera
- Motion(运动)
- 3D
- GeoCoordinateWatcher
我将尝试按照相同的顺序解释这里发生的情况,您可以在附加的 zip 文件中的源代码中引用实际的“#region”。
Silverlight
为了在 Xna 中渲染 Silverlight,我们必须首先定义并实例化一个 Microsoft.Xna.Framework.Graphics.UIElementRenderer。
UIElementRenderer silverlightRenderer; silverlightRenderer = new UIElementRenderer(this, (int)ActualWidth, (int)ActualHeight);
如果您在程序的“Ondraw”部分渲染并绘制此对象作为 Xna Sprite,您应该能够将现实世界显示为 Xna 世界的背景。
spriteBatch.Begin(); silverlightRenderer.Render(); spriteBatch.Draw(silverlightRenderer.Texture, Vector2.Zero, Color.White); spriteBatch.End();
对于我们这里使用的简单 3D 模型,接下来的语句几乎没有必要,但您应该习惯性地将它们包含进去。
SharedGraphicsDeviceManager.Current.GraphicsDevice.BlendState = BlendState.Opaque; SharedGraphicsDeviceManager.Current.GraphicsDevice.DepthStencilState = DepthStencilState.Default; SharedGraphicsDeviceManager.Current.GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
这将重新配置您的 GraphicsDeviceManager,以便更好地处理更高级的“蒙皮”3D 纹理模型,这些模型通常使用 Autocad、Maya 或 Blender 等工具创建。
PhotoCamera
这里没有什么特别的
PhotoCamera photoCamera; photoCamera = new PhotoCamera(); videoBrush.SetSource(photoCamera);
这里最后的两行位于“OnNavigatedTo”中的“#region Silverlight”,抱歉,最后一行是我们在 GamePage.xaml 中的 xaml 代码中定义的并实例化的“photoCamera”被设置为“viewfinderCanvas”的 VideoBrush“videoBrush”的源。
运动
WP Mango 中一个“新”类,主要处理您如何握持相机、观察方向、纵向、横向等,基于您手机的罗盘、加速度计、可能的陀螺仪等。它提供关于 X、Y、Z 轴旋转的信息,以弧度或四元数表示,或者如本项目中那样以矩阵表示。还提供有关加速度的信息,但在此项目中并未考虑。
Motion motion; motion = new Motion(); motion.TimeBetweenUpdates = timer.UpdateInterval; motion.Start();
提供对此的访问
motion.CurrentValue.Attitude.RotationMatrix;
只需从“Microsoft.Devices.Sensors.Motion”实例化一个 motion,启动它,然后就可以通过使用 Motion 的“CurrentValue”中的“Attitude”来开始。有些人更喜欢连接 CurrentValueChanged 事件,但在这种简单的例子中,我并没有真正看到这样做的意义。
3D
初始化
TeapotPrimitive teapot; Matrix teapotWorld; Matrix projection; Matrix view; teapot = new TeapotPrimitive( SharedGraphicsDeviceManager.Current.GraphicsDevice);
从你的 Primitives3D TeaPotPrimitive 类创建茶壶。请确保在你的 GamePage.xaml.cs 文件中声明你正在“使用 Primitives3D”。对于那些没有注意到的人。使用茶壶作为例子,当然是为了故意调侃所有 Java 爱好者。 " src="https://codeproject.org.cn/script/Forums/Images/smiley_smile.gif" />
更新或绘制部分
view = Matrix.CreateRotationX(MathHelper.PiOver2); view *= motion.CurrentValue.Attitude.RotationMatrix; view *= Matrix.CreateLookAt(Vector3.Zero, new Vector3(0.0f, 0.0f, -0.001f), Vector3.Up); projection = Matrix.CreatePerspectiveFieldOfView( 1, SharedGraphicsDeviceManager.Current.GraphicsDevice.Viewport.AspectRatio, 0.1f, 100f);
首先,您需要用真实世界的视图定向您的 XNA 世界。这是通过围绕 X 轴将您的 XNA 世界视图旋转 90 度来完成的。真实世界的 Y 轴(海拔)和 XNA 世界的 Y 轴现在都指向天空。
这时 Motion API 就派上用场了。投影矩阵的创建应该真正移到 OnNavigatedTo 事件中,以节省游戏循环的执行时间。(不需要不断更新。)
茶壶
teapotWorld = Matrix.CreateScale(3.0f) * Matrix.CreateFromYawPitchRoll( MathHelper.ToRadians(0), MathHelper.ToRadians(0), MathHelper.ToRadians(0)) * Matrix.CreateTranslation(new Vector3( (float)PositionChangeX + 0.0f, 0.0f, (float)(PositionChangeZ - 5.0f))); teapot.Draw(teapotWorld, view, projection, Color.Red);
也许有点难以消化,但请慢慢来,您应该会轻松应对。
Matrix teapotWorld 用于操作此 Model 在您的 XNA 世界中的位置。
您可以根据需要缩放、旋转和翻译(定位)您的茶壶。请记住,这只能以缩放、旋转、翻译的顺序进行。先旋转、再翻译然后缩放可能会导致意外结果。矩阵乘法在这方面不像普通乘法。
永远不要使用以 0 作为参数的 CreateScale Matrix 并期望在您的视图中看到任何东西。
如果您想保持模型的当前尺寸,则使用 1。(或者在这种情况下,您可以完全省略此矩阵)
在 Matrix.CreateFromYawPitchRoll(决定茶壶的哪一面朝向您)中,我喜欢使用 MathHelper.ToRadians,因为我总是很难想到弧度。我发现用旋转度来思考更容易。
CreateTranslation 部分使用了变量 PositionChangeX 和 PositionChangeZ,我将在“GeoCoordinateWatcher”->“Position Change”部分再讨论它们。
GeoCoordinateWatcher
初始化
const double MeterInLatitude = 111290.91975341858; const double MeterInLongitude = 55729.5173743959; double PositionChangeX = 0; double PositionChangeZ = 0; GeoCoordinateWatcher geoWatcher; GeoPositionStatus currentState = GeoPositionStatus.Initializing; public GeoPosition<GeoCoordinate> InitialGeoCoordinate { get; set; } public GeoPosition<GeoCoordinate> CurrentGeoCoordinate { get; set; }
我们将在“Points Of Interest”部分讨论“MeterInLatitude”和“MeterInLongitude”。
实例化
geoWatcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High); geoWatcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs> (geoWatcher_StatusChanged); geoWatcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>> (geoWatcher_PositionChanged); geoWatcher.MovementThreshold = 0.0; geoWatcher.Start();
连接了两个事件,然后启动了该设备……
状态
void geoWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e) { currentState = e.Status; switch (e.Status) { case GeoPositionStatus.Disabled: if (geoWatcher.Permission == GeoPositionPermission.Denied) { string[] strings = { "ok" }; MessageBox.Show("Please turn on geo-location service in the settings tab."); } else if (geoWatcher.Permission == GeoPositionPermission.Granted) { string[] strings = { "ok" }; MessageBox.Show("Your device doesn't support geo-location service."); } break; case GeoPositionStatus.Ready: InitialGeoCoordinate = geoWatcher.Position; break; } }
记录下在冒险开始时您在世界上的位置……
位置变化
void geoWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) { if (currentState == GeoPositionStatus.Ready) { CurrentGeoCoordinate = e.Position; PositionChangeZ = (CurrentGeoCoordinate.Location.Latitude - InitialGeoCoordinate.Location.Latitude) * MeterInLatitude; PositionChangeX = (InitialGeoCoordinate.Location.Longitude - CurrentGeoCoordinate.Location.Longitude) * MeterInLongitude; } }
每次 PositionChanged 事件触发时,我们都会估算您在经度(X)和纬度(Z)位置上移动了多少米。海拔(Y)位置目前尚未考虑在内。尽管“Location”有一个“Altitude”属性,但目前您的硬件几乎无法利用它。
关注点
常量
GeoCoordinate GeoCoordinate1 = new GeoCoordinate(59.949702262878418, 10.763088226318359); double meterInLatitude = GeoCoordinate1.GetDistanceTo( new GeoCoordinate(60.949702262878418, 10.763088226318359)); // 1 Latitude degree = 111290.91975341858 meter double meterInLongitude = GeoCoordinate1.GetDistanceTo( new GeoCoordinate(59.949702262878418, 11.763088226318359)); // 1 Longitude degree = 55729.5173743959 meter
我通过使用“GeoCoordinate”的“GetDistanceTo”函数来估算了常数“MeterInLongitude”和“MeterInLatitude”的值。只需检查在同一纬度上经度相差 1 的两个 GeoCoordintes 之间的距离,反之亦然。
动画
通过在估算 teapotWorld 时使用 GameTimerEventArgs 的 Totaltime 乘以某个因子来更新 CreateFromYawPitchRoll 的 Yaw(绕 Y 轴)部分,可以让您的茶壶旋转起来并不难。事实上,一项有趣的挑战可能是实时动画日本“子弹”列车新干线的模型,当它从东京移动到长崎时。(开玩笑的)。
世界中的固定位置
如果您有一个模型,比如埃菲尔铁塔,即使您本人在乌拉圭,您也总是希望它位于巴黎,那么您应该计算自己的特定“EiffelTowerPositionChangeX”和“EiffelTowerPositionChangeZ”。这基于铁塔的实际地理坐标(而不是使用“InitialGeoCoordinate”),并在您的 EiffelTowerWorld 中使用这些新变量。当然,如果您是法国人并且住在巴黎,您就不会让您的小模型遮挡住这个“壮丽”地标的真实视野。
警告
当您在户外(GeoCoordinateWatcher 在开阔地带效果最好)寻找飞行茶壶时,请注意周围的交通。不是所有东西都是“增强”的,您知道的。
而且四处奔跑,挥舞着手机,试图看到那些并不真正存在的东西,可能会让您的同事认为您有点疯……
非常欢迎关于如何改进功能的评论……
祝您好运,玩得开心,用飞行的茶壶装饰世界……
历史
创建于 2012 年 8 月
附加图片和 zip 文件下载……