ffycxyw2274436 发表于 2021-2-15 17:50

Unity基础:浮点精度详解

Unity并没有去限制世界的大小,你可以把世界扩张到无限大,让玩家永远迷失其中,直到引擎逐渐用完所有数字精度并且崩溃。
从完整的行星到苹果中的虫子,Unity均可让你按自己喜欢的任何比例工作。但是如果你尝试同时做微观的和宏观的事情,则可能会遇到一些严重的困难。
本文介绍了Unity坐标系的工作原理、对可玩区域和渲染的影响,以及如何让大世界游戏真正正确的工作。
浮点精度

Unity允许你将对象放置在基于浮点数的坐标系限制内的任何位置。Transform的X,Y和Z的限制是7个有效数字,在这7个数字中的任何位置都可以保留小数;举个例子:你可以将对象放置在12345.67或12.34567的坐标点上
使用此系统,离原点(0.000000-绝对零)越远,浮点数精度越低。例如,假设一个单位(1u)等于一米(1m),则1.234567处的对象的浮点精度为6个小数位(微米),而76543.21处的对象只能有两个小数位(厘米)。
随着距原点的距离越来越远,精度下降成为一个明显的问题。比如说由于精度的原因,你无法将765432.1处的对象移动0.01(一厘米)。
这看起来似乎不是一个大问题,但是物体在离原点太远时,在更大距离上失去浮点精度会导致诸如照相机抖动和不准确的物理现象。所以大多数游戏都试图将事物合理地保持在接近原点的位置,以避免这些问题。
空间不是无限的

Unity对开发者一向很宽容,但这并不好。如果在任何一个轴上放置的物体距离超过100000个单位,Unity虽然会发出警告,但仍然可以让你这样做。类似的,我能够插入Unity中的Transform的最大值是3e + 38,它是一个3,后面有38个零。尽管这是一个惊人的数字,但随着符号更改为1e + 07(一百万),浮点精度问题在99999999之后瞬间爆发,浮点精度会全部丢失,此时你所能做的唯一一件事就是控制第一位之后零的数量。
实际上,最好将浮点数保持在建议的最大值100000以下,以确保浮点精度至少达到小数点后一位。由于当浮点精度较低时,摄像机的运动和物理运算将变得不可靠 ,所以需要什么水平的浮点精度才能保持可接受的游戏体验,是对开发者的一个考验。
一个例子

当我们把比例尺设置为一米等于一个单位(1m = 1u)时,对于距离相机约1m的人体大小的物体,你需要精确到毫米的浮点精度才能在低速下平稳移动,否则你将开始看到颤抖的运动。这意味着你将需要3个小数位数的精度(10 -3 =毫米)。
由于你始终需要3个小数位的浮点精度供你的角色使用,因此总体上只有7位数字可用,这意味着你只能用4位数字来表示米(1234.567)。所以最大可游玩区域现在为9999.999,不到十公里。
不过,如果你的角色不总是用步行移动,那么可能不需要精度始终达到毫米级。在我的项目里,我希望让玩家驾驶飞机移动,只有在起点附近,他们才能跳出来走走。这意味着我可以在遥远出把精度降低到厘米级(0.00)。这样我可以将可玩区域扩大到99999.99--差不多一百公里。
这依然然不是物理限制,只是我们对于玩家的明智的限制。当我们超过100公里大关时,物理效果和渲染将变得异常,因此我们可以弹出警告告诉玩家“返回战场”,并在如果超过一定时间未返回,就强制杀死他们。实际上,他们可以再行驶90万米,不过如果我们再失去小数位,游戏表现会变得非常奇怪并且不可控。
我们可以利用缩小比例尺来获取更高的浮点精度吗?

