【GAD翻译馆】小浮点格式——R11G11B10F精度

发表于2017-09-28
评论0 1.4k浏览

想免费获取内部独家PPT资料库?观看行业大牛直播?点击加入腾讯游戏学院游戏程序行业精英群

711501594

bartwronski发表于201742

翻译:赵菁菁(轩语轩缘,657459265) 审校:李笑达(DDBC4747

      虽然这篇文章还没有与抖动相关联,但它在某种程度上是这个关于抖动系列的一部分。大家可以点击这里查看所有部分的目录,或点击这里阅读前一部分。

      我将在这里讨论使用不是很流行/出名的格式——R11 G11 B10浮点(R11G11B10F)格式——关于其精度、注意事项以及如何提升它。

      我想在这里指出,这篇文章不会涉及许多浮点的微妙之处,即NaN、不规范(denorm)和无穷大。很多GPU着色器编译器都使用快速数学算法(除非被要求严格遵从IEEE标准)并忽略它们——程序员在使用结果值时候必须加倍小心。

你可以点击这里下载Mathematica(数学软件)笔记,点击这里获取对应的pdf

更新Tom ForsythPeter Pike Sloan校正GPU是标准化支持denorm写小浮点数的,在此之后,我更新了关于在denorm范围内丢失动态范围的部分。

问题描述

渲染中最常用的颜色表示不是整数/自然数/有理数表示,而是浮点表示法。浮点数和它们的大范围是有用的,有几个不同的原因,但最重要的是:

据说,渲染技术甚至为HDR色彩也很少存储32位浮点值——由于内存存储和性能都需要成本。内存带宽和缓存通常是最神圣的资源,简单的经验法则是“ALU(运算器)很便宜,内存访问很昂贵”。即使是最简单的内存访问操作也有上百个周期的延迟(至少在AMD GCN上是这样)。此外,使用纹理单元时成本会增加——因为过滤操作变得越来越昂贵,操作速度也越来越慢。

因此,渲染程序员通常在内存用较小的浮点格式存储——两种最常见的是RGBA16F(四个16位半浮点通道)和R11G11B10F (通道RG11位小浮点,通道B10位小浮点)。

让我们看看这些格式和全32IEEE浮点的区别。如果大家对浮点表示感到满意,可以跳过下一节。

浮点——回顾

我在这里假设读者知道浮点数是如何表示的,但作为提醒,典型的浮点值是由一些位表示的:

  • 符号位 —— 数字的符号,最大单位值和可选的(之后再讨论)
  • 指数位 ——一些位以偏移整数格式表示,在与数字的其余部分相乘之前描述2的有偏指数
  • 尾数位 ——在乘指数前,一些位表示数的小数部分。假定有一个开头的1,小数点,所以01011000的尾数对应数字1.01011000表示的二进制(基为2)。

因此最后的典型的数字是符号位( /- 1) * 2解码的指数* 1.尾数

有很多用特殊指数最小值和最大值的“特殊”案例(denorm,无限,NaN,零),但这篇文章的目的,我们之后只关注一个特殊的情况——零的编码——将所有指数和尾数位置零实现。(注:因为符号位仍然可以设置,有两个零, 0- 0)。

浮点是一个非常聪明的表示,有很多漂亮的特性(例如:解释为整数的正浮点数可以排序或原子地取最小/最大值!整数零正好对应于正浮点零),然而,伴随着精度的许多问题并不总是最直观的。我只在这里提及其中一些——与讨论问题有关的部分。

普通和小浮点

到目前为止,我试图保持通用,没有指定任何比特数,但在硬件(或软件仿真)中使用浮点,我们需要定义它们。

下面是一个表,显示了常规32位浮点的各种位深度以及图形硬件/标准使用的半浮点和11位和10位浮点:

位深度

存在符号位?

指数位

尾数位

32

Yes

8

23

16

Yes

5

10

11

No

5

6

10

No

5

5

我们马上就可以看到一些有趣的观察:

同时,由于11 11 10浮点格式不同的比特深度,问题是来自蓝色通道和其他通道的不同尾数位深度——会产生各种变色和色调变化——类似于经常在BC1块压缩中出现的情况(有565个端点的位深度),但不是绿色/紫色,而是黄色/蓝色。我会在后面给一个例子。显然,这一决定是有道理的——11 11 10格式,非常适合一个单一的双字节值,而且感官上,人类视觉对于蓝色通道最不敏感。

因此,正如我们所看到的,我们在将32位浮点转换为1611/10位的过程中,正在丢失大量的信息。此外,在指数和尾数之间的信息丢失是不成比例的——在每个小浮点情况下,我们都会在尾数中丢失更多信息。这可能会导致一些量化和连带误差。

在分析量化之前,有一件事要提一下——IEEE标准定义了几种不同的舍入模式(例如,到最接近的、到零的、到正无穷的和到负无穷的)。我不认为它们基于GPU(至少在标准、跨供应商的API上)有任何可配置的方式,本篇文章的后半段会忽略这种复杂性,假设用的是简单的四舍五入。

小浮尾数精度——实例

我希望前面的部分,加上看一些数字的比特深度可以清楚地展示:因为很小的尾数问题,损失较小格式的浮点数的数值精度。

首先,一些数值例子。让我们取3个简单的8位整数值,并将它们表示为01范围的浮点——颜色的通用操作。

N[252/255, 8]
0.98823529

N[253/255, 8]
0.99215686

N[254/255, 8]
0.99607843

让我们试着用浮点数来表示它们。利用浮点值的知识,知道尾数总是始于一,我们需要将它们乘上2,指数就是2-1

乘法后我们得到:

BaseForm[N[2*252/255, 8], 2]
1.1111100111111001111110100

BaseForm[N[2*253/255, 8], 2]
1.1111101111111011111111000

BaseForm[N[2*254/255, 8], 2]
1.1111110111111101111111100

      我加粗了前5位,为什么?想想10位半浮点只有5位尾数!因此10位半浮点(R11 G11 b10f的蓝色通道)甚至不能准确表示三个几乎最后8位的颜色值!同时,你可以看到下一位确实不同——因此这三个数字会在11F产生两种不同的值,产生白色值错误的颜色。

小浮点尾数精度–可视化

好的,所以我们知道小浮点甚至不能准确反映简单的8位亮度!但是这到底有多糟糕?我创建了一些Mathematica(数学软件)可视化(见链接页面顶部)——首先,最坏的情况下,B10F,因此舍弃了18位的尾数。

情况看起来很好(甚至更好——考虑到浮点是如何编码的就不意外了!)接近于零,但误差开始增多,与线性8位值量化误差相比几乎是其4倍!

这是很不公平的比较,——我们不使用8位线性颜色而使用sRGB,是由于对黑暗与明亮的感知灵敏度(“伽马”),所以不要在意那些明亮的区域,决定将更多信息编码到较暗的部分。这就是3种编码方法的比较:

      好,还有点事。看起来10位浮点精度对于值达到线性0.125的值要好点,但是之后就不好了。误差最大值对于10位浮点来讲几乎是1,大了两倍,这样不好……这会在光滑梯度上带来可见带(visible band)。

轻松一下,额外的可视化,相关的误差(被原始值分开):

如所期望的那样,浮点值量化相关误差是有界限的,而且在对应于下一个指数的范围中有最大值(如果我们这里不考虑比最小规范化浮点表示还小),但随着我们接近08位线性或者sRGB相关误差会增加。浮点相关误差也在“带(band)”中展现了,对应于下一个指数,误差在两个相邻带之间变大2倍。

我们会继续研究怎样改进,但是首先——再说一点关于第二个问题的事情。

小浮点不均匀尾数长度问题

因为R11G11B10浮点尾数的比特长度分布不均匀,它们数字转换将会不同。厉害到什么程度?与浮点数一样,绝对误差取决于范围:

数越大——误差越大 。此图后面的部分看起来太糟糕了:

这种不同的量化在实际中意味着什么?这意味着信号会出现变色/错误的饱和。让我们看看一个简单的从0.50.6的渐变。

如果你有很好的显示器/查看条件,就会发现这种渐变是非常糟糕的。现在想象一下,和你一起工作的导演喜欢强烈反差,喜欢过度饱和的像电影一样的渐变:

这个看起来很难用……我们来看看如何改进它。在这篇文章中是通过改变信号的动态范围,在下一篇博客中是通过抖动。

重新调节没有用

一个普遍的误解是,把一个浮点数乘上大数,编码再在解码之后分开就可以了。这是不行的,比如说,让我们看看用16左乘时的量化误差:

零差!为什么?让我们考虑在浮点表示除以16中意味着什么。嗯,尾数是不会改变的!唯一的事情是,我们将从指数减去4。所以由于尾数量化产生的相对误差都是一样的。你可以试着在1/22之间乘以一个数字,我们会看到范围变化中的差异,但是它只会把误差转移到更多的白色或更多的黑暗部分:

错误带只向左或向右滑动。

应用一些γ法改善误差

让我们来看看一个不同的方法——这种方法将利用如下事实:即(如果图像是预曝光的!)在大多数精度(为了达到约束的相对精度)被定位的情况下,我们可能不关心非常小的值。

我在之前的博客中提到过关于动态范围精度问题的常用解决方法——利用一定的信号幂次拉伸动态范围(更小或更大)。为了以整数存储更高精度的图像暗区,我们希望采用较低的编码幂次,例如著名的伽玛12.2。然而,在这种情况下,我们想做……相反的事情!采用更大的幂次——要想知道为什么这么做,可以看看我们原来引入sRGB变量的比较:

我们把固定的界限内不断振荡的蓝线重新调节成增长的蓝线。这里10位浮点数存在相反的问题——我们有一个渐进增长太快的函数——我们要去掉它。

想一想,这是个很有趣的问题。它与浮动精度分布方式有关——它是非线性的对数分布,能很好地处理较大的动态范围;此外,指数型信号曲线几乎是线性表示的!因此,要从我们低位深度的浮点表示中获得最多信息,我们希望在编码之前尽可能地增加动态范围。比如说我们可以这样做:把信号开方或采取更大的幂次。我使用的三个初始浮点,实际上需要相当大的指数,3是给定值:

BaseForm[N[2*(252/255)*(252/255)*(252/255), 8], 2]
1.1110111000100100001001000

BaseForm[N[2*(253/255)*(253/255)*(253/255), 8], 2]
1.1111010000001100000101000

BaseForm[N[2*(254/255)*(254/255)*(254/255), 8], 2]
1.1111101000000000000001000

注意它们是不同的(尽管前两个将以同样的方式四舍五入)。

让我们看看应用γ3的绝对误差(注:本图假设反规范处理正确,详见下文):

我们的误差看起来比8sRGB误差小——这可能已经是相当有用的存储库了。我们以前的带状梯度也看起来更好,它的高对比度的版本也更好了(虽然不完美——回忆那种重做伽马的对比):

之前:

之后:

对比前:

对比后:

但天下没有免费的午餐!

首先,有ALU的成本。当我们每3个通道做一次这个操作时,ALU会变得相当重要!x*x*x是两个全速率操作,但如powx1 / 3)是log2 EXP2 乘,所以每个颜色通道2个四分速率 1个全速率= 9FR指令!廉价的变量只有开方,sqrtx)是一个四分速率指令,与4FR指令等效。

