1

在常规 24 bpp 显示器上显示每通道超过 8 位的最忠实图形的最佳方式是什么?

4

1 回答 1

6

我能想到的最佳解决方案是基于改变每一帧的随机抖动。这结合了抖动与不具有固定抖动模式的优点,并且由于给定像素每秒多次更改值,因此您认为更接近这些不同值的平均值,这更接近原始“深色”值而不是任何给定的 24 bpp 值。

它看起来如何

渐变的绿色,无抖动,抖动(显示 10 帧),然后以相同的方式增强可见性:

带状渐变

抖动渐变

增强的带状渐变

增强的抖动渐变

抖动

抖动是通过将每个通道的伽马压缩深色值与随机值相加,然后四舍五入到最接近的 8 位值来实现的。使用在 -0.5 和 0.5 之间均匀分布的随机数似乎很自然(我说的是在 8 位伽马压缩值中相当于 1 的单位,例如 0 和 1 或 254 和 255 之间的差异) ,但是这会导致一种带状伪影,其中接近 8 位值的梯度值几乎没有噪声,而离任何 8 位值最远的值会显示更多噪声。高斯噪声更合适,因为它提供了更平滑的噪声水平。我选择了 1.0 的 sigma,但为了减少噪音,可以使用 0.8 的 sigma。

您可以通过取两个随机数来创建高斯 PRNG,n1并将n2它们分别拟合在 [-1 , 1] 范围内,并且它们是否代表单位圆内的一个点(如果sum它们的平方和小于或等于 1 ,否则重新开始)返回sqrt(-2. * log(sum) / sum) * n1

实际实施

我选择通过将每通道 15 位的线性 RGB 帧缓冲区转换为每通道 8 位的 sRGB 帧缓冲区来实现这一点。sRGB 的线性部分只是一个细节,我使用查找表将线性值转换为 gamma 压缩值(我选择使这些中间值使用 13 位,您可以将其视为 sRGB 值的 8.5 定点符号)。

不用说,您不会为每个像素生成一个新的随机高斯数,您需要预先计算一堆并将它们放入循环缓冲区中。我选择了 16384 个,是的,只有 16384 个,我通过在这个缓冲区中选择一个随机入口点来避免任何重复模式,一个随机长度(在 100 到 1123 之间,这是非常任意的),当我到达长度的结束我选择了一个新的随机起点和一个新的随机长度。通过这种方式,我可以从相对较小的数字缓冲区中获得相当随机的非重复模式。缓冲区中的数字以 2.5 定点格式存储,这样它们都在 -4.0 和 4.0 之间,涵盖了我想要的高斯随机数范围。只需确保添加 0。

以下是每个像素和每个通道的基本工作原理:

15 位线性值——通过 LUT——> 13 位(8.5 定点)伽马压缩值,然后添加 2.5 定点随机数,然后向右 SHIFT 5 位。

现在你得到一个介于 -4 和 260 之间的整数值,你可以使用 if()s 来限制它们,但是使用 264 元素 LUT 会更快,它为负数返回 0(你可以通过分配使用负数作为索引您的缓冲区然后执行 buffer = &buffer[4],我猜会为您节省一个加法)并且对于 255 以上的数字返回 255。此外,我对三个颜色通道中的每一个都使用相同的随机数,这可以避免色度噪声,尽管可以说是如果这三个使用独立的数字,结果可能看起来不那么嘈杂。

对于单个像素的红色通道,我的代码如下所示: sfb[i].r = bytecheck_l.lutb[lsrgb_l.lutint[fb[i].r] + dither_l.lutint[id] >> 5]; sfb 是 sRGB 24 bpp 缓冲区,fb 是 45 bpp 线性 RGB 缓冲区,lsrgb_l.lutint[] 是线性到伽马压缩 LUT,dither_l.lutint[] LUT包含 2.5 定点格式的随机高斯数和 bytecheck_l.lutb[] 返回值裁剪为 [0 , 255]。

表现

我在 1400x820 SDL 窗口中获得超过 50 FPS 的测试梯度,仅使用 2.4 GHz Core 2 Quad Q6600 的一个内核和双通道 800 MHz DDR2 内存,按当前标准来看,这台机器有点平庸,所以这个解决方案似乎绝对适合现代电脑。

如果我的任何解释需要澄清,请告诉我。

于 2013-04-03T17:11:45.553 回答