如何将 vec4 rgba 值转换为浮点数?
我将一些浮点数据作为 unsigned_byte 打包在纹理中,这是我在 webgl 中唯一的选择。现在我想将其解压到顶点着色器中。当我对一个像素进行采样时,我得到一个 vec4,它实际上是我的浮点数之一。如何从 vec4 转换为浮点数?
I packed some float data in a texture as an unsigned_byte, my only option in webgl. Now I would like unpack it in the vertex shader. When I sample a pixel I get a vec4 which is really one of my floats. How do I convert from the vec4 to a float?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
以下代码专门针对使用 OpenGL ES 2.0 的 iPhone 4 GPU。我没有 WebGL 经验,所以我不能声称知道代码在该上下文中如何工作。此外,这里的主要问题是 highp float 不是 32 位,而是 24 位。
我的解决方案是针对片段着色器的 - 我没有在顶点着色器中尝试它,但它不应该有任何不同。为了使用它,您需要从 Sampler2d 制服获取 RGBA 纹素,并确保每个 R、G、B 和 A 通道的值在 0.0 和 255.0 之间。这很容易实现,如下所示:
您应该知道,机器的字节顺序将决定字节的正确顺序。上面的代码假设浮点数以大端顺序存储。 写入即可交换数据顺序
如果您发现结果错误,则只需在设置数据的行之后立即 。或者交换 rgba 上的索引。我认为上面的代码更直观,并且不太容易出现粗心错误。
我不确定它是否适用于所有给定的输入。我测试了大范围的数字,发现decode32和encode32不是精确的逆。我还遗漏了我用来测试它的代码。
以下是我发现有用的一些有关 IEEE 精度的链接。 链接1。 链接2。 链接3。
The following code is specifically for the iPhone 4 GPU using OpenGL ES 2.0. I have no experience with WebGL so I cant claim to know how the code will work in that context. Furthermore the main problem here is that highp float is not 32 bits but is instead 24 bit.
My solution is for fragment shaders - I didnt try it in the vertex shader but it shouldnt be any different. In order to use the you will need to get the RGBA texel from a sampler2d uniform and make sure that the values of each R,G,B and A channels are between 0.0 and 255.0 . This is easy to achieve as follows:
You should be aware though that the endianess of your machine will dictate the correct order of your bytes. The above code assumes that floats are stored in big-endian order. If you see your results are wrong then just swap the order of the data by writing
immediately after the line where you set it. Alternatively swap the indices on rgba. I think the above line is more intutive though and less prone to careless errors.
I am not sure if it works for all given input. I tested for a large range of numbers and found that decode32 and encode32 are NOT exact inverses. Ive also left out the code I used to test it.
Here are some links on IEEE precision I found useful. Link1. Link2. Link3.
Twerdster 在他的回答中发布了一些优秀的代码。所以所有的功劳都归于他。我发布了这个新答案,因为注释不允许使用漂亮的语法彩色代码块,并且我想分享一些代码。但如果您喜欢该代码,请投票给 Twerdster 原始答案。
在 Twerdster 之前的文章中,他提到解码和编码可能不适用于所有值。
为了进一步测试这一点,并验证结果,我编写了一个java程序。在移植代码时,我尝试尽可能接近着色器代码(因此我实现了一些辅助函数)。
注意:我还使用存储/加载函数来模拟从纹理写入/读取时发生的情况。
我发现:
float Mantissa = (exp2(- Exponent) * F);
更改为float Mantissa = F/exp2(Exponent);
以减少精度误差float Exponent = Floor(log2(F));
计算指数。 (通过新的尾数检查进行简化)使用这些小的修改,我在几乎所有输入上获得了相同的输出,并且当出现问题时,原始值和编码/解码值之间只有很小的错误,而在 Twerdster 的原始实现中,舍入错误通常会导致指数错误(因此结果相差两倍)。
请注意,这是我为测试算法而编写的 Java 测试应用程序。我希望移植到 GPU 上时也能正常工作。如果有人尝试在 GPU 上运行它,请留下您的经验评论。
对于带有简单测试的代码,尝试不同的数字,直到失败。
Twerdster posted some excellent code in his answer. So all credit go to him. I post this new answer, since comments don't allow for nice syntax colored code blocks, and i wanted to share some code. But if you like the code, please upvote Twerdster original answer.
In Twerdster previous post he mentioned that the decode and encode might not work for all values.
To further test this, and validate the result i made a java program. While porting the code i tried to stayed as close as possible to the shader code (therefore i implemented some helper functions).
Note: I also use a store/load function to similate what happens when you write/read from a texture.
I found out that:
float Mantissa = (exp2(- Exponent) * F);
tofloat Mantissa = F/exp2(Exponent);
to reduce precision errorsfloat Exponent = floor(log2(F));
to calc exponent. (simplified by new mantissa check)Using these small modifications i got equal output on almost all inputs, and got only small errors between the original and encoded/decoded value when things do go wrong, while in Twerdster's original implementation rounding errors often resulted in the wrong exponent (thus the result being off by factor two).
Please note that this is a Java test application which i wrote to test the algorithm. I hope this will also work when ported to the GPU. If anybody tries to run it on a GPU, please leave a comment with your experience.
And for the code with a simple test to try different numbers until it failes.
我尝试了 Arjans 解决方案,但它返回了 0、1、2、4 的无效值。指数的打包存在一个错误,我对此进行了更改,因此 exp 需要一个 8 位浮点数,并且符号用尾数打包:
I tried Arjans solution, but it returned invalid values for 0, 1, 2, 4. There was a bug with the packing of the exponent, which i changed so the exp takes one 8bit float and the sign is packed with the mantissa:
由于您没有打算向我们提供您用于创建和上传纹理的确切代码,因此我只能猜测您在做什么。
您似乎正在创建一个 JavaScript 浮点数数组。然后创建一个 Uint8Array,将该数组传递给构造函数。
根据 WebGL 规范(或者更确切地说,WebGL 规范在表面上指定此行为时引用的规范),从浮点到无符号字节的转换根据目标以两种方式之一发生。如果目标被视为“固定”,则它将数字固定在目标范围内,即您的情况的 [0, 255] 。如果目的地不被视为“限制”,则以 28 为模。 WebGL“规范”非常糟糕,以至于不完全清楚 Uint8Array 的构造是否被认为是限制的。无论是钳位还是取模 28,小数点都会被截去并存储整数值。
但是,当您将此数据提供给
OpenWebGL 时,您告诉 WebGL 将字节解释为标准化的无符号整数值。这意味着纹理用户将把 [0, 255] 范围内的输入值作为 [0, 1] 浮点值进行访问。因此,如果输入数组的值为 183.45,则 Uint8Array 中的值将为 183。纹理中的值将为 183/255,即 0.718。如果您的输入值为 0.45,则 Uint8Array 将保存 0,并且纹理结果将为 0.0。
现在,因为您将数据作为 GL_RGBA 传递,这意味着每 4 个无符号字节将被视为一个纹理元素。因此,每次调用
texture
都会获取这四个特定的值(在给定的纹理坐标处,使用给定的过滤参数),从而返回一个vec4
。目前尚不清楚您打算如何处理此浮点数据,因此很难就如何最好地将浮点数据传递到着色器提出建议。但是,通用解决方案是使用 OES_texture_float 扩展并实际创建存储浮点数据的纹理。当然,如果不可用,您仍然需要找到一种方法来完成您想要的操作。
顺便说一句,Khronos 确实应该为自己将 WebGL 称为规范而感到羞耻。它几乎没有指定任何内容;它只是一堆对其他规范的引用,这使得找到任何东西的效果变得极其困难。
Since you didn't deign to give us the exact code you used to create and upload your texture, I can only guess at what you're doing.
You seem to be creating a JavaScript array of floating-point numbers. You then create a Uint8Array, passing that array to the constructor.
According to the WebGL spec (or rather, the spec that the WebGL spec refers to when ostensibly specifying this behavior), the conversion from floats to unsigned bytes happens in one of two ways, based on the destination. If the destination is considered "clamped", then it clamps the number to the destination range, namely [0, 255] for your case. If the destination is not considered "clamped", then it is taken modulo 28. The WebGL "specification" is sufficiently poor that it is not entirely clear whether the construction of Uint8Array is considered clamped or not. Whether clamped or taken modulo 28, the decimal point is chopped off and the integer value stored.
However, when you give this data to
OpenWebGL, you told WebGL to interpret the bytes as normalized unsigned integer values. This means that the input values on the range [0, 255] will be accessed by users of the texture as [0, 1] floating point values.So if your input array had the value 183.45, the value in the Uint8Array would be 183. The value in the texture would be 183/255, or 0.718. If your input value was 0.45, the Uint8Array would hold 0, and the texture result would be 0.0.
Now, because you passed the data as GL_RGBA, that means that every 4 unsigned bytes will be taken as a single texel. So every call to
texture
will fetch those particular four values (at the given texture coordinate, using the given filtering parameters), thus returning avec4
.It is not clear what you intend to do with this floating-point data, so it is hard to make suggestions as to how best to pass float data to a shader. However, a general solution would be to use the OES_texture_float extension and actually create a texture that stores floating-point data. Of course, if it isn't available, you'll still have to find a way to do what you want.
BTW, Khronos really should be ashamed of themselves for even calling WebGL a specification. It barely specifies anything; it's just a bunch of references to other specifications, which makes finding the effects of anything exceedingly difficult.
您将无法在着色器中(至少在 GLES 或 WebGL 中,我认为)将 4 个无符号字节解释为浮点值的位(我假设您想要)。您可以做的不是将浮点的位表示存储在 4 个 ubyte 中,而是存储尾数的位(或定点表示)。为此,您需要知道浮点数的大致范围(为了简单起见,我在这里假设为 [0,1],当然,否则您必须以不同的方式进行缩放):
当然,您也可以直接使用尾数位。然后在着色器中,您可以利用
vec4
的组件全部位于 [0,1] 中的事实来重建它:虽然我不确定这是否会导致完全相同的值,两个的幂应该有一点帮助。
You won't be able to just interpret the 4 unsigned bytes as the bits of a float value (which I assume you want) in a shader (at least not in GLES or WebGL, I think). What you can do is not store the float's bit representation in the 4 ubytes, but the bits of the mantissa (or the fixed point representation). For this you need to know the approximate range of the floats (I'll assume [0,1] here for simplicity, otherwise you have to scale differently, of course):
Of course you can also work directly with the mantissa bits. And then in the shader you can just reconstruct it that way, using the fact that the components of the
vec4
are all in [0,1]:Although I'm not sure if this will result in the exact same value, the powers of two should help a bit there.