当前位置: 首页 > news >正文

用于Forza系列测试自动化的3D可视化工具

21.1 引言

物理引擎通常依赖于由艺术家手工制作的碰撞网格。这些网格可能存在洞口、错误的法线或其他错误数据,这可能会在运行时导致奇怪的行为。手动测试这些错误行为的成本极高。一个小洞或错误的法线可能会导致角色或车辆的行为完全错误,而这些错误很少能被复现,因为它可能取决于许多变量,如引擎时间步长、角色速度和角度。

在多边形网格处理领域,找到类似网格中开放边缘这样的问题并不是特例,一些3D内容创建包中已经提供了这个功能。然而,从拓扑学角度来看,对于非封闭网格,网格的边界和坏洞没有实质性的差异。因此,要可视化哪些是设计上的,哪些是错误需要对这样的网格进行过滤和语义分析,这在这些工具中几乎是不切实际的,特别是对于多尺度碰撞网格。因此,这导致了大量难以发现的错误。

本章将介绍一种3D可视化工具,它能自动分析网格中的坏洞和法线数据,并为手动测试者提供一个容易理解的语义视图,显示哪些可能是错误,哪些是按设计的数据。它还将快速回顾多边形网格处理中的算法实现主题,如网格遍历、半边加速数据结构、检测坏洞、开放边缘和其他问题。这个工具在Forza Motorsport 5和Forza: Horizon 2的整个生产周期中被Turn 10 Studios和Playground Games使用。在Forza系列的之前的版本中,没有这个工具,测试团队通常需要花费数百小时手动测试游戏以找到碰撞网格问题,并且没有保证能找到所有问题,因为基本上是一种蛮力方法。有了这个工具,现在可以分析一整个赛道的网格,并且在不到500毫秒内找到所有碰撞错误。此外,这为我们提供了信心,我们正在以完美状态的碰撞网格发布游戏。

21.2 碰撞网格问题

这个工具最初是为了只检测碰撞网格中的洞而设计的;后来,它还扩展到检测翻转/歪斜的法线和畸形的三角形。这些是导致运行时物理引擎出现问题的主要问题。

21.2.1 洞

                        


碰撞网格中的洞对我们来说是一个大问题。巨大的坏洞通常不是要解决的问题,因为它们很容易被测试人员发现,它们的行为通常非常确定且明确:例如,车穿过了它不应该穿过的墙,或者它掉进了洞口之中。尽管发现这些问题并不快,有时还很浪费精力,但它们最终会被发现并修复。真正的问题是小洞/微小的洞,它会导致车辆行为怪异,以非自然的方式行动;这样的错误只会在特定的车辆上,在特定的速度下,如果以特定的角度撞击时才会复现。此外,当错误被记录时,它通常只有一个视频,显示了在赛道的特定部分发生的奇怪行为,因此负责修复的艺术家通常不会知道是哪个特定的三角形造成的。图21.1和21.2展示了工具检测到的一个小洞。
 

                                


21.2.2 错误的法线

碰撞网格中的法线负责确定在每个特定顶点施加给汽车的力。因此,如果某个道路片段是平的,那么这些顶点处的法线应该是垂直向上的。如果法线被翻转,用户会看到汽车被拖入地面。与小洞一样,一个倾斜或翻转的法线可能会在运行时导致完全错误的行为,并且也可能仅在非常特定的场景中才能被检测到。在本章中,我将任何与Y向上向量的角度大于90度的法线称为翻转法线。翻转的法线总是错误的法线;然而,我们可以有设计上倾斜的法线,这是我们模拟轮胎撞击隆隆带的物理效果的方式。因此,特别难以检测一个倾斜的法线是设计上的还是错误的。
 

                    


 

艺术家通常不会手工创作法线;它们是由3D数字内容创建工具创建的。法线变歪斜或翻转的原因是因为它们可能焊接顶点并创建非常小的三角形。这些小三角形一起在3D DCC工具对法线的计算上产生精度问题,碰撞网格最终带有错误的法线。图21.3显示了工具检测到的一个翻转法线。注意每个法线都遵循与Y向上向量对齐的良好模式,而被标记的法线明显打乱了这一模式。

21.2.3 畸形三角形

工具还能检测到畸形的三角形,比如线状的三角形(即,两个顶点共线)或三个顶点相同的三角形。通常,这些并不像洞或错误的法线那样导致非常糟糕的行为,但它们绝对是错误的数据,本不应该存在。

21.3 检测问题

