简体   繁体   English

WebGL2 和 C++ 上浮点计算的不同结果

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

I am trying to make calculations on the fragment shader in WebGL2.我正在尝试对 WebGL2 中的片段着色器进行计算。 And I've noticed that the calculations there are not as precise as on C++.而且我注意到那里的计算不如 C++ 精确。 I know that the high precision float contains 32 bits either in the fragment shader or in C++.我知道在片段着色器或 C++ 中,高精度浮点数包含 32 位。

I am trying to compute 1.0000001^(10000000) and get around 2.8 on C++ and around 3.2 on the shader.我正在尝试计算 1.0000001^(10000000) 并在 C++ 上获得大约 2.8,在着色器上获得大约 3.2。 Do you know the reason that the fragment shader calculations are not as precise as the same calculations on C++?你知道片段着色器计算不如C++上的相同计算精确的原因吗?

code on C++ 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更新在这里可以找到包含 WebGL2 示例完整代码的 html 文件

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++ WebGL2 所基于的 OpenGL ES 3.0 不需要 GPU 上的浮点数来像在 C++ 中一样工作

From the spec规范

2.1.1 Floating-Point Computation 2.1.1 浮点计算

The GL must perform a number of floating-point operations during the course of its operation. GL 在其操作过程中必须执行许多浮点运算。 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.由 OpenGL ES 着色语言规范用于着色器中的操作,并且在某些情况下受到 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 .我们只要求数字的浮点部分包含足够的位,并且它们的指数字段足够大,以便浮点运算的单个结果精确到大约 10 5 的1 部分。 The maximum representable magnitude for all floating-point values must be at least 2 32 .所有浮点值的最大可表示幅度必须至少为 2 32 x· 0 = 0 ·x = 0 for any non-infinite and non-NaN x. x·0 = 0 ·x = 0 对于任何非无限和非 NaN x。 1 ·x = x· 1 = x. 1·x = x· 1 = x。 x + 0 = 0 + x = x. x + 0 = 0 + x = x。 0 0 = 1. (Occasionally further requirements will be specified.) Most single-precision floating-point formats meet these requirements. 0 0 = 1。(有时会指定进一步的要求。)大多数单精度浮点格式都满足这些要求。

Just for fun let's do it and print the results.只是为了好玩,让我们这样做并打印结果。 Using WebGL1 so can test on more devices使用 WebGL1 可以在更多设备上进行测试

 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.事实上,如果您使用 double(64 位浮点,53 位尾数)而不是 float(32 位浮点,24 位尾数)编译 c++ 片段,您将获得结果 3.29397,即是使用着色器得到的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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