我正在尝试基于32位浮点数在WebGL或WebGL2着色器上实现64位算术。需要的基本功能之一是将任何浮点数分成两个“非重叠”浮点的功能。第一个浮点数包含原始浮点数的小数位的前一半,第二个浮点数包含第二半数。这是此功能的实现:
precision highp float;
...
...
vec2 split(const float a)
{
const float split = 4097.0; // 2^12 + 1
vec2 result;
float t = a * split; // almost 4097 * a
float diff = t - a; // almost 4096 * a
result.x = t - diff; // almost a
result.y = a - result.x; //very small number
return result;
}
如果我将其传递给着色器中定义的参数,则该函数将按预期工作:
precision highp float;
...
...
float number = 0.1;
vec2 splittedNumber = split(number);
if (splittedNumber.y != 0.0)
{
// color with white
// we step here and see the white screen
}
else
{
//color with black
}
但是只要数字以某种方式取决于任何制服,一切就会开始表现出不同:
precision highp float;
uniform float uniformNumber;
...
...
float number = 0.2;
if (uniformNumber > 0.0)
{
// uniform number is positive,
// so we step here
number = 0.1;
}
vec2 splittedNumber = split(number);
if (splittedNumber.y != 0.0)
{
// color with white
}
else
{
//color with black
// we step here and see the black screen
}
因此,在第二种情况下,当“数字”取决于制服时,分割函数以某种方式得到优化,并以零y值返回vec2。
关于OpenGL Differing floating point behaviour between uniform and constants in GLSL中类似问题的stackoverflow也有类似问题。建议在函数“ split”中使用“ precise”修饰符。不幸的是,在WebGL / WebGL2着色器中没有这样的修改器。
您有任何建议如何摆脱我的情况下的优化并实现“拆分”功能吗?
从数学上讲,两个示例都应输出黑色像素。因为:
float diff = t - a;
result.x = t - diff; // t - diff = t - t + a = a
result.y = a - result.x; // a - a = 0
[可能是split()
的常量参数(预先知道的值)的情况,编译器在优化表达式之前采用了计算函数的路径,由于精度错误,您得到了splittedNumber.y != 0.0
。当您使用统一时,编译器会采用优化表达式的方式,该表达式产生数学上严格的零。
要验证是否是这种情况,您可以尝试以下比较:
if(abs(splittedNumber.y) > 1e-6)
{
}
附带说明,highp
不保证WebGL Shaders中的32位浮点数。取决于硬件,由于回退到mediump(如果不支持highp),它可能是24位浮点数甚至是16位。要查看实际精度,可以使用gl.getShaderPrecisionFormat
https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/getShaderPrecisionFormat