我试图使用intBitsToFloat
将GLSL 3.3着色器中的整数标识符编码为浮点输出(我使用的是highp输出浮点向量),然后使用readPixels
将此值获取到pixelData = new Float32Array(4)
中。然后我使用floatToIntBits(pixelData[0])
将其解码回JS中的Int,其中
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]
}
现在我得到了一个非常奇怪的结果。也就是说,当我:
- 在GLSL中编码范围为
[0,2^23-1]
的值,我在JS中总是得到0
- 在GLSL中编码
[2^23,...[
范围内的值,我在JS中得到正确的值 - 在GLSL中编码范围为
[-2^23+1,-1]
的值,我在JS中总是得到-2^23+1
- 在GLSL中编码
]..., -2^23]
范围内的值,我在JS中得到了正确的结果
有人知道为什么会发生这种事吗?
我猜这是因为您使用的是一个函数,而不是直接从readPixels
的结果中读取值。Javascript只有一个数字类型,它实际上是一个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];
此时,CCD_ 15已被转换为双精度。它不再是你想要的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];
为了澄清,从typedarray中提取一个值的那一刻,它就被转换为双值
假设你做了
console.log(floatToIntBits(pixels[0]))
转化为实际发生的事情就是
- float32值从字节偏移量为0*4的像素中提取
- 该值被转换为双精度此处数据丢失
- floatToIntBits是用这个双值调用的
- floatToIntBits将已经坏的数据
f
转换为float32,并将其放入float32
数组 - floatToIntBits从int32中获取一个int
- 该int被强制转换为双精度
- 那个替身回来了
- 该双精度被传递给
console.log
与比较
console.log(intPixels[0]);
转化为实际发生的事情就是
- 在字节偏移量0*4处从intPixels中取出一个int32值
- 该值转换为双精度
- 该双精度被传递给
console.log
双精度可以保存53位整数,因此将int32转换为双精度不会丢失任何数据。当你把值作为浮点值取出时,它也被转换成了double,但将浮点值转换成double并不能保持比特不变,所以当你试图把比特作为int读取时,它们不再是你期望的比特。
也就是说,如果你只想要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);
}