我一直在寻找一种在WebGL纹理上存储浮点数的方法。我在互联网上找到some solutions,但这些只涉及[0..1]范围内的浮动。我希望能够存储任意浮点数,并且,为此,需要扩展这样的函数以存储指数(例如,在第一个字节上)。我不太明白这些是如何工作的,所以如何做到这一点并不明显。简而言之:
将浮点数打包成4个字节的有效算法是什么?
答案 0 :(得分:2)
一种简单的方法是首先就你所支持的漂浮范围达成一致,并在包装前将其重新映射到[0 ... 1)范围。
const MIN = -100;
const MAX = 100;
function packRemap(v){
return (v - MIN) / (MAX - MIN);
}
function unpackRemap(p){
return MIN + p * (MAX - MIN);
}
答案 1 :(得分:2)
嗯,float
是一个32位数字(23 bits for mantissa + 1 bit implicitly, 8 bits for exponent and 1 bit for sign),RGBA8纹理的纹素也是32位。因此,我们只需要一种编码方案,可以用JS(或任何其他语言)打包并在GLSL中解压缩(给定限制os GLSL ES 1.0,例如缺少按位操作)。这是我的建议(在C ++中):
#include <cstdint>
#include <iostream>
#include <cmath>
// for storing result of encoding
struct rgba {
uint8_t r, g, b, a;
};
rgba float2rgba(float x) {
union {
float xc;
uint32_t xi;
};
// let's "convert" our float number to uint32_t so we can mess with it's bits
xc = x;
// in v we'll pack sign bit and mantissa, that would be exactly 24 bits
int_least32_t v =
// sign bit
(xi >> 31 & 1) |
// mantissa
((xi & 0x7fffff) << 1);
rgba r;
// then we just split into bytes and store them in RGB channels
r.r = v / 0x10000;
r.g = (v % 0x10000) / 0x100;
r.b = v % 0x100;
// and we'll put the exponent to alpha channel
r.a = xi >> 23 & 0xff;
return r;
}
float rgba2float(rgba r) {
// let's "rebuild" mantissa and sign bit first
uint32_t v = (r.b / 2) + r.g * 0x80 + r.r * 0x8000;
return
// let's apply sign (it's in least significant bit of v)
(r.b % 2 ? -1.f : 1.f) *
// and reconstruct the number itself
(1.f + v * pow(2.f, -23.f)) * pow(2.f, static_cast<unsigned>(r.a) - 127);
}
int main() {
const float a = -1.34320e32f;
rgba r = float2rgba(a);
std::cout <<
a << '\n' <<
static_cast<unsigned>(r.r) << ',' <<
static_cast<unsigned>(r.g) << ',' <<
static_cast<unsigned>(r.b) << ',' <<
static_cast<unsigned>(r.a) << '\n' <<
rgba2float(r) << std::endl;
}
输出:
-1.3432e+32
167,214,213,233
-1.3432e+32
答案 2 :(得分:1)
我不确定我是否理解这个问题但是。
为什么不使用浮点纹理?
var ext = gl.getExtension("OES_texture_float");
if (!ext) {
// sorry no floating point support)
}
至于将数据放入纹理,只需使用Float32Array
。
var data = new Float32Array([0.123456, Math.sqrt(2), ...]);
gl.texImage2D(gl.TARGET_2D, 0, gl.RGBA, width, height, 0,
gl.RGBA, gl.FLOAT, data);
大多数硬件都支持从浮点纹理读取。渲染到浮点纹理的支持较少。见WebGL iOS render to floating point texture
我还要指出,你可以在JavaScript中的浮点数中获取字节
var arrayOf10Floats = new Float32Array(10);
var arrayOf40bytes = new Uint8Array(arrayOf10Floats.buffer);
这两个数组共享相同的内存。它们只是基础ArrayBufferView
的{{1}}。
答案 3 :(得分:1)
这太慢了,但是可行。 (在测试时,请注意GLSL 1.00浮点字面值不是您所期望的,转换存在很多错误)。
struct Bitset8Bits {
mediump vec4 bit0;
mediump vec4 bit1;
mediump vec4 bit2;
mediump vec4 bit3;
mediump vec4 bit4;
mediump vec4 bit5;
mediump vec4 bit6;
mediump vec4 bit7;
};
vec4 when_gt (vec4 l, vec4 r) {
return max(sign(l - r), 0.0);
}
Bitset8Bits unpack_4_bytes (lowp vec4 byte) {
Bitset8Bits result;
result.bit7 = when_gt(byte, vec4(127.5));
vec4 bits0to6 = byte - 128.0 * result.bit7;
result.bit6 = when_gt(bits0to6, vec4(63.5));
vec4 bits0to5 = bits0to6 - 64.0 * result.bit6;
result.bit5 = when_gt(bits0to5, vec4(31.5));
vec4 bits0to4 = bits0to5 - 32.0 * result.bit5;
result.bit4 = when_gt(bits0to4, vec4(15.5));
vec4 bits0to3 = bits0to4 - 16.0 * result.bit4;
result.bit3 = when_gt(bits0to3, vec4(7.5));
vec4 bits0to2 = bits0to3 - 8.0 * result.bit3;
result.bit2 = when_gt(bits0to2, vec4(3.5));
vec4 bits0to1 = bits0to2 - 4.0 * result.bit2;
result.bit1 = when_gt(bits0to1, vec4(1.5));
vec4 bit0 = bits0to1 - 2.0 * result.bit1;
result.bit0 = when_gt(bit0, vec4(0.5));
return result;
}
float when_gt (float l, float r) {
return max(sign(l - r), 0.0);
}
vec4 pack_4_bytes (Bitset8Bits state) {
vec4 data;
data = state.bit0
+ 2.0 * state.bit1
+ 4.0 * state.bit2
+ 8.0 * state.bit3
+ 16.0 * state.bit4
+ 32.0 * state.bit5
+ 64.0 * state.bit6
+ 128.0 * state.bit7;
return data;
}
vec4 brians_float_pack (
float original_value) {
// Remove the sign
float pos_value = abs(original_value);
float exp_real = floor(log2(pos_value));
float multiplier = pow(2.0, exp_real);
float normalized = pos_value / multiplier - 1.0;
float exp_v = exp_real + 127.0;
// if exp_v == -Inf -> 0
// if exp_v == +Inf -> 255
// if exp_v < -126.0 -> denormalized (remove the "1")
// otherwise + 127.0;
Bitset8Bits packed_v;
packed_v.bit7.a =
step(sign(original_value) - 1.0, -1.5); // pos
// Exponent 8 bits
packed_v.bit6.a = when_gt(exp_v, 127.5);
float bits0to6 = exp_v - 128.0 * packed_v.bit6.a;
packed_v.bit5.a = when_gt(bits0to6, 63.5);
float bits0to5 = bits0to6 - 64.0 * packed_v.bit5.a;
packed_v.bit4.a = when_gt(bits0to5, 31.5);
float bits0to4 = bits0to5 - 32.0 * packed_v.bit4.a;
packed_v.bit3.a = when_gt(bits0to4, 15.5);
float bits0to3 = bits0to4 - 16.0 * packed_v.bit3.a;
packed_v.bit2.a = when_gt(bits0to3, 7.5);
float bits0to2 = bits0to3 - 8.0 * packed_v.bit2.a;
packed_v.bit1.a = when_gt(bits0to2, 3.5);
float bits0to1 = bits0to2 - 4.0 * packed_v.bit1.a;
packed_v.bit0.a = when_gt(bits0to1, 1.5);
float bit0 = bits0to1 - 2.0 * packed_v.bit0.a;
packed_v.bit7.b = when_gt(bit0, 0.5);
// Significand 23 bits
float factor = 0.5;
// 0.4999999
// Significand MSB bit 22:
packed_v.bit6.b =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit6.b;
factor = 0.5 * factor;
packed_v.bit5.b =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit5.b;
factor = 0.5 * factor;
packed_v.bit4.b =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit4.b;
factor = 0.5 * factor;
packed_v.bit3.b =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit3.b;
factor = 0.5 * factor;
packed_v.bit2.b =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit2.b;
factor = 0.5 * factor;
packed_v.bit1.b =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit1.b;
factor = 0.5 * factor;
packed_v.bit0.b =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit0.b;
factor = 0.5 * factor;
packed_v.bit7.g =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit7.g;
factor = 0.5 * factor;
packed_v.bit6.g =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit6.g;
factor = 0.5 * factor;
packed_v.bit5.g =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit5.g;
factor = 0.5 * factor;
packed_v.bit4.g =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit4.g;
factor = 0.5 * factor;
packed_v.bit3.g =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit3.g;
factor = 0.5 * factor;
packed_v.bit2.g =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit2.g;
factor = 0.5 * factor;
packed_v.bit1.g =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit1.g;
factor = 0.5 * factor;
packed_v.bit0.g =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit0.g;
factor = 0.5 * factor;
packed_v.bit7.r =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit7.r;
factor = 0.5 * factor;
packed_v.bit6.r =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit6.r;
factor = 0.5 * factor;
packed_v.bit5.r =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit5.r;
factor = 0.5 * factor;
packed_v.bit4.r =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit4.r;
factor = 0.5 * factor;
packed_v.bit3.r =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit3.r;
factor = 0.5 * factor;
packed_v.bit2.r =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit2.r;
factor = 0.5 * factor;
packed_v.bit1.r =
when_gt(normalized, factor - 0.00000005);
normalized = normalized - factor * packed_v.bit1.r;
factor = 0.5 * factor;
// LSB bit 0
packed_v.bit0.r =
when_gt(normalized, factor - 0.00000005);
vec4 result = pack_4_bytes(packed_v);
return result;
}```
答案 4 :(得分:0)
由于我找不到任何解决我问题的方法,我已经收集了这个解决方案:
function fract(x){
return x - Math.floor(x);
};
function packFloat(x) {
var s = x > 0 ? 1 : -1;
var e = Math.floor(Math.log2(s*x));
var m = s*x/Math.pow(2, e);
return [
Math.floor(fract((m-1)*256*256)*256),
Math.floor(fract((m-1)*256)*256),
Math.floor(fract((m-1)*1)*256),
((e+63) + (x>0?128:0))];
}
function unpackFloat(v){
var s = v[3] >= 128 ? 1 : -1;
var e = v[3] - (v[3] >= 128 ? 128 : 0) - 63;
var m = 1 + v[0]/256/256/256 + v[1]/256/256 + v[2]/256;
return s * Math.pow(2, e) * m;
};
for (var i=0; i<10; ++i){
var num = (Math.random()*2.0-1.0)*1000;
console.log(num, packFloat(num), unpackFloat(packFloat(num)));
}
它将float转换为4个字节,来回。与其他解决方案相反,它不限于小的或预定义的范围,并且能够表示形状s * m * 2^e
上的任何数字,其中s = -1 or 1
,m = 1 til 2
(与24位精度)和e = -63 to 64
。将它移植到GLSL是微不足道的,因为它只使用常见的浮点运算。