Unity 坐标系与缩放

翻译 – Unity coordinates and scales
如果你觉得这篇教程不错,请去支持原作者

概述

与UDK不同,Unity不会主动限制游戏世界的大小,你可以随便增加游戏世界的大小使你的玩家迷失在其中,直到引擎坐标系统位数溢出并导致数学上的崩溃.

从全尺寸的行星到苹果里的虫子,Unity让你在任何你想要的尺度上工作——然而如果你试图同时在超小和超大的尺度上进行操作,你会发现你可能遇到了一些严重的问题.

这篇文章讲述了Unity的坐标系统的工作原理,以及它对游戏世界区域的影响,渲染时的问题,还有如何创建超大型的世界.

浮点数精度

Unity允许你在浮点数范围内的坐标系统中放置物体.X,Y,Z的值算上小数总共只有7位有效数字,比如12345.67或12.34567.

在这个系统中,你离原点越远(0.000000–绝对的零),你失去的浮点精度就越多.例如,设1个Unity单位等于1米,那么一个有1.234567单位的对象具有的浮点精度为6位小数(1微米),而另一个有76543.21单位的对象精度只有两位小数(1厘米),因此后者精度较低.

空间并不是无限的

就算你在Unity里面乱填坐标数字,也是可以的.比如你将一个物体的坐标在任意一个轴上设置超过100000,这时系统会发出警告,但它仍然允许你这样做.另外,在Unity中能设置的最大坐标值是3e+38,它是一个3后面有38个0,这是一个相当惊人的数字,但是当数值大到超过一定的级别时,由于科学计数法的表示问题,你不能手动控制精度,所以能做的就是控制第一个数字后面的零的数量.

说实话,最好将对象位置设定在建议最大值100000以下,以确保浮点精度至少有一位小数.问题在于需要什么级别的浮点精度才能使玩家觉得游戏玩起来感觉还算正常,因为当浮点精度较低时,摄像机的运动和物理计算将开始变得很不稳定.

举一个实际例子

还是设1个Unity单位等于1米,对于距离摄像机约1米的人体大小的物体,你需要精确到毫米才能在低速下平稳移动,否则你将看到物体在运动中的发生抖动,也就是说需要3位精度小数(10-3=毫米).

因为小数精度设定为3位,总共只有7位,所以就只能有4位代表米(1234.567).因此,最大坐标值就是9999.999,这还不到十公里.

如果你的玩家并不总是步行,那么不需要在任何时刻都精确到毫米级别.假设另外一个例子,我希望让玩家驾驶一架飞机,并且只能在原点附近移动,那么在远距离只需要精确到厘米级别(0.00)就可以了.这样的结果就是,我可以让坐标区域达到99999.99–几乎100公里.

我们可以用下面这种方法来限制玩家的移动区域.因为移动超过100公里时,物理计算和渲染会变得不稳定,所以我们要弹出一个警告告诉玩家“回到战场”,如果玩家不返回,就在规定时间内杀死他.但是实际上在游戏变得不可描述之前,玩家确实可以再走900000米,因为还剩2位小数.

缩放 我们可以利用未使用的小数位吗

如果设定在步行状态下我们无法在100米外看到玩家(99.99999米),你可能会认为我们可以缩小所有东西的比例以调整小数点的位数来得到更高的精确度,但是其实根本不行.

如果我们把所有东西都缩小1000倍,那新比例尺就是1m=0.001单位.在这个标准下,1毫米表示为0.000001单位.这样我们就没有浪费任何有效位数,因为最后一位小数现在表示毫米.

你可能会觉得我们的游戏世界范围扩大了1000倍,但是你错了.在下面的表格中我强调了”厘米”这个单位级别,因为这是防止游戏出现问题所需的最小精确度.当我们无法表示厘米的时候,也就达到了我们游戏世界的极限范围.

大小 单位
0.000-0-00 Origin(原点)
0.0000-0-1 1 Millimeter(1毫米)
0.0000-1-0 1 Centimeter(1厘米)
0.0010-0-0 1 Meter(1米)
1.0000-0-0 1 Kilometer(1千米)
10.0000-0- 10 Kilometer(10千米)
100.0000 100 Kilometer(100千米)

注意在上面的数据中,当你超过100千米时,厘米精度就会丢失,所以最大游戏世界的范围还是100KM,因为总长度是受有效数字的限制,而不是游戏世界的缩放程度.

这是完全可以算出来的.因为只有7位有效数字,所以上限100KM=10,000,000cm=107cm(注意是7个0和10的7次方).如果你需要的毫米级的精确度,则107mm=10,000,000mm=10KM.

缩放 什么时候能起作用

如果只是单纯地为了增加游戏世界的大小而缩放并没有任何意义,但你可能仍需要缩放你的游戏世界.