如果我们知道我们不会在100米(99.99999)以外的地方看到我们的角色,那么你可能会认为我们可以按比例缩小所有内容以移动浮点位,从而使我们在更远的距离上具有更高的浮点精度,但是不幸的是,这不是解决问题的方法。
如果我们将所有内容按比例缩小1000倍,则新的比例尺为1m = 0.001u。在此比例下,一毫米表示为0.000001。请注意,由于最后一个小数位现在代表毫米,因此我们不再浪费任何有效数字。
你可能倾向于认为这将使我们拥有1000倍的游戏空间,但是这是不对的。在下表中,我强调了表示“厘米”这一精度的位置,这是我们防止事情抽风的最低精度。当我们再也无法表示厘米时,这就是我们游戏区域的极限。
0.000_0_00原点0.0000_0_11毫米0.0000_1_01厘米0.0010_0_01米0.1000_0_0100米1.0000_0_01公里| 10.0000_0_10公里100.0000100公里请注意,在上表中,当您经过100公里时,你将失去厘米分辨率。运动和渲染的分辨率受限于浮点数的有效数字而不是世界的缩放,所以100 km仍是最大可播放区域。
这也是完全可以预见的。因为只有7个有效数字可用,而我们的100km的上限= 10,000,000cm = 10的7次方 cm(请注意7个零和7的幂)。如果所需的精度是毫米,则可可活动区域只有10的7次方 mm = 10000000mm = 10km。
缩小比例尺正确的用法

虽然缩小比例尺并不能带来浮点精度上的优势,但是还是有其特定的作用域。
例如,如果要精确表示地球和月球,以地球为原点(0),则需要将月球放置在384,400,000米处。如上所述,Unity无法处理此类数字,你最终会得到“ 3e + 8”,即300,000,000。虽然月亮最初可以在该位置处停留,但渲染器会像癫痫发作一样(稍后会解释原因),如果你尝试使月亮绕地球运行,则物理引擎也会癫痫发作。
相反,我们可以按比例缩小天体,并将其降低到Unity可以轻松处理的水平。下面我们将通过使比例尺为1:100000(1u = 100km)来尝试该理论。
实际尺寸Unity中的1:1尺寸Unity中的1:100000大小地球直径12742000m1e+712.74200月球直径1737000m17370001.737000地球到月球的距离384400000m3e+8384.0000如你所见,重新缩放的大小将Transform值带回Unity可以轻松处理的地步,并且这些值不会吓坏物理引擎。应当指出的是,通过减小比例尺,我们已经失去了分辨率(浮点精度)。我们月球的位置分辨率只有十米。如果你以天文规模工作(卫星轨道速度约为1.03 km / s),这没有问题,但是很显然,你不能在这些小行星上放置一个仅仅1m的玩家并期望他能够正确执行任何可行的动作。
在这样的规模的比例尺下,在事情再次变得疯狂之前,我们有1,000,000,000,000m(1000000u)的理论上限,在这个距离上,我们的分辨率已经降低至1km。但是这还远远不够到达冥王星,冥王星距离地球最近约42亿公里。这个会让你决定是否需要进一步缩小比例,或者为你的游戏找到一个复合的缩放解决方案,还是放弃这个想法。
在大比例尺下相机会出现的问题

Unity(以及大多数其他引擎)中的相机具有“近”和“远”裁剪平面,该平面定义了“视锥”。视锥范围内的所有内容都将被渲染,超出此范围的任何内容都不会渲染。
要渲染像苹果这样的小物体,你可能希望能够将摄像头尽量靠近物体,可能被近裁切平面裁切之前需要接近到1cm(0.01u)。相反,要渲染巨大的物体(如行星),无论对象处于任何距离,都需要设置远剪裁平面以包围对象。就我们的示例而言,假设我们要渲染到最大可视区域100 km(100000u)。
如果将这两个值插入“摄像机”(near = 0.01,far = 100000)中,则会发现一些问题。首先,多边形在会出现Z-fight并开始相交并闪烁。如果在Unity中应用了屏幕空间环境光遮挡(SSAO)的情况下,你还会注意到效果看起来像是泥泞的条纹。最后,如果确实有一个放置在100000u处的物体,你可能还会注意到照明中有些奇怪的地方,动态阴影变得细节不够清晰,或者在某些视角下出现闪烁的情况。
产生这些异常的原因是使用如此大的摄像机视锥时缺少z-buffer(深度)精度造成的,基本上与我们在坐标系统上使用浮点精度时遇到的相同问题相同。想象一下,将“近”和“远”剪切平面之间的距离像面包中的切片一样分开。切片的数量始终是相同的,但需要合理地间隔一定距离,以使所有组件都能按预期工作。在相机上设置如此长的视锥会导致切片间隔太远,导致发生怪异的事情。
Unity摄像头的默认近裁剪平面和远裁剪平面分别为0.3和1000,因此在配置相机时,建议使用与该比例相似的比例(1:10000)来避免上述问题。有关更多信息,请查看MSDN文章“改善阴影深度贴图的常用技术”和Unity的相机文档。
使用多相机来解决这个问题

