简体   繁体   中英

Different results in floating-point calculations on WebGL2 and C++

I am trying to make calculations on the fragment shader in WebGL2. And I've noticed that the calculations there are not as precise as on C++. I know that the high precision float contains 32 bits either in the fragment shader or in C++.

I am trying to compute 1.0000001^(10000000) and get around 2.8 on C++ and around 3.2 on the shader. Do you know the reason that the fragment shader calculations are not as precise as the same calculations on C++?

code on C++

#include <iostream>
void main()
{
  const float NEAR_ONE = 1.0000001;
  float result = NEAR_ONE;

  for (int i = 0; i < 10000000; i++)
  {
    result = result * NEAR_ONE;
  }

  std::cout << result << std::endl; // result is 2.88419
}

Fragment shader code:

#version 300 es
precision highp float;
out vec4 color;
void main()
{
  const float NEAR_ONE = 1.0000001;
  float result = NEAR_ONE;

  for (int i = 0; i < 10000000; i++)
  {
    result = result * NEAR_ONE;
  }    

  if ((result > 3.2) && (result < 3.3))
  {
    // The screen is colored by red and this is how we know 
    // that the value of result is in between 3.2 and 3.3
    color = vec4(1.0, 0.0, 0.0, 1.0); // Red
  }
  else
  {
     // We never come here. 
     color = vec4(0.0, 0.0, 0.0, 1.0); // Black
  }
}

Update : Here one can find the html file with the full code for the WebGL2 example

OpenGL ES 3.0 on which WebGL2 is based does not require floating point on the GPU to work the same as it does in C++

From the spec

2.1.1 Floating-Point Computation

The GL must perform a number of floating-point operations during the course of its operation. In some cases, the representation and/or precision of such operations is defined or limited; by the OpenGL ES Shading Language Specification for operations in shaders, and in some cases implicitly limited by the specified format of vertex, texture, or renderbuffer data consumed by the GL. Otherwise, the representation of such floating-point numbers, and the details of how operations on them are performed, is not specified . We require simply that numbers' floating point parts contain enough bits and that their exponent fields are large enough so that individual results of floating-point operations are accurate to about 1 part in 10 5 . The maximum representable magnitude for all floating-point values must be at least 2 32 . x· 0 = 0 ·x = 0 for any non-infinite and non-NaN x. 1 ·x = x· 1 = x. x + 0 = 0 + x = x. 0 0 = 1. (Occasionally further requirements will be specified.) Most single-precision floating-point formats meet these requirements.

Just for fun let's do it and print the results. Using WebGL1 so can test on more devices

 function main() { const gl = document.createElement('canvas').getContext('webgl'); const ext = gl.getExtension('OES_texture_float'); if (!ext) { return alert('need OES_texture_float'); } // not required - long story gl.getExtension('WEBGL_color_buffer_float'); const fbi = twgl.createFramebufferInfo(gl, [ { type: gl.FLOAT, minMag: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE, } ], 1, 1); const vs = ` void main() { gl_Position = vec4(0, 0, 0, 1); gl_PointSize = 1.0; } `; const fs = ` precision highp float; void main() { const float NEAR_ONE = 1.0000001; float result = NEAR_ONE; for (int i = 0; i < 10000000; i++) { result = result * NEAR_ONE; } gl_FragColor = vec4(result); } `; const prg = twgl.createProgram(gl, [vs, fs]); gl.useProgram(prg); gl.viewport(0, 0, 1, 1); gl.drawArrays(gl.POINTS, 0, 1); const values = new Float32Array(4); gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, values); console.log(values[0]); } main();
 <script src="https://twgljs.org/dist/4.x/twgl.js"></script>

My results:

Intel Iris Pro          : 2.884186029434204
NVidia GT 750 M         : 3.293879985809326
NVidia GeForce GTX 1060 : 3.2939157485961914
Intel UHD Graphics 617  : 3.292219638824464 

The difference is precision. In fact, if you compile the c++ fragment using double (64-bit floating-point, with 53-bit mantissa) instead of float (32-bit floating-point, with 24-bit mantissa), you obtain as result 3.29397, which is the result you get using the shader.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM