【问题标题】:How to quantize floating point to unsigned byte in GLSL如何在 GLSL 中将浮点量化为无符号字节
【发布时间】:2016-05-03 07:25:13
【问题描述】:

我在 GLSL 中使用浮点纹理作为数据缓冲区,需要将数据保存在普通纹理上(每个像素的颜色有 1 个字节)。在我的情况下,浮点是 [-2048.0, 2048.0],所以我必须将 [-2048.0, 2048.0] 量化为 [0, 255]。我认为这个问题的 C++ 代码是这样的:

//*quantization*
float fvalue = ... ;  // floating point data
fvalue /= 16.0f;  // [-128.0, 128.0]
fvalue = roundf(fvalue);  // [-128, 128]
if(fvalue > 127.0f) fvalue = 127.0f;
else if(fvalue < -128.0f) fvalue = -128.0f;
u_char byte = (int)fvalue + 128;  // [0, 255]

//*inverse quantization*
u_char byte = ...;  // [0, 255]
float fvalue = byte - 128;  // [-128, 127]
fvalue *= 16.0f;  // [-2048, 2032] (it can't be helped?)

我不确定这段代码是否良好,但我也不确定 GLSL 的优点是什么(GLSL 将字节值 [0, 255] 处理为浮点 [0.0, 1.0])。我的代码是:

//*quantization*
vec3 F = ...;  //F is floating vector [-2048.0, 2048.0]
F /= 16;  // [-128.0, 128.0]
F /= 256;  // [-0.5, 0.5]
F += vec3(0.50f);  // [0.0, 1.0]
gl_FragData[0] = vec4(F, 1.0);

//*inverse quantization*
vec3 F = texture2D(...); //byte data [0.0, 1.0]
F -= vec3(0.50f); //byte data [-0.5, 0.5]
F *= 256; //[-128, 128]
F *= 16; //[-2048, 2048]

这并不好。但是,如果我将代码 F += vec3(0.50f); 重写为 F += vec3(0.51f); 并将 F -= vec3(0.50f); 重写为 F -= vec3(0.51f);,似乎效果很好。但我认为0.51f 的值不合理。事实上,这在一种硬件中运行良好,而在另一种硬件中运行不佳。 我想知道量化(也是 inv-quantize)浮点值的好方法。

【问题讨论】:

  • 定义“效果不佳”。另外,您允许使用哪些版本的 OpenGL?
  • 表示“没有量化好”;如果我将浮点 f 转换为 intefer b 并再次转换为 f',则错误 f-f' 很大。我使用 OpenGL 2.0(也是 OpenGL ES 2.0)。
  • 多大?误差是否大到不合理?发布实际数字。
  • 在 GLSL 中获取纹理像素的实际值非常困难。但是,它可以说是我可以“看到”错误的最大程度。假设我要压缩要解压缩的图像数据。解压后得到相同的图像是理想的,但不可能,因为这些过程需要量化和逆量化。如果图像变得有些退化,这是一个小错误。但是,如果图像的白色像素变成绿色像素,那就是一个很大的错误,我可以“看到”。
  • "在 GLSL 中获取纹理像素的实际值非常困难。" 你使用glGetTexImage。下载纹素数据与上传并没有什么不同。

标签: c++ opengl floating-point glsl


【解决方案1】:

我可以找到“好”的方法。恐怕我无法合理解释它为什么起作用,所以我不知道这是否是通用方法。

//*quantization*
vec3 F = ...;  //F is floating vector [-2048.0, 2048.0]
F += 2048;
F /= 16;
F /= 255;
gl_FragData[0] = vec4(F, 1.0);

//*inverse quantization*
vec3 F = texture2D(...);  //byte data [0.0, 1.0]
F *= 255.0;
F *= 16.0;
F -= vec3(2048 + 8);  //adding bias -16.0/2.0
F = 2.0 * F * qp * Q / 16.0;

【讨论】:

  • 我认为你想多了。只需乘以并添加以更改范围。 F' = F*(.5/2048.0)+.5。为什么多个整数除法?而倒数就是 F=(F'-.5)*(2048.0*2.0)=F'*2048.0*2.0-2048.0。两者都将编译为单个 MAD 指令。从 [a..b] 到 [0..1] 的通用方法是 x'=(x-a)/(b-a) = x/(b-a) - a/(b-a)。
【解决方案2】:

首先,每个具有 1 个字节的像素并不能充分传达您要描述的内容。这种所谓的“正常纹理”更准确地称为“无符号归一化”(通常缩写为unorm)。

您需要一个 8 位 unorm 纹理(理想情况下具有多个组件);这些是存储定点数据的纹理,在通过将数据标准化到其固有范围进行采样时被视为浮点数(在 [0.0,1.0] 范围内) (eg 提升为浮点数并除以 255.0)。

鉴于刚才的描述,您只需将原始数据 [-2048.0,2048.0] 转换为 [0.0,1.0],然后乘以 255

不过,这是相当不可取的,因为您将无法在没有严重混叠的情况下表示原始范围。相反,乘以 4294967295 (2564-1) 并将 8 位打包成 R,8 位到 G 中,8 位到 B 中,8 位到 A 中。您没有尝试将组件打包到所示着色器中。

【讨论】:

    猜你喜欢
    • 2017-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-07
    相关资源
    最近更新 更多