其次,这个数据现在显然不是可过滤的/可混合的……在这个空间上混合会带来过亮的问题。这是一个重大问题(如果你需要硬件混合,或者用双线性挖掘进行重采样),但也许不是(如果你可以手动/在计算着色器中这样做)。

第三,这个额外的精度是通过牺牲动态范围来实现的。通过使用的伽马,它的绝对值等于用伽马除指数的绝对值。例如,对于γ3,我们最大的可表示值大约是pow65000,1 / 3~ = 40!只有40HDR够你用吗?如果预先曝光的场景可能够用,但最热的点将被剪辑……开方的变量看起来更好,大约250

潜在的小数问题

注意:这一节在Tom ForsythPeter Pike Sloan修正后略有改写。我的假设是悲观的(denorm刷新到零),但显然,GPU必须正确处理好,如DirectX中的GPU。多谢你注意到这件事!

另一个问题可能是在不同的部分——最小可表示的数。相同的指数除法绝对值是用于最小可表示数的!因此,应用γ3后,最小可表示的数将为0.03125,大约是8 / 255,如果我们没有denormdenorm刷新到零,这将导致裁剪!没有处理denorm,放大的误差实际走势图如下:

图如下:

你可以通过预曝光尝试修复它,如4

但这张图不仅不完美,而且从最高范围内数据又开始不清晰了。(最热的可表示值)不是已经限制的40,你只能得到10!即使在HDR电视上显示信号,这也是不够的……

