Я собираюсь догадаться, что это потому, что вы используете функцию вместо чтения значений непосредственно из результатов readPixels
.Javascript имеет только 1 числовой тип, который по сути является C / C ++ double
, поэтому в тот момент, когда вы читаете любое значение из типизированного массива, оно преобразуется в double
. Давайте проверим без GLSL
* 1007.*
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]))
В переводе на то, что происходит на самом деле, это
- Значение float32 извлекается из пикселей со смещением байта 0 * 4
- Это значение преобразуется в двойное значение DATA LOST HERE
- floatToIntBits вызывается с этим двойным значением
- floatToIntBits преобразует
f
, и без того плохие данные удваиваются в float32, поскольку он помещает их в float32
массив - floatToIntBits получает int из int32
- , в котором int приводится к удвоенному значению
- , для которого возвращается двойное значение
- , для которого значение типа double передается в
console.log
Сравните с
console.log(intPixels[0]);
То, что на самом деле происходит,
- значение int32 извлекается из intPixels со смещением байта 0 *4
- Это значение преобразуется в двойное
- Это двойное значение передается в
console.log
Двойное число может содержать 53-разрядное целое число без потери точности, поэтому преобразование int32 в двойное не теряет данных.Тогда как тогда вы извлекали значение как число с плавающей запятой, оно также было преобразовано в двойное число, но преобразование числа с плавающей запятой в двойное не сохраняло биты одинаковыми, поэтому, когда вы пытаетесь считать биты как целые, они больше не являютсяожидаемые биты.
Тем не менее, если вы просто хотите получить целые числа, вы можете создать текстуру 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);
}