在WebGL中将Float32Array转换为Uint16Array

7

我有一些 Float32Array 纹理,可以通过 WebGL 正确地显示。然而,当我尝试将它们转换为 Uint16Array 时,问题出现了。

enter image description here

这是我的转换部分。

var _floatToHalfFloat = function(input, offset) {
    var largestHalf = Math.pow(2, 30-15) * (1 + 1023/1024);
    var m = new ArrayBuffer(4);
    var n = new Float32Array(m);
    var o = new Uint32Array(m);
    var f = 0.0;
    for (var i = input.length - 1 - offset; i >= 0;i--) {
        n[0] = input[i];
        f = o[0];
        // fast conversion of half
        // ref : ftp://www.fox-toolkit.org/pub/fasthalffloatconversion.pdf

        if (isNaN(input[i])) {
            input[i] = 0x7fff;
        } else if(n === Infinity || n > largestHalf) {
            input[i] = 0x7c00;
        } else if(n === -Infinity || n < -largestHalf) {
            input[i] = 0xfc00;
        } else if(n === 0) {
            input[i] = 0;
        } else {
            input[i] = ((f>>16)&0x8000)|((((f&0x7f800000)-0x38000000)>>13)&0x7c00)|((f>>13)&0x03ff);
        }
    }
    return new Uint16Array(input);
};
1个回答

1
当原始图像达到黑色时,转换后的图像中会出现饱和颜色(完整的红色、绿色和/或蓝色)。我认为该函数在接近0时效果不佳。
我已经快速实现了wikipedia浮点16位规范的解释
<html>
<head>
<script>
var _floatToHalfFloat = #### YOUR FUNCTION HERE CUT ####

var _halfFloatToFloat = function(hf) {
  var m = new ArrayBuffer(2);
  var n = new Uint16Array(m);
  n[0] = hf;
  var sign = n[0] & 0x8000;
  var exp = (n[0] >> 10) & 0x1F;
  var mant = n[0]& 0x03FF;
  document.getElementById('sign').innerHTML += sign+" - ";
  document.getElementById('exp').innerHTML += exp+" - ";
  document.getElementById('mant').innerHTML += mant+" - ";
  if (exp == 0x1F) {
    return 1.0 * Math.pow(-1, sign) * Infinity;
  } else if (exp == 0) {
    return Math.pow(-1, sign) *
           Math.pow(2, -14) *
           (mant / Math.pow(2, 10));
  } else {
    return Math.pow(-1, sign) *
           Math.pow(2, exp-15) *
           (1+(mant / Math.pow(2, 10)));
  }
};

document.addEventListener("DOMContentLoaded", function(event) {
  var input = new Float32Array(8);
  input[0] = 2.5;
  input[1] = 0.25;
  input[2] = 0.025;
  input[3] = 0.025;
  input[4] = 0.0025;
  input[5] = 0.00025;
  input[6] = 0.000025;
  input[7] = 0.0;

  var i, s = "Value before = ";
  for (i = 0; i < input.length; i++) 
    s += input[i] + " - ";
  document.getElementById('res1').innerHTML = s;
  var output = _floatToHalfFloat(input, 0);
  s = "Value after = ";
  for (i = 0; i < output.length; i++) 
    s += _halfFloatToFloat(output[i]) + " - ";
  document.getElementById('res2').innerHTML = s;
});
</script>
</head>
<body>
  <span id="res1">result</span></br>
  <span id="res2">result</span></br>
  </br></br></br>
  <span id="sign">signs =</span></br>
  <span id="exp">exponents =</span></br>
  <span id="mant">mantissas =</span></br>
</body>
</html>

测试结果如下:
Value before = 2.5 - 0.25 - 0.02500000037252903 - 0.02500000037252903 - 0.0024999999441206455 - 0.0002500000118743628 - 0.00002499999936844688 - 0 -
Value after = 2.5 - 0.25 - 0.024993896484375 - 0.024993896484375 - 0.002498626708984375 - 0.0002498626708984375 - Infinity - 2 -



signs =0 - 0 - 0 - 0 - 0 - 0 - 0 - 0 -
exponents =16 - 13 - 9 - 9 - 6 - 3 - 31 - 16 -
mantissas =256 - 0 - 614 - 614 - 286 - 24 - 653 - 0 - 

这表明最后两个信息不一致。0.000025被转换为Infinity(而不是0?),而0本身被转换为2。这似乎是不正确的。当你想编写一个零时,“维基百科说”你的尾数和指数应该都是零。在你提供的代码中,尾数是零,但指数是16,导致了2(2^(16-15))。
稍微调整一下你的函数后,所有情况似乎都被正常处理了。这是由于你的if语句中存在一个错误。所以,改为:
} else if(n === 0) {
    input[i] = 0;
}

你可能想做这样的事情:

} else if(n[0] === 0) {
    input[i] = 0;
}

对于n变量的所有用法都是一样的。但是你仍然会遇到下溢问题。所以也许你可以接受这样做:

} else if(Math.abs(n[0]) < 0.0001) {
    input[i] = 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接