繁体   English   中英

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

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

我正在尝试对 WebGL2 中的片段着色器进行计算。 而且我注意到那里的计算不如 C++ 精确。 我知道在片段着色器或 C++ 中,高精度浮点数包含 32 位。

我正在尝试计算 1.0000001^(10000000) 并在 C++ 上获得大约 2.8,在着色器上获得大约 3.2。 你知道片段着色器计算不如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
}

片段着色器代码:

#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
  }
}

更新在这里可以找到包含 WebGL2 示例完整代码的 html 文件

WebGL2 所基于的 OpenGL ES 3.0 不需要 GPU 上的浮点数来像在 C++ 中一样工作

规范

2.1.1 浮点计算

GL 在其操作过程中必须执行许多浮点运算。 在某些情况下,此类操作的表示和/或精度是被定义或限制的; 由 OpenGL ES 着色语言规范用于着色器中的操作,并且在某些情况下受到 GL 消耗的顶点、纹理或渲染缓冲区数据的指定格式的隐式限制。 否则,不会指定此类浮点数的表示形式以及如何对其执行操作的详细信息 我们只要求数字的浮点部分包含足够的位,并且它们的指数字段足够大,以便浮点运算的单个结果精确到大约 10 5 的1 部分。 所有浮点值的最大可表示幅度必须至少为 2 32 x·0 = 0 ·x = 0 对于任何非无限和非 NaN x。 1·x = x· 1 = x。 x + 0 = 0 + x = x。 0 0 = 1。(有时会指定进一步的要求。)大多数单精度浮点格式都满足这些要求。

只是为了好玩,让我们这样做并打印结果。 使用 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>

我的结果:

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

区别在于精度。 事实上,如果您使用 double(64 位浮点,53 位尾数)而不是 float(32 位浮点,24 位尾数)编译 c++ 片段,您将获得结果 3.29397,即是使用着色器得到的结果。

暂无
暂无

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

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