Webgl 奇怪的浮点数数学

Webgl strange float math

当在条件语句中使用统一坐标或纹理坐标时,在条件语句中修改浮点变量的值会得到奇怪的结果。这发生在许多移动设备上的片段着色器中,例如 iPhone 4S、iPhone 5、iPhone 5S、iPhone 6(Safari、Chrome 和 Firefox), Samsung J3(Android 浏览器,Chrome),很可能还有其他。 似乎该值可以与另一个浮点数进行比较,但是如果除数比该值大一半以上,则除数将变为零。 这是一个简单的测试用例。 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

大多数现代手机以牺牲速度为代价支持 highp,但一些较旧的手机(如 iPhone3GS)不支持。

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

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

在台式机上,无论您要求什么,就相关规范而言,您总是会得到 highp。