因此,如果不正确处理denorm,我宁愿推荐坚持γ2,预曝光值为4,接受略高的量化误差:

幸运的是,我得到了纠正——denorm没有不正确处理,我们可以假设,denorm会被处理——所以如果需要的话可以使用那些较大的指数——只是需要考虑我们在上面/较高部分牺牲了多少动态范围。

在完成这一部分之前,有一个有趣的边注:你曾经考虑过在16位浮点操作时标准化浮点精度有多低吗?半浮点有相同的指数位深度,所以,如果你对15位浮点应用对比的操作,你可能会很快进入denorm范围!这在理论上可能导致裁剪。

未经检验的思想–使用YCoCg色彩空间?

一些有趣的(?)想法是可以尝试使用一些不同的像YCoCg这样的颜色空间,或用相似的代替RGB。在(有符号的)YCoCg的色度更小=CG成分的量级更小=更高的精度。这将有助于降低颜色通道之间的关联,当颜色不太饱和时(和当这些变化更明显时)避免丑陋的色度变化。

不幸的是,R11G11B10没有符号位可用——我们需要在“某处”存储两个额外的符号位(不同的表面?尾数最低位/指数最高位?)。

总结——到底用不用R11G11B10 还是不用?

      R11G11B10和小的1110位的花车有许多局限性,但也是极其引人注目的存储格式。与RGBA16F相比,它们将记忆存储和带宽的要求减半,通过一些数值技巧提供大多数彩色编码方案可以接受的精度之后,能够存储高动态范围的信号。对于非临界信号(环境缓冲区、许多后效应缓冲区),我经常使用它们,但我认为如果不需要alpha混合或过滤,也可以对常规的颜色缓冲区进行使用,并且可以稍微修改输入数据。

      更新:我从Volga AksoyTom Forsyth得到一些消息,Oculus SDK现在支持并建议按这种格式输出,所以它绝对实用。因为带有HMD的黑暗/完美的观看条件,人类的感知是黑暗和R11G11B10F中更敏感,在较低范围内比8 sRGB效果好。

在下一篇文章中,我将展示如何抖动浮点,并获得更好的结果,几乎没有感知带(这么做换取噪音)。

奖励——与10sRGB比较

作为一个小的奖励,与10sRGB编码简单比较一下(没有硬件支持,但一些视频库支持它允许更精确的颜色配置文件/曲线转换)。两图显示误差在0-1之间,全在0-0.1偏暗范围内。

我们可以看到,10sRGB更优于大多数的范围,但在非常低/暗的值中,10位浮点是同等的,甚至更优秀一点。。

参考文献

https://www.opengl.org/wiki/Small_Float_Formats

http://steve.hollasch.net/cgindex/coding/ieeefloat.html Steve Hollasch, “IEEE标准754浮点数

http://community.wolfram.com/groups/-/m/t/274061 Mathematica 帮助文档——把浮点数表示法转换到任何科学记数法

https://msdn.microsoft.com/en-us/library/windows/desktop/cc308050(v=vs.85).aspx#alpha_11_bit Direct3D 10个浮点规则

 

【版权声明】

原文作者未做权利声明,视为共享知识产权进入公共领域,自动获得授权;

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

游戏学院公众号二维码
腾讯游戏学院
微信公众号

提供更专业的游戏知识学习平台