【小玩意儿】UE中的浮点数精度问题
今天来随便聊聊UE4的浮点数精度问题,可能对做移动端开发的朋友有点帮助吧(主要之前在UDN看到有人问过这个),这篇文章的目的也就是给大伙们提个醒,有这么个事儿,万一以后遇到了少踩点坑。不过如果你不打算做移动端的Shader开发,下面的内容应该也没有什么用,不如拿这时间打一把炉石。half和float这两种浮点数类型,对于各位写Unity Shader的朋友或者各种大佬来说,应该都是非常熟悉的了。但在写UE4材质的时候,引擎并没有很好地为我们区分这两种类型,于是有的朋友可能就对此根本没有概念。本来这也不算什么问题,PC上的显示正常得很,能出什么问题呢?
问题就是UE4根本没管你移动端啊!
为了性能,UE4默认让材质编辑器中连的大部分东西都用的half浮点数,电脑上的显卡写作half实际用的float,然而移动端的half那真的是half。
先来看看材质编辑器中连的节点生成的HLSL代码。随便连个材质。
可以看到这几个节点生成的HLSL代码,类型用的是MaterialFloat
在引擎目录\Engine\Shaders\Private里的Common.ush可以看到对MaterialFloat的定义,在像素着色器阶段,这个东西等于half
那么用half会有什么问题呢?
首先是half数字最大也就大概65504,超过这个大小的数字全都会被限制在65504。这个大小限制,两个三位数相乘可能就爆掉了。
然后是数值越大越不精准,这是浮点数天生的问题,但half由于数值范围太小,这个问题相比正常的单精度float,突出很多。数值在几十上百的时候,小数点后的数字就已经很不准了。
百度百科对半精度浮点数的定义
案例测试
下面我们来看几个例子。
地形案例
打开一个UE4,新建一个地形材质(或者随便什么材质),随便连一下,贴图平铺次数随便给一下,然后在UV计算后面加个100,让UV从100开始。
在电脑上显示很正常。
然后打包到手机上看看。如下图所示
拉近之后可以发现贴图已经成马赛克了,因为此时用来采样的UV坐标,由于精度不足,已经在屏幕上已经不够连续了。
水材质案例
再新建一个简单的水材质,水长得好不好看不重要,重要是里面用Panner这个函数来做个简易的流动效果。
Panner(或者说Time)引起的精度问题可以说是最常见的问题了,特别是很多跟着网上教程做的特效小哥,把东西打包到手机之后发现,怎么过了几分钟特效变成马赛克了。
电脑显示正常。
然后打包到手机。刚进去的时候可以看到水面是正常的,然后过1分钟,可以看到水面变成了马赛克。如果是再放久一点,马赛克会更加严重,并且会变得卡顿。
这是由于panner直接使用了时间去做UV偏移,时间不断增长的情况下,采样用的UV坐标数值很快就到了half精度不够的范围,然后导致马赛克出现。
天空案例
再做个例子,写个简单的材质,用来当作天空盒。重点是材质里面用使用位置减去摄像机位置,然后做normalize,用来采样一张Cubemap。
电脑上看着很正常。
手机上他长这样。
由于像素的世界空间位置减去摄像机位置,可能得到的数值是好几百上千。用RenderDoc随便看了一个点,发现这个数值已经上万了。。。(为了做例子,用力有点过猛)
然后normalize的第一步就是自己dot自己,这一步操作轻轻松松突破了half的最大数值65504。那么后面剩余的计算那都是不准的了。
在RenderDoc中让手机这一步计算改用float单精度浮点数,发现画面正常了。说明这就是由half引起的问题。
快速定位问题
快速定位是否是精度引起的问题,可以用renderdoc快速测试一下。找到对应的DC,在Pipeline State窗口选中Fragment Shader点Edit编辑。
将mediump改成highp,点击Refresh,如果问题修复则说明是精度引起的问题。
解决方法
在编辑器中解决问题有这么一些方法。
最省事的方法,直接材质里勾上Use FullPrecision,让所有的MaterialFloat都变成float,不过这会带来一点性能影响,毕竟用half就是为了提升性能。UE5里多了一个选项会让这个影响范围小一点。
一个比较难用但看起来不得不这么做的方法,首先得清楚是哪一步导致精度出现问题,那么将对应的节点用Custom Node节点写出来,手动控制float和half的使用。不过这个方法坑就坑在返回值类型和输入值类型也都是half的,要求使用者对相关代码很熟悉。或者干脆整坨代码用Custom写。
可能是这么写的?
还有一些比较特殊的方法,比如UV相关的,取小数点再用;时间相关的,改用循环的时间;数值太大爆掉的,变小一点再送去做计算等等。
当然最好用的还是改引擎,可以手动针对某个节点设置其浮点数类型。修改难度不大,不知道为什么官方没有提供这个功能。
官方做了,在ue5里,把用户节点用half,但不能指定某个节点的精度。也可以用custom node手写float解决 啥意思,没看明白[发呆] 就是材质编辑器里节点的用float,引擎的shader用half。用custom node就是哪用float就写hlsl代替原来的节点 UE4不就是这样,你说UE5做了是做了啥 我做过一个需求就是在ue4里让单个材质函数支持float,改改材质hlsl翻译器就可以。 UE4改全精度连引擎里shader都变全精度了 不是,是只对单个material function改,只要翻译器在翻译material function时改改类型就可以了 就是支持在material function里用use full precision,这个选项在材质蓝图里有,类似
页:
[1]