在3D世界中创建不同的相机模式——检查对象是否可见
2010-10-18 21:32
465 查看
2.5 检查对象是否可见
问题 你想检查对象是否可见来决定是否要绘制这个物体。解决方案 XNA拥有BoundingFrustum类支持这个功能,你可以通过传递View和Projection矩阵创建一个BoundingFrustum类,然后就可以很容易地在这个类中检查对象是否包含在视锥体中。
工作原理 视锥体定义了相机可以看到的范围,类似于削去顶部的金字塔,如图2-4所示,它的边是由视域角,近裁平面和远裁平面决定的,更多的信息可见教程2-1。
你应该让XNA只绘制包含在视锥体中的物体,否则,你会浪费显卡的处理能力。所以你需要一个方法用来检测对象是否在视锥体中。
XNA Framework中的BoundingFrustum类包含了实现这个功能完整方法,你只需通过指定相机的观察矩阵和投影矩阵创建BoundingFrustum类即可。
图2-4 相机的视锥体
BoundingFrustum对象允许你使用它的Contain方法,你可以传递一个Vector3和一个BoundingSphere或BoundingBox检查是否包含在相机的视锥中。这个方法会返回一个Containment Type对象,这个对象包含三个值:
Contains:完整包含在视锥体中的测试对象。
Intersects:部分包含在视锥体中的测试对象,这时这个对象与视锥体相交。
Disjoint:不在视锥体中的测试对象。
检查3D空间中的点是否在相机可视范围内 要检查一个点是否可见,只需将这个点传递到Contains方法:
Vector3 pointToTest = new Vector3(0, 0, 0); BoundingFrustum cameraFrustum = new BoundingFrustum(fpsCam.ViewMatrix * fpsCam.ProjectionMatrix); ContainmentType containmentType = cameraFrustum.Contains(pointToTest); if (containmentType != ContainmentType.Disjoint) { Window.Title = "Point inside frustrum"; graphics.GraphicsDevice.Clear(Color.CornflowerBlue); } else { Window.Title = "Point outside frustrum"; graphics.GraphicsDevice.Clear(Color.Red); }
这个例子中你检查了点(0,0,0)是否在视锥体中,将检查结果写在了窗口标题栏中并相应地改变背景颜色。
检查一个物体是否可见
如果你想检查一个物体是否可见,一个方法是事先检查模型的每个顶点,这样做非常耗时。更快的方法(尽管不太确切)是定义一个包含模型的包围体,然后检查这个包围体是否在视锥体中。能包含模型的最简单的包围体是一个球。
XNA已在BoundingSphere类中支持创建一个包围体,使用教程4-5中介绍的LoadModelWithBoundingSphere方法载入模型,并在Tag属性中保存BoundingSphere:
myModel = XNAUtils.LoadModelWithBoundingSphere(ref modelTransforms, "content/tiny", content);
技巧:因为模型的Tag属性可以存储任何类型的数据结构,你可以用让它存储任何与模型相关的数据,例如BoundingSphere,纹理等。你甚至还可以创建一个包含所有数据的结构并将这个结构存储在Tag属性中。
现在有了包围体你就可以测试它是否在视锥体中了:
BoundingFrustum cameraSight = new BoundingFrustum(fpsCam.ViewMatrix* fpsCam.ProjectionMatrix); ContainmentType containmentType = cameraSight.Contains( (BoundingSphere)myModel.Tag); if (containmentType != ContainmentType.Disjoint) { Window.Title = "Point inside frustrum"; graphics.GraphicsDevice.Clear(Color.CornflowerBlue); Window.Title = "Point outside frustrum"; graphics.GraphicsDevice.Clear(Color.Red); }
检查一个设置了世界矩阵的对象在大多数情况中,你的模型并不在(0,0,0)位置而且还可能旋转,缩放或通过设置世界矩阵放置在其他位置,更多信息可见教程4-2。显然包围体也应该旋转/缩放/移动已匹配模型。这可以通过模型的世界矩阵变换包围体实现:
Matrix worldMatrix = Matrix.CreateScale(0.01f, 0.01f, 0.01f) *Matrix.CreateTranslation(5, 0, 0); BoundingFrustum cameraSight = new BoundingFrustum(fpsCam.ViewMatrix * fpsCam.ProjectionMatrix); BoundingSphere origSphere = (BoundingSphere)myModel.Tag; BoundingSphere transSphere = origSphere.Transform(worldMatrix); ContainmentType containmentType = cameraSight.Contains(transSphere); if (containmentType != ContainmentType.Disjoint) { Window.Title = "Model inside frustrum"; graphics.GraphicsDevice.Clear(Color.CornflowerBlue); myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); foreach (ModelMesh mesh in myModel.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.EnableDefaultLighting(); effect.World = modelTransforms[mesh.ParentBone.Index] * worldMatrix; effect.View = fpsCam.ViewMatrix; effect.Projection = fpsCam.ProjectionMatrix; } mesh.Draw(); } Window.Title = "Model outside frustrum"; graphics.GraphicsDevice.Clear(Color.Red); }
注意可见教程第三章学习如何绘制一个模型。这个代码中的主要变化是你调用了origSphere. Transform方法,这个方法获取经过世界变换的BoundingSphere。本例中transSphere的大小是origSphere百分之一,中心沿x轴移动了5个单位。只有当模型在视锥体时才会绘制这个模型。
代码
下面的代码会在一个指定位置绘制一个模型,当它在视锥体之外就不会被绘制:
protected override void LoadContent() { device = graphics.GraphicsDevice; basicEffect = new BasicEffect(device, null); cCross = new CoordCross(device); myModel = XNAUtils.LoadModelWithBoundingSphere(ref modelTransforms,"content/tiny", content); } protected override void Draw(GameTime gameTime) { Matrix worldMatrix = Matrix.CreateScale(0.01f,0.01f,0.01f)*Matrix.CreateTranslation(5, 0, 0); BoundingFrustum cameraSight = new BoundingFrustum(fpsCam.ViewMatrix * fpsCam.ProjectionMatrix); BoundingSphere origSphere = (BoundingSphere)myModel.Tag; BoundingSphere transSphere = origSphere.Transform(worldMatrix); ContainmentType containmentType = cameraSight.Contains(transSphere); if (containmentType != ContainmentType.Disjoint) { Window.Title = "Model inside frustrum"; graphics.GraphicsDevice.Clear(Color.CornflowerBlue); //draw model myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); foreach (ModelMesh mesh in myModel.Meshes) { foreach (BasicEffect effect in mesh.Effects) { effect.EnableDefaultLighting(); effect.World = modelTransforms[mesh.ParentBone.Index] * worldMatrix; effect.View = fpsCam.ViewMatrix; effect.Projection = fpsCam.ProjectionMatrix; } mesh.Draw(); } } else { Window.Title = "Model outside frustrum"; graphics.GraphicsDevice.Clear(Color.Red); } //draw coordcross cCross.Draw(fpsCam.ViewMatrix, fpsCam.ProjectionMatrix); base.Draw(gameTime); }
扩展阅读
这个教程提出了一个检查点或物体是否在视锥体中的方法。但是如果场景中有大量的物体,那么要对每个对象进行检查会使程序变得很慢。在教程2-9会介绍一个更好的方法处理大场景。
相关文章推荐
- 在3D世界中创建不同的相机模式——编写自定义内容导入器
- 在3D世界中创建不同的相机模式——指定相机的目标
- 在3D世界中创建不同的相机模式——检测相机与模型,墙或地形的碰撞
- 在3D世界中创建不同的相机模式——创建一个Freelancer风格的相机:使用四元数的3D旋转
- 在3D世界中创建不同的相机模式——创建一个相机飞入效果
- 在3D世界中创建不同的相机模式——创建一个第一人称射击游戏(FPS)的相机:Quake风格的相机
- 在3D世界中创建不同的相机模式——天空盒
- 在3D世界中创建不同的相机模式——只绘制在相机视野中的物体:八叉树
- 在3D世界中创建不同的相机模式——使用四叉树隐藏不在视野中的部分网格
- 在3D世界中创建不同的相机模式——创建一个ROAM地形
- 在3D世界中创建不同的相机模式——创建一个Post-Processing Framework
- 在3D世界中创建不同的相机模式——创建一个相机:Position,Target和View Frustum
- 在3D世界中创建不同的相机模式——创建一个模糊(Blur),(发光)Glow Post-Processing Effect
- 设计技巧20:建造模式:Builder 分不同的步骤创建复杂的对象,支持可变参数
- 无法创建XMLHTTP对象,请检查是否安装了MS XML Parser运行库
- 自己在项目中的学习总结:利用工厂模式+反射机制+缓存机制,实现动态创建不同的数据层对象接口
- 无法创建DOMDocument对象,请检查是否安装了MS XML Parser 运行库!
- JavaScript模式读书笔记 第5章 对象创建模式
- javascript组合模式创建对象
- 对象的创建:工厂模式/构造函数模式/原型模式 (笔记)