例如,要准确地再现地球和月球在宇宙中的位置,如果以地球为原点(0),那么月球需要被设置在距离地球384,400,000米的坐标上.正如上面所说的,Unity不能支持这么大的数字,你最终会得到“3e+8”,即300,000,000.虽然月球最初在这个位置是OK的,但是在渲染时会疯狂抽搐(原因稍后介绍),并且如果你试图使月球绕地球运行,物理引擎也会炸掉.

取而代之,我们可以缩小天体的比例来达到Unity可以处理的程度,比如把比例尺设置为1:100000(1单位=100KM)

真实大小 Unity 1:1 Unity 1:100000
Earth Diameter (地球直径) 12742000m 1e+7 12.74200
Moon Diameter (月球直径) 1737000m 1737000 1.737000
Distance from Earth to the Moon (地月距离) 384400000m 3e+8 384.0000

正如你所看到的,重新调整大小后,坐标值长度转换到了Unity可以轻松处理的范围内,并且这些值不会使物理引擎崩溃.不过要注意的是,我们缩放后丢失了分辨率(浮点精度).月球的坐标位置只有10米的分辨率.如果你是在天文尺度上进行操作(卫星的轨道速度大约是1.03千米/秒),这没有问题,但显然你不能把一个1米的玩家放在这些小行星上,然后还期待他们可以被正常操作.

在这个比例下,1000,000,000,000m(1000000单位)是我们的理论上限距离.在这个距离下,我们的分辨率会下降到1KM.但是这个距离还不足够飞到冥王星,冥王星距离地球最近的距离大约42亿公里.你是会继续缩放你的游戏世界,还是找另一个可行的方案,还是放弃?

相机在超大规模世界下的问题

Unity中的相机(和大多数其他引擎)有一个近距离和远距离的剪裁平面,这定义了视锥体的范围.所有在视锥体范围内的东西都将被渲染,而任何超出这个范围的东西都不会被渲染.

为了渲染像苹果这样的小物体,你可能想要让相机离物体非常近,可能是1厘米(0.01单位)的距离.相反地,为了渲染像行星一样巨大的物体,你需要让远剪裁平面尽可能的远来包含这个物体.设我们想要渲染的最大游戏区域为100km(100000单位).

如果把这两值设定到近远剪裁面上(near=0.01,far=100000),你会发现一些问题.首先多边形可能会因为Z轴渲染精度问题不停地交叉闪烁.如果是使用SSAO,效果会看上去像泥泞的条纹.如果你还有一个物体放在离相机100000单位的位置上,他的光照也会出问题,阴影细节变得模糊不清,甚至在某些角度上看不到.

这是过长的视锥体会因为z-buffer(depth)的精度而造成的渲染问题,基本上与我们在坐标系中使用浮点数时遇到的精度问题相同.但对于相机来说,可以很简单的像切面包片一样把近远平面切开分成几片,切片的数量保持一定,间隔合理,就能渲染出很好的效果.反之如果视锥体过长,切面间隔过大,就会出问题.

Unity的默认相机近平面和远平面值分别是0.3和1000,所以建议在配置相机时使用与此相似的比例(1:10000),以避免上述问题.更多信息,请参阅MSDN文章和Unity的相机文档.

解决方案 多相机系统

现在,我们已经了解了缩放和空间限制,以及单个相机1:10000视锥体比例,让我们讨论解决问题的方多个相机.

Unity支持多个相机渲染,相机之间的渲染效果可以叠加.简单地说,我们可以在相同的位置创建两个摄像机,并将其中一个的视锥体设置为0.01到1000,另一个设置为1000到1000000.通过两个不同层次的相机,我们可以得到从0.01到1000000的完整可见性,并解决了z缓冲的深度限制.

为巨大景象而生的天空盒

如果我们想有巨大的场景,但是这场景又不被内在坐标系统支持,我们可以像缩放世界一样缩放这个场景天空盒.

设置一个相机给天空盒,一个相机给玩家,只需要写点代码把天空盒相机的操作与玩家操作同步就行了.Unity商店就有,只需5刀Package

超越天空盒

Kerbal太空计划使用的一个解决方案是将摄像机限制在原点,并围绕它旋转世界.我们的“世界”只有有限的小数位数用于渲染,世界中物体的实际坐标是存储在另一个自定义坐标系统中,每一帧计算并转换到Unity的坐标空间.

KSP还使用缩放后的天空盒来解决远处天体的显示.当玩家靠近这些物体时,它们会隐藏在天空盒中,并在玩家的游戏空间中生成一个真实大小的版本,让玩家可以与之交互.

这里还有一篇KSP的Floating Origin System(不过很可惜链接已经失效了,大家可以自行搜索这个关键词)

这些技巧最大限度的利用了浮点数精度,使用了自己的坐标系统,理论上可以得到一个更大的游戏空间.但也是有代价的,必须自己编写代码把自定义坐标系转换到引擎的坐标系中,以便进行渲染.这很难,也可能使你无法使用引擎本身的某些功能特性,还可能使制作AI系统或多人游戏成为一件非常困难的事情.

作者

特兹卡特(Tezcat)

发布于

2019-12-25

更新于

2022-01-15

许可协议