你如何在JavaScript中转换为半浮动? [英] How do you convert to half floats in JavaScript?

查看:124
本文介绍了你如何在JavaScript中转换为半浮动?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够在WebGL中使用 OES_texture_half_float 扩展名并提供我自己的数据但是没有 Float16Array 在JavaScript中。那么如何生成半浮点数据?

I want to be able to use the OES_texture_half_float extension in WebGL and provide my own data but there's no Float16Array in JavaScript. So how do I generate half float data?

推荐答案

我将这两个函数改编为JavaScript。他们似乎工作

I adapted these 2 functions to JavaScript. They seem to work


  1. 从这里开始

var toHalf = (function() {

  var floatView = new Float32Array(1);
  var int32View = new Int32Array(floatView.buffer);

  /* This method is faster than the OpenEXR implementation (very often
   * used, eg. in Ogre), with the additional benefit of rounding, inspired
   * by James Tursa?s half-precision code. */
  return function toHalf(val) {

    floatView[0] = val;
    var x = int32View[0];

    var bits = (x >> 16) & 0x8000; /* Get the sign */
    var m = (x >> 12) & 0x07ff; /* Keep one extra bit for rounding */
    var e = (x >> 23) & 0xff; /* Using int is faster here */

    /* If zero, or denormal, or exponent underflows too much for a denormal
     * half, return signed zero. */
    if (e < 103) {
      return bits;
    }

    /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
    if (e > 142) {
      bits |= 0x7c00;
      /* If exponent was 0xff and one mantissa bit was set, it means NaN,
           * not Inf, so make sure we set one mantissa bit too. */
      bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff);
      return bits;
    }

    /* If exponent underflows but not too much, return a denormal */
    if (e < 113) {
      m |= 0x0800;
      /* Extra rounding may overflow and set mantissa to 0 and exponent
       * to 1, which is OK. */
      bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
      return bits;
    }

    bits |= ((e - 112) << 10) | (m >> 1);
    /* Extra rounding. An overflow will set mantissa to 0 and increment
     * the exponent, which is OK. */
    bits += m & 1;
    return bits;
  };

}());


  • 从这里开始

    var toHalf = (function() {
    
      var floatView = new Float32Array(1);
      var int32View = new Int32Array(floatView.buffer);
    
      return function toHalf( fval ) {
        floatView[0] = fval;
        var fbits = int32View[0];
        var sign  = (fbits >> 16) & 0x8000;          // sign only
        var val   = ( fbits & 0x7fffffff ) + 0x1000; // rounded value
    
        if( val >= 0x47800000 ) {             // might be or become NaN/Inf
          if( ( fbits & 0x7fffffff ) >= 0x47800000 ) {
                                              // is or must become NaN/Inf
            if( val < 0x7f800000 ) {          // was value but too large
              return sign | 0x7c00;           // make it +/-Inf
            }
            return sign | 0x7c00 |            // remains +/-Inf or NaN
                ( fbits & 0x007fffff ) >> 13; // keep NaN (and Inf) bits
          }
          return sign | 0x7bff;               // unrounded not quite Inf
        }
        if( val >= 0x38800000 ) {             // remains normalized value
          return sign | val - 0x38000000 >> 13; // exp - 127 + 15
        }
        if( val < 0x33000000 )  {             // too small for subnormal
          return sign;                        // becomes +/-0
        }
        val = ( fbits & 0x7fffffff ) >> 23;   // tmp exp for subnormal calc
        return sign | ( ( fbits & 0x7fffff | 0x800000 ) // add subnormal bit
             + ( 0x800000 >>> val - 102 )     // round depending on cut off
             >> 126 - val );                  // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
      };
    }());
    


  • 使用示例

    var tex = new Uint16Array(4);
    tex[0] = toHalf(0.5);
    tex[1] = toHalf(1);
    tex[2] = toHalf(123);
    tex[3] = toHalf(-13);
    

    以下是第一个使用WebGL的示例

    Here's an example using the first one with WebGL

    var toHalf = (function() {
    
      var floatView = new Float32Array(1);
      var int32View = new Int32Array(floatView.buffer);
    
      /* This method is faster than the OpenEXR implementation (very often
       * used, eg. in Ogre), with the additional benefit of rounding, inspired
       * by James Tursa?s half-precision code. */
      return function toHalf(val) {
    
        floatView[0] = val;
        var x = int32View[0];
    
        var bits = (x >> 16) & 0x8000; /* Get the sign */
        var m = (x >> 12) & 0x07ff; /* Keep one extra bit for rounding */
        var e = (x >> 23) & 0xff; /* Using int is faster here */
    
        /* If zero, or denormal, or exponent underflows too much for a denormal
         * half, return signed zero. */
        if (e < 103) {
          return bits;
        }
    
        /* If NaN, return NaN. If Inf or exponent overflow, return Inf. */
        if (e > 142) {
          bits |= 0x7c00;
          /* If exponent was 0xff and one mantissa bit was set, it means NaN,
                       * not Inf, so make sure we set one mantissa bit too. */
          bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff);
          return bits;
        }
    
        /* If exponent underflows but not too much, return a denormal */
        if (e < 113) {
          m |= 0x0800;
          /* Extra rounding may overflow and set mantissa to 0 and exponent
           * to 1, which is OK. */
          bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1);
          return bits;
        }
    
        bits |= ((e - 112) << 10) | (m >> 1);
        /* Extra rounding. An overflow will set mantissa to 0 and increment
         * the exponent, which is OK. */
        bits += m & 1;
        return bits;
      };
    
    }());
    
    (function() {
      twgl.setAttributePrefix("a_");
      var m4 = twgl.m4;
      var gl = document.getElementById("c").getContext("webgl");
      var ext =  gl.getExtension("OES_texture_half_float");
      if (!ext) {
        alert("no support for OES_texture_half_float on this device");
        return;
      }
      var onePointProgramInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
    
      var shapes = [
        twgl.primitives.createCubeBufferInfo(gl, 2),
      ];
    
      function rand(min, max) {
        if (max === undefined) {
          max = min;
          min = 0;
        }
        return min + Math.random() * (max - min);
      }
    
      // Shared values
      var baseHue = rand(360);
      var lightWorldPosition = [1, 8, -10];
      var lightColor = [1, 1, 1, 1];
      var camera = m4.identity();
      var view = m4.identity();
      var viewProjection = m4.identity();
    
      var halfFloatData = new Uint16Array(4);
    
      // will divide by 400 in shader to prove it works.
      halfFloatData[0] = toHalf(100);  
      halfFloatData[1] = toHalf(200);
      halfFloatData[2] = toHalf(300);
      halfFloatData[3] = toHalf(400);
    
      var textures = twgl.createTextures(gl, {
        // A 2x2 pixel texture from a JavaScript array
        checker: {
          // Note: You need OES_texture_half_float_linear to use anything other than NEAREST
          mag: gl.NEAREST,
          min: gl.NEAREST,
          format: gl.LUMINANCE,
          type: ext.HALF_FLOAT_OES,
          src: halfFloatData,
        },
      });
    
      var objects = [];
      var drawObjects = [];
      var numObjects = 100;
      for (var ii = 0; ii < numObjects; ++ii) {
        var uniforms;
        var programInfo;
        var shape;
        shape = shapes[ii % shapes.length];
        programInfo = onePointProgramInfo;
        uniforms = {
          u_diffuse: textures.checker,
          u_worldViewProjection: m4.identity(),
        };
        drawObjects.push({
          programInfo: programInfo,
          bufferInfo: shape,
          uniforms: uniforms,
        });
        objects.push({
          translation: [rand(-10, 10), rand(-10, 10), rand(-10, 10)],
          ySpeed: rand(0.1, 0.3),
          zSpeed: rand(0.1, 0.3),
          uniforms: uniforms,
        });
      }
    
      function render(time) {
        time *= 0.001;
        twgl.resizeCanvasToDisplaySize(gl.canvas);
        gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    
        gl.enable(gl.DEPTH_TEST);
        gl.clearColor(0.2, 0.3, 0.8, 1);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
        var radius = 20;
        var orbitSpeed = time * 0.1;
        var projection = m4.perspective(30 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 100);
        var eye = [Math.cos(orbitSpeed) * radius, 4, Math.sin(orbitSpeed) * radius];
        var target = [0, 0, 0];
        var up = [0, 1, 0];
    
        m4.lookAt(eye, target, up, camera);
        m4.inverse(camera, view);
        m4.multiply(projection, view, viewProjection);
    
        objects.forEach(function(obj) {
          var uni = obj.uniforms;
          var world = m4.identity(world);
          m4.rotateY(world, time * obj.ySpeed, world);
          m4.rotateZ(world, time * obj.zSpeed, world);
          m4.translate(world, obj.translation, world);
          m4.rotateX(world, time, world);
          m4.multiply(viewProjection, world, uni.u_worldViewProjection);
        });
    
        twgl.drawObjectList(gl, drawObjects);
    
        requestAnimationFrame(render);
      }
      requestAnimationFrame(render);
    }());

    body {
      margin: 0;
      font-family: monospace;
    }
    canvas {
      width: 100vw;
      height: 100vh;
      display: block;
    }

    <script src="https://twgljs.org/dist/3.x/twgl-full.min.js"></script>
    <canvas id="c"></canvas>
    <script id="vs" type="notjs">
    uniform mat4 u_worldViewProjection;
    
    attribute vec4 a_position;
    attribute vec2 a_texcoord;
    
    varying vec4 v_position;
    varying vec2 v_texCoord;
    
    void main() {
      v_texCoord = a_texcoord;
      gl_Position = u_worldViewProjection * a_position;
    }
    </script>
    <script id="fs" type="notjs">
    precision mediump float;
    
    varying vec2 v_texCoord;
    uniform sampler2D u_diffuse;
    
    void main() {
      gl_FragColor = texture2D(u_diffuse, v_texCoord) / vec4(400.0, 400.0, 400.0, 1.0);
    }
    </script>

    请注意,虽然如果你上传图片纹理会有效,但最好离线进行这种转换。然后,您可以将它们存储为二进制文件并使用XMLHttpRequest下载。您可以使用gzip压缩它们(与png大致相同),只要您的服务器发送正确的标题告诉浏览器文件已被gzip压缩,它就会自动为您解压缩。

    Note that while this works if you're uploading image textures it's probably better to do this conversion offline. You can then store them as binary and download with XMLHttpRequest. You can compress them with gzip (about the same as png) and as long as your server sends the correct headers telling the browser the file has been gzipped it should be decompressed automatically for you.

    这篇关于你如何在JavaScript中转换为半浮动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