在常规24 bpp显示器上显示每个通道超过8位的最忠实图形的最佳方法是什么?
答案 0 :(得分:4)
我能想到的最佳解决方案是基于随机抖动改变每一帧。这结合了抖动与不具有固定抖动模式的优点,并且由于给定像素每秒多次改变值,您所感知的值更接近于那些不同值的平均值,这更接近于原始的“深色”值。任何给定的24 bpp值。
绿色,无抖动,抖动的渐变(显示10帧),然后以相同的方式增强可见度:
通过为每个通道添加具有随机值的伽马压缩深色值,然后舍入到最接近的8位值来实现抖动。使用在-0.5和0.5之间具有均匀分布的随机数似乎很自然(我说的是在8位伽马压缩值中相当于1的单位,比如0和1或254和255之间的差值)然而,这会导致一种条带伪影,其中接近8位值的梯度值几乎没有噪声,而距离任何8位值最远的值会显示更多噪声。高斯噪声更合适,因为它可以提供更平滑的噪声水平。我选择了一个1.0的sigma,但是为了减少噪音,可能会有0.8的sigma。
您可以通过获取两个随机数n1
和n2
来创建高斯PRNG,使它们在[-1,1]范围内拟合,如果它们代表单位圆内的一个点(如果广场的总和sum
低于或等于1,否则重新开始)返回sqrt(-2. * log(sum) / sum) * n1
。
我选择通过将每通道15位线性RGB帧缓冲器转换为每通道8位sRGB帧缓冲器来实现此功能。线性到sRGB部分只是一个细节,我使用查找表将线性值转换为伽马压缩值(我选择使这些中间值使用13位,您可以将其视为sRGB值的8.5固定点表示法)。
不言而喻,你不会为每个像素生成一个新的随机高斯数,你需要预先计算一堆它们并将它们放在一个循环缓冲区中。我选择制作16384个,是的,只有16384个,我通过在这个缓冲区中选择一个随机入口点来避免任何重复模式,一个随机长度要经过(在100到1123之间,这是非常随意的),当我到达时我选择了一个新的随机起点和一个新的随机长度。这样,我可以从相对较小的数字缓冲区中获得非常随机的非重复模式。缓冲区中的数字以2.5定点格式存储,这样它们都在-4.0和4.0之间,涵盖了我想要的高斯随机数范围。只需确保将0.5添加到随机数中,因为这将在以后舍入到最接近的整数。
以下基本上是每个像素和每个频道的工作原理:
15位线性值 - 通过LUT - > 13位(8.5定点)伽马压缩值然后加上2.5个固定点随机数,然后向右移动5位。
现在你得到一个介于-4和260之间的整数值,你可以使用if()来限制它们,但是使用264元素LUT为负数返回0要快得多(你可以使用负数作为通过分配你的缓冲区然后做缓冲区=&缓冲区[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内存的一个内核,这是目前标准的有点平庸的机器,所以这个解决方案似乎绝对是适用于现代电脑。
如果我的任何解释需要澄清,请告诉我。