[英]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++ 中一样工作
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.