现在我们对3D空间的规模和空间限制有了一个了解,并了解了单个摄像头的视锥率只能达到1:10000,下面我们来讨论如何使用多台相机来解决这个问题。
Unity可以使用多个摄像按顺序把图像渲染到GamePlay视窗。最简单的方法是,我们可以在同一位置创建两个摄像机,然后将一个视锥的远近截面设置为0.01到1000,将第二个摄像头的远近截设置为1000到1000000。通过两个分层的摄像机渲染不同层级的物体(第一个渲染苹果,第二个渲染行星),这样,我们就可以同时看到近处和远处的物体,并克服了z-buffer深度限制。
大型场景的3D天空盒

如果我们想拥有不适合玩家坐标系比例限制的大型风景对象(天空盒),我们可以使用前面讨论的景物比例尺以及标准的玩家比例尺。
这种情况实质上就是设置了一个3D天空盒。我们可以这样设置相机,一台供玩家使用,另一台相机用来拍摄巨大的风景物体(天空盒)。我们需要一些代码才能使3d skybox摄像机的相对位置和旋转与玩家摄像机在每个帧上的相对位置和旋转同步。
超越3D天空盒,走向银河系

Kerbal太空计划(我最喜欢的天空模拟建设游戏)使用的一种解决方案是将摄影机集中在原点,并让世界围绕着摄像机旋转。我们的“世界”有限的浮动空间仅用于渲染,世界中对象的实际坐标存储在更好的“自定义”系统中的其他位置(64位浮点数),并在每一帧上转换为世界空间坐标。
KSP还使用缩放天空盒方案来解决遥远天体的显示问题。当玩家靠近这些对象时,它们会隐藏在天空盒中,并且会在与玩家互动的空间中添加实际大小的版本。
您可以在2012年开发人员博客文章Scaled Space:现在有了100%的Floating Origin,更多地了解Kerbal Space Program的坐标系!
这些技巧可以极大程度地提高浮点精度,你可以使用自定义坐标新获得更大的可玩区域,但是它们编码比较杂。使用自己编写的坐标系并将这些坐标转换为引擎中的世界空间以在每一帧上进行渲染绝非易事。你还会失去引擎本机功能的很多助力,这使AI或多人游戏变得非常困难。
关注我,我将持续输优质内容^.^

123456806 发表于 2021-2-15 17:53

那么无限大地图怎么办 如我的世界

掌舵的鱼1987 发表于 2021-2-15 18:00

我的世界也不是无限大,只是能大的让你'感觉"上无限大,而且早期版本到极远处的的表现会变得很奇怪。

六月清晨搅 发表于 2021-2-15 18:01

原神那种大世界地图不知道是怎么处理这个问题

愿为素心人 发表于 2021-2-15 18:10

元神的地图大约为10KM*7KM,在此范围内,浮点精度可以达到毫米级别,并不要什么特殊的处理。

六月清晨搅 发表于 2021-2-15 18:19

学习了!顺便请问下这些资料的出处是哪里?

愿为素心人 发表于 2021-2-15 18:24

http://davenewson.com/posts/2013/unity-coordinates-and-scales.html
页: [1]
查看完整版本: Unity基础:浮点精度详解