floating-point - Webgl 奇怪的 float 学

标签 floating-point webgl precision

当条件语句中使用统一坐标或纹理坐标时,修改条件语句内浮点变量的值会得到奇怪的结果。这种情况发生在许多移动设备上的片段着色器内,例如 iPhone 4S、iPhone 5、iPhone 5S、iPhone 6(Safari、Chrome 和 Firefox)、Samsung J3(Android 浏览器、Chrome)以及很可能其他设备。 似乎该值可以与另一个 float 进行比较,但是当除以该值时,如果除数比该值大一半以上,则该值为零。 这是一个简单的测试用例。 R、G 和 B 颜色的所有值预计都为相同的 128,因此输出应为灰色,但是在这些移动设备上,R 等于 0,因此输出为海色。 jsfiddle 是 here

html 是:

<html>
<head>
    <title>Hello</title>
</head>
<body>
    <script src="js/shadertest.js"></script>
</body> 

js代码为:

    function test(){
    var create3DContext = function(canvas, opt_attribs) {
      var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
      var context = null;
      for (var ii = 0; ii < names.length; ++ii) {
        try {
          context = canvas.getContext(names[ii], opt_attribs);
        } catch(e) {}
        if (context) {
          break;
        }
      }
      return context;
    }

    var compileShader = function(type, source) {
        var shader = gl.createShader(type);
        gl.shaderSource(shader, source);
        gl.compileShader(shader);

        if( !gl.getShaderParameter(shader, gl.COMPILE_STATUS) ) {
            throw new Error(gl.getShaderInfoLog(shader));
        }

        return shader;
    };

    var canvas = document.createElement('canvas');
    canvas.width = 1;
    canvas.height = 1;
    canvas.style.width = '100%';
    canvas.style.height = '100%';
    document.body.appendChild(canvas);

    var gl = create3DContext(canvas, {});

    // init buffers
    var buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]), gl.STATIC_DRAW);

    // The main IDWT Shader
    var webglProgram = gl.createProgram();
    gl.attachShader(webglProgram, compileShader(gl.VERTEX_SHADER, SHADER_VERTEX_IDENTITY));
    gl.attachShader(webglProgram, compileShader(gl.FRAGMENT_SHADER, SHADER_FRAGMENT));

    gl.linkProgram(webglProgram);



    if( !gl.getProgramParameter(webglProgram, gl.LINK_STATUS) ) {
        throw new Error(gl.getProgramInfoLog(webglProgram));
    }

    gl.useProgram(webglProgram);


    var vertexAttr = gl.getAttribLocation(webglProgram, 'vertex');
    gl.enableVertexAttribArray(vertexAttr);
    gl.vertexAttribPointer(vertexAttr, 2, gl.FLOAT, false, 0, 0);

    gl.uniform1i(gl.getUniformLocation(webglProgram, 'vertical'), 0);

    gl.viewport(0, 0, 1, 1);

    gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

    var pixels = new Uint8Array(4);
    var imgData = gl.readPixels(0,0,1,1,gl.RGBA,gl.UNSIGNED_BYTE, pixels);
    console.log('R = '+pixels[0]);
    console.log('G = '+pixels[1]);
    console.log('B = '+pixels[2]);
    console.log('A = '+pixels[3]);
}
var SHADER_FRAGMENT = [

        'precision mediump float;',

        'uniform int vertical;',

        'void main() {',
            'float valueLow  = 60.0;',
            'float valueHigh = 60.0;',
            'if ( vertical == 1)',
            '{',
                'valueLow = 60.0;',
            '}',
            'gl_FragColor.rgba = vec4((valueLow+10000.0)/20000.0, (valueHigh+10000.0)/20000.0, (60.0+10000.0)/20000.0, 1.0);',
        '}'
    ].join('\n'),
SHADER_VERTEX_IDENTITY = [
    'attribute vec2 vertex;',
    'varying vec2 texCoord;',

    'void main() {',
        'texCoord = vertex;',
        'gl_Position = vec4((vertex * 2.0 - 1.0) * vec2(1, -1), 0.0, 1.0);',
    '}'
].join('\n');
test();

编辑 我找到了一种解决方法,即将数学与相关变量放在同一条件语句中,如 jsfiddle 所示。 ,但我希望有更优雅的方式。

最佳答案

我不知道如何解决低精度问题,除了改变你的数学以免遇到问题,但你可以有条件地要求高精度

#if GL_FRAGMENT_PRECISION_HIGH
  precision highp float;
#else
  precision mediump float;
#else

大多数现代手机都支持高性能,但会牺牲速度,但一些较旧的手机(例如 iPhone3GS)则不支持。

您还可以设置特定变量的精度

#if GL_FRAGMENT_PRECISION_HIGH
  highp vec3 someVar;
#else
  mediump vec3 someVar;
#else

在桌面上,无论您要求什么,就规范而言都可以,您总是会得到高分。

关于floating-point - Webgl 奇怪的 float 学,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44361299/

相关文章:

javascript - 视网膜 Macbook Pro 的 WebGL 性能问题

objective-c - 基于计时器以毫秒精度执行功能

c - C语言中如何乘以12位小数?

javascript - WebGL 的绘图原语是什么?

canvas - Three.js:菲涅尔着色器 - 灯光:真正的问题

delphi - 如何获得指针指向的浮点值?

floating-point - 为什么 float 不正确?

matlab - eps() 在 MATLAB 中是如何计算的?

c++ - 16 位 float 和 GL_HALF_FLOAT

python - 为什么Python中除以10不会简单地导致小数点向左移动?