本节将详细介绍构建查询和遍历网格所需的数据结构,以及我们如何检测前一节中描述的各个问题。

21.3.1 构建数据结构

为了能够检测坏洞,我们需要将网格添加到一个易于查询的数据结构中。我们使用了(half-edge data structure)[M¨antyl¨a 88, pp. 161 174; Kettner 99].。半边数据结构易于实现,并且能够表示任意可定向的二维流形多边形网格,没有复杂的边或顶点。


                


 

数据结构以这样一种方式存储:每个三角形面有三个半边,以相同的绕组顺序排列,每个边引用下一个半边、邻近面的对面半边和一个顶点,如图21.4所示。我们网格的成员在以下伪代码片段中详细说明:

                   
                         


通过解析网格的顶点和索引缓冲区,并填充到如上所述的数据结构中,开始对网格进行查询变得非常容易。例如,以下片段找到给定面的所有邻近面:

                        

关于半边数据结构的更多信息,我们建议读者参考以下文献:[McGuire 00, Botsch et al. 10]。


                        


 

21.3.2 检测洞

在将网格存储到半边数据结构后,我们首先寻找洞。为此,我们将网格视为一个无向图,其中每个三角形面是一个节点,每个三角形边是图的一条边。这在图21.5中进行了说明。

接下来,我们进行广度优先搜索(BFS),寻找没有对面半边的任何半边;这将是图21.5所示的开放边。任何开放边都是洞的一部分,我们将这个开放边存储在开放边列表中。这个过程在以下伪代码片段中显示:

                                      

最后一步是根据开放边信息创建一个洞。为此,我们从列表中获取第一个开放边,让这个边的顶点为V1和V2。接下来,我们将开放边分配给一个洞,并从开放边列表中移除该边。同时,我们将V1标记为洞的第一个顶点;这是当洞的循环完成时必须结束的顶点。换句话说,网格上必须有一个顶点为(Vn, V1)的边来完成这个洞。现在,我们遍历所有开放边,找到第一个顶点为V2的边;这是我们洞的下一个边,我们将其添加到洞中,并从开放边列表中移除。我们继续这个搜索,直到找到边(Vn, V1);这个边完成了洞,并且我们继续到下一个洞。当开放边列表中没有更多边时,循环结束。以下片段说明了这最后一步:


                             


 

这个算法将网格边界识别为洞,这将在下一小节中解释。


                        


 

21.3.3 洞分类

从拓扑学角度来看,对于一个开放的网格,洞和网格的边界之间没有区别。通过用一张纸制作一个圆的扇形并以此构建一个圆锥,留下圆锥的底部开放,可以很容易地将这一点可视化。圆锥的底部是网格中的一个洞,但它也是其边界。通过将圆锥展平并再次使其成为一个圆,你可以再次看到边界和洞之间在拓扑上没有区别;见图21.6。

在我们的碰撞网格中,有大量的边界是按设计的,将它们全部标记为可能的问题以供测试人员过滤,会产生太多噪音和假阳性,使得工具的使用变得不切实际。为了解决这个问题,我们提出了两条规则来白名单化那些很可能是按设计的洞。首先,我们的碰撞网格有非常高的垂直墙,以防止汽车掉出世界,而绝大多数按设计的边界都在这些墙的顶部。我们通常不关心碰撞网格上非常高处的问题;因此,我们将发现在网格上半部分以上的任何洞列入白名单。我们使用的第二条规则是当洞非常大时。我们的碰撞网格包含沿赛道的障碍物,这些障碍物有大边界,都是按设计的;第二条规则的目的是将这些障碍物列入白名单。基于大洞尺寸的白名单化已被证明是安全的;当然,我们可能在赛道上有一个巨大的洞,实际上是一个错误,但这些通过正常玩游戏很容易且迅速地被发现。此外,用户也可以移除白名单化,让所有洞出现并通过在赛道上方飞行快速检查。

21.3.4 检测错误的法线

如21.2.2节所述,有两种错误的法线:翻转的法线和倾斜的法线。翻转的法线(见图21.3)很容易检测。我们遍历碰撞网格的所有法线,并将满足以下方程的任何法线标记为翻转:

                                   

其中y是单位Y向上向量。倾斜的法线更复杂,因为我们可以通过设计拥有这种法线;见图21.7。然而,实际上是错误的那些来自于制作的网格中的缺陷,通常是非常小的三角形。我们尝试识别这些的第一种方法是简单地标记面积小的三角形。这不太有效,因为一个法线受到顶点一环邻域内整个区域的影响,仅在局部查看一个三角形产生了太多不正确的结果。

                 

