Преобразование intBitsToFloat в GLSL и floatBitsToInt обратно в Javascript - PullRequest
0 голосов
/ 30 декабря 2018

Я пытаюсь кодировать целочисленный идентификатор в шейдере GLSL 3.3 для вывода с плавающей запятой, используя intBitsToFloat (я использую вектор с плавающей запятой highp output), затем я использую readPixels, чтобы получить это значение в pixelData = new Float32Array(4).Затем я декодирую его обратно в Int в JS, используя floatToIntBits(pixelData[0]), где

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], я всегда получаю 0 в JS
  • , кодирую в GLSL значение из диапазона [2^23,...[,Я получаю правильное значение в JS
  • кодировать в GLSL значение из диапазона [-2^23+1,-1], я всегда получаю -2^23+1 в JS
  • кодировать в GLSL значение из диапазона ]..., -2^23], яполучить правильный результат в JS

У кого-нибудь есть идеи, почему это может произойти?

1 Ответ

0 голосов
/ 30 декабря 2018

Я собираюсь догадаться, что это потому, что вы используете функцию вместо чтения значений непосредственно из результатов 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]))

В переводе на то, что происходит на самом деле, это

  1. Значение float32 извлекается из пикселей со смещением байта 0 * 4
  2. Это значение преобразуется в двойное значение DATA LOST HERE
  3. floatToIntBits вызывается с этим двойным значением
  4. floatToIntBits преобразует f, и без того плохие данные удваиваются в float32, поскольку он помещает их в float32массив
  5. floatToIntBits получает int из int32
  6. , в котором int приводится к удвоенному значению
  7. , для которого возвращается двойное значение
  8. , для которого значение типа double передается в console.log

Сравните с

console.log(intPixels[0]);

То, что на самом деле происходит,

  1. значение int32 извлекается из intPixels со смещением байта 0 *4
  2. Это значение преобразуется в двойное
  3. Это двойное значение передается в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);
}
...