我正在尝试使用intBitsToFloat
在GLSL 3.3着色器中将整数标识符编码为Float输出(我正在使用highp输出float矢量),然后使用readPixels
将此值输入pixelData = new Float32Array(4)
。然后,我使用floatToIntBits(pixelData[0])
将其解码回JS int,其中</ p>
var int8 = new Int8Array(4)
var int32 = new Int32Array(int8.buffer, 0, 1)
var float32 = new Float32Array(int8.buffer, 0, 1)
var floatToIntBits = function (f) {
float32[0] = f
return int32[0]
}
现在我得到了一个非常奇怪的结果。即当我:
[0,2^23-1]
范围内的值,我在JS中总是得到0
[2^23,...[
范围内的值,我在JS中得到正确的值[-2^23+1,-1]
范围内的值,我在JS中总是得到-2^23+1
]..., -2^23]
范围内的值,我在JS中得到正确的结果有人知道为什么会发生吗?
答案 0 :(得分:1)
我猜这是因为您使用的是函数,而不是直接从readPixels
的结果中读取值。 Javascript只有1个数字类型,实际上是C / C ++ double
,因此,当您从typedarray中读取任何值时,它就会转换为double
让我们在没有GLSL的情况下进行测试
const i32 = new Int32Array([
123,
-123,
20000000,
-20000000,
]);
console.log(i32[0], i32[1], i32[2], i32[3]);
const f32 = new Float32Array(i32.buffer);
var int8 = new Int8Array(4)
var int32 = new Int32Array(int8.buffer, 0, 1)
var float32 = new Float32Array(int8.buffer, 0, 1)
var floatToIntBits = function (f) {
float32[0] = f
return int32[0]
}
console.log(floatToIntBits(f32[0]), floatToIntBits(i32[1]), floatToIntBits(i32[2]), floatToIntBits(i32[3]));
我上面得到的结果是
123 -123 20000000 -20000000
123 -1024065536 1268291200 -879192448
换句话说,我猜你在叫gl.readPixels
这样的东西
const pixels = new Float32Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.FLOAT);
现在像素是Float32s。
然后您得到一个这样的人
const v = pixels[someOffset];
此刻v
已转换为双精度型。它不再是您想要的int位。而是这样做
const pixels = new Float32Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.FLOAT);
const intPixels = new Int32Array(pixels.buffer);
const v = intPixels[someOffset];
为明确起见,从值数组中拉出值后,该值将转换为双精度
假设您做了
console.log(floatToIntBits(pixels[0]))
这就是实际发生的情况
f
转换为已经存在的错误数据,并将其放入float32
数组中,并转换为float32 console.log
与
比较console.log(intPixels[0]);
这就是实际发生的情况
console.log
double可以容纳53位整数而不会损失精度,因此将int32转换为double不会丢失任何数据。然后,当您将值作为浮点数取出时,它也被转换为双精度数,但是将浮点数转换为双精度数不会使位保持相同,因此当您尝试将位读取为整数时,它们不再您所期望的位。
也就是说,如果您只需要int,则可以创建一个int纹理,将其附加到帧缓冲区,将整数呈现到其中,然后将它们读取为整数。
const gl = document.createElement('canvas').getContext('webgl2');
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA32I, 1, 1, 0, gl.RGBA_INTEGER, gl.INT, null);
log('errors:', gl.getError() !== gl.NONE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
log('errors:', gl.getError() !== gl.NONE);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
log('errors:', gl.getError() !== gl.NONE);
// just for sanity check: spec says this should work
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
log('fb good:', status === gl.FRAMEBUFFER_COMPLETE);
gl.clearBufferiv(gl.COLOR, 0, [-456, -123, 789, 112233]);
const pixel = new Int32Array(4);
gl.readPixels(0, 0, 1, 1, gl.RGBA_INTEGER, gl.INT, pixel);
log('pixel:', pixel);
log('errors:', gl.getError() !== gl.NONE);
function log(...args) {
const elem = document.createElement('pre');
elem.textContent = [...args].join(' ');
document.body.appendChild(elem);
}