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会介绍一个更好的方法处理大场景。

转载于:https://www.cnblogs.com/AlexCheng/archive/2010/10/18/2120172.html

在3D世界中创建不同的相机模式——检查对象是否可见相关推荐

  1. ROS机器人程序设计(原书第2版)3.9.1 使用rqt_rviz在3D世界中实现数据可视化

    3.9.1 使用rqt_rviz在3D世界中实现数据可视化 在roscore运行时,启动rqt_rviz(请注意rviz在ROS hydro中依然有效): 我们将会看到如下图所示的图形化工作界面: 在 ...

  2. 在3D世界中的获取鼠标的位置

    原理 电脑的鼠标是在屏幕的2D坐标上运动的,而我们要获取的是3D世界中的一个三维坐标,在游戏引擎中的实现原理如下: 先获取鼠标在屏幕上的2D坐标. 结合摄像机平面计算出这个点在3D世界中的坐标. 从这 ...

  3. ArcGIS10.6中,在3D分析工具中创建视线之后,怎么将其删除?

    如下图所示, 为3D分析工具条: 在3D分析中创建的线,例如做剖面图是插入的线,该怎样删除呢? 如下图所示. 删除方法是,先使用如下选择工具,然后双击线条,再按delete键.

  4. 第1部分: 游戏引擎介绍, 渲染和构造3D世界

    原文作者:Jake Simpson 译者: 向海 Email:GameWorldChina@myway.com  ------------------------------------------- ...

  5. NeHe OpenGL第十课:3D世界

    NeHe OpenGL第十课:3D世界 加载3D世界,并在其中漫游: 在这一课中,你将学会如何加载3D世界,并在3D世界中漫游.这一课使用第一课的代码,当然在课程说明中我只介绍改变了代码. 这一课是由 ...

  6. NeHe OpenGL教程 第十课:3D世界

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  7. 多目立体匹配的前世今生 | 聊一聊MVS及其在3D检测中的应用

    点击下方卡片,关注"自动驾驶之心"公众号 ADAS巨卷干货,即可获取 点击进入→自动驾驶之心技术交流群 后台回复[数据集下载]获取计算机视觉近30种数据集! 学习资料: GitHu ...

  8. Qt OpenGL 加载3D世界,并在其中漫游

    这次教程中,我将教大家如何加载一个3D世界,并在3D世界中漫游.这相较于我们只能创造一个旋转的立方体或一群星星时有很大的进步了,当然这节课代码难度不低,但也不会很难,只要你跟着我慢慢一步一步来. 一个 ...

  9. 虚拟世界由此开始 追逐3D世界的脚步zz

    十一年前,我们在争论究竟是256色画面好还是16位色彩深度画面好.十一年后的今天,我们争论的是超采样抗锯齿好还是多重采样抗锯齿好.十一年前,我们在争论是否可能实现32位渲染,十一年后,我们争论的是需不 ...

最新文章

  1. 面试官问:生成订单30分钟未支付,则自动取消,该怎么实现?
  2. 云原生平台的建设怎么搞?监控系统又该如何演进?这里有答案!
  3. PTA数组作业一查找整数
  4. C#关于MSMQ通过HTTP远程发送专有队列消息的问题
  5. 黑龙江计算机单招学校,黑龙江有名气的中专单招
  6. Google小组研发模式分析 1
  7. Ajax-jsonp
  8. [蓝桥杯][2014年第五届真题]兰顿蚂蚁-模拟
  9. 【计算机网络】Socket
  10. python中引入包的时候报错AttributeError: module ‘sys‘ has no attribute ‘setdefaultencoding‘解决方法?
  11. Mat拜耳数据邻域转换算法返回Mat对象,16bit三通道
  12. jmeter 连接mysql数据库
  13. CRC校验的计算和原理(包括对模2除法的说明)
  14. win10怎么设置无线网连接到服务器,win10wifi自动连接在哪里设置_win10设置自动连接wifi的方法...
  15. Python实现cosx函数(泰勒公式)
  16. “均线金叉和均线死叉”的经典战法
  17. 使用OTP动态口令(每30s变一次)进行登录认证
  18. 海尔电商峰值系统架构设计最佳实践
  19. 颜值性能满分的华为Noto9,这个性能会让将他视为手中的至宝吗
  20. 哈工大计算机学院非全日制,哈尔滨工业大学非全日制研究生上课方式

热门文章

  1. 编译原理-First集和Follow集
  2. 内嵌iframe撑高父容器,底部有4px留白问题解决办法
  3. JQuery模拟网页中自定义鼠标右键菜单
  4. windows下nginx+tomcat分布式集群部署
  5. iOS之百度导航SDK的坐标转换
  6. 字节流 system.in
  7. Webview--如何让加载进来的页面自适应手机屏幕分辨率居中显示
  8. 2018年年度总结,以及2019年规划
  9. 【剑指offer-Java版】21包含min函数的栈
  10. Android EventBus 的使用