后来,我们找到了一个对我们的网格来说效果很好的启发式方法来检测这些错误的倾斜法线。我们的法线以64位精度导出,在工具中我们使用以下非加权公式重新计算法线:

                     

其中k是顶点一环邻域内的面数,ni是每个三角形的面法线。我们使用32位精度计算这个公式。之后,我们有两组法线:原始的64位精度和计算的32位精度。然后我们比较每组的两个法线;如果它们的方向差异超过特定阈值,很可能该网格区域存在错误的倾斜法线,我们将其标记。这是为了模拟导出器的行为以进行有意义的比较并捕获较少的假阳性。

这种方法已被证明是一个好的启发式方法;然而,它有时仍会导致假阳性。在查看这些问题时,我们要求测试人员注意被标记顶点周围的区域,看看所有法线是否遵循一个良好的模式。比较两组法线的阈值是为用户预定义的,但如果某个区域仍然发生奇怪的物理行为而工具没有标记任何东西,可以在运行时修改。阈值越小,标记的假阳性就越多。

21.3.5 检测畸形三角形

检测畸形三角形很简单。在遍历网格填充半边数据结构时,我们查看三角形数据,看看是否有任何三角形的顶点设置在相同的坐标上,或者三个顶点是共线的。这些问题由工具标记。


                  


 

21.4 可视化

我们的可视化方案被证明是简单且有效的。通常,工具的用户不是很技术化,设计工具时,我们考虑到可视化问题应该非常直观。工具基本上有两种模式。常规模式用于显示洞和畸形三角形,法线模式用于显示翻转和倾斜的法线。在每种模式下,用户选择他想要可视化的问题类型(即,洞),所有这些问题都被高亮显示。然后用户可以使用键盘箭头循环查看它们,当他循环时,一组从每个高亮顶点到Y轴非常高处的绿色线条出现。这些绿色线条对于实际找到问题出现在网格中的位置非常有用;见图21.8。

21.5 导航

如前一节所述,该工具的用户并不是很技术化,我们的导航系统应该尽可能易于首次用户学习。对于多尺度环境,3D内容创建包通常使用某种弧形球摄像机方案来在场景中导航。虽然艺术家通常对这类方案相当舒适,但普通用户一开始可能会觉得它难以理解且不直观。对测试人员来说理想的导航方案是第一人称射击游戏风格,他们会觉得非常熟悉。这种方案在多尺度环境中的最大问题是摄像机的速度;有时用户希望它非常快以跨越长距离,而其他时候可能需要它非常慢以便更仔细地查看一个非常小的洞。为了解决这个速度问题,我们尝试了类似于 [Trindade et. al 11] 的自动化方法,其中生成一个动态立方图来计算摄像机与周围对象之间的距离,并根据距离自动调整速度。这种方法在一定程度上有效,但仍有非常糟糕的情况发生,即摄像机行进速度过慢或过快,导致用户感到沮丧。

在测试了一些可能的导航方法后,我们找到了一个在可用性和学习曲线方面对我们的环境来说最具成本效益的方案。摄像机以默认速度开始,用户可以通过鼠标滚轮的微妙移动线性增加其速度。然而,滚轮上的快速移动会使速度呈指数增长(每次翻倍),并且一个阈值控制了速度的上下限。我们还设置了一个快捷键,用于直接跳转到特定选定的问题。尽管这不是第一人称射击游戏中使用的方案,但我们发现经过几次使用后,用户可以快速且精确地使用这个方案。

21.6 工作流程

工作室的工作流程始于艺术家制作内容源文件,然后将赛道及其碰撞网格和其他所有内容构建成二进制文件,这些文件准备好在运行时被游戏读取。构建完成后,赛道位于一个托管文件夹中,等待通过测试来进行提升;如果每个测试标准都通过了,赛道就会被提升,工作室中的其他人将在构建中看到它。起初,我们有一个将3D内容创建包中的碰撞网格导出到我们的工具能读取的格式的单独导出过程。然而,这引起了太多与同步相关的问题。有时导出过程会失败,不会创建新的碰撞文件,测试人员会在旧网格上进行整个测试过程。此外,如果艺术家尝试使用不同的源文件作为碰撞网格,导出脚本必须始终更新;如果导出过程没有更新,测试人员也会测试错误的碰撞网格。为了解决这个问题,我们取消了导出过程,并使工具能够读取运行时物理引擎读取的相同的二进制文件。

该工具还具有一些不错的功能,可以在提交和修复错误时改善测试人员和艺术家的工作流程。每当突出显示一个问题时,用户可以按下热键以输出3D内容创建包空间中问题的坐标。因此,在修复错误时,艺术家知道洞的确切坐标。此外,每当测试人员在工具中按下“打印屏幕”时,一个截图将自动保存在用户文件夹中,附带问题的类型和编号,这使得测试人员更容易导航到工具,拍摄每个错误的坐标截图,并稍后一次性提交它们。

21.7 结论

本章介绍了一个用于检测碰撞网格问题的3D可视化工具。该工具已在生产中使用,我们通过使用它在开发过程中节省了数百小时的手动测试时间。我们的目标不仅是为这个特定问题提供解决方案,还希望激励读者使用计算机图形技术解决其他领域的问题,正如我们的测试问题所示。

21.8 致谢

感谢Zach Hooper在开发这个工具过程中不断提供反馈,以及Daniel Adent在出版上的支持。特别感谢我的妻子和家人的所有帮助,以及我的朋友F. F. Marmot。
 

Bibliography

[Botsch et al. 10] Mario Botsch, Leif Kobbelt, Mark Pauly, Pierre Alliez, and Bruno Levy. Polygon Mesh Processing. Natick, MA: A K Peters/CRC Press,2010.


[Kettner 99] Lutz Kettner. “Using Generic Programming for Designing a Data Structure for Polyhedral Surfaces.” Computational Geometry 13.1 (1999),65–90. 


[M¨antyl¨a 88] Martti M¨antyl¨a. An Introduction to Solid Modeling. New York: W. H. Freeman, 1988.


[McGuire 00] Max McGuire. “The Half-Edge Data Structure.” http://www.flipcode.com/articles/articlehalfedgepf.shtml, 2000.


[Trindade et al. 11] Daniel R.Trindade and Alberto B. Raposo. “Improving 3D Navigation in Multiscale Environments Using Cubemap-Based Techniques.” In Proceedings of the 2011 ACM Symposium on Applied Computing,pp. 1215–1221. New York: ACM, 2011.

http://www.dtcms.com/a/308635.html

相关文章:

  • MySQL 8.0 OCP 1Z0-908 题目解析(38)
  • Android Jetpack消息推送全解析:从FCM集成到Jetpack组件优化
  • 信令和信号在通信领域的区别
  • 详解分布式数据库缓存技术:高性能数据访问的基石
  • 【javascript】Reflect学习笔记
  • OCP网卡、OVS网卡和DPU(数据处理单元)三类技术方案
  • system.conf linux用于启动和管理系统进程的初始化系统和服务管理器的配置文件
  • 检索召回率优化探究二:基于 LangChain 0.3集成 Milvus 2.5向量数据库构建的智能问答系统
  • 中国高速铁路网的“四纵四横“和“八纵八横“shp数据
  • LLM——使用 LangGraph 构建 ReAct 智能体:多轮对话 + 工具调用 + 可视化流程图
  • flowable对已经部署的流程进行更新,不产生新版本
  • 【问题】Docker 容器内的应用(如n8n),访问不到外部主机的应用(如mysql)
  • C语言基础第18天:内存操作函数
  • Jmeter 性能测试常用图表、服务器资源监控
  • AI学习笔记三十四:基于yolov5+deepsort+slowfast的视频实时行为检测测试
  • 【源力觉醒 创作者计划】文心大模型4.5体验:技术跃迁,拥抱AI新时代
  • Coze是什么?能做什么?
  • MySQL 9 INNODB Cluster部署
  • Qt之CJSON:从基础到进阶的 JSON 数据处理指南
  • MySQL 8.0 OCP 1Z0-908 题目解析(41)
  • 节目预告:工程师张仰彪在相对论学习中的九个疑问
  • 【Kubernetes 指南】基础入门——Kubernetes 集群(一)
  • python每日一题练习---简单题目
  • 基于STM32设计的景区便民服务系统(NBIOT)_261
  • IDEA识别lombok注解问题
  • MySQL常用命令完整指南
  • systmctl的作用,使用场景和用法
  • 硬件-音频学习DAY1——音箱材料选择:密度板为何完胜实木
  • 动手学习深度学习-深度学习知识大纲
  • 云迁移技术深度解析:核心原理与最佳实践