如何禁用WebGL/WebGL2着色器中使用uniform时的浮点算术优化?

4

我正在尝试在基于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问题的stackoverflow上有一个类似的问题Differing floating point behaviour between uniform and constants in GLSL。那里的建议是在函数“split”内使用“precise”修饰符。不幸的是,在WebGL/WebGL2着色器中没有这样的修饰符。

您有什么建议可以摆脱我的情况中的优化并实现“split”函数吗?

1个回答

1

从数学角度来看,你的两个示例都应该输出黑色像素。因为:

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。当你使用uniform时,编译器会优化表达式,从而产生严格的数学零。为了验证这一点,你可以尝试使用以下比较:
if(abs(splittedNumber.y) > 1e-6)
{

}

顺便提一下,在WebGL着色器中,highp不能保证是32位浮点数。根据硬件的不同,它可能是24位浮点数甚至16位浮点数,因为会回退到使用mediump(如果片元着色器不支持highp)。要查看实际精度,可以使用gl.getShaderPrecisionFormat

https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/getShaderPrecisionFormat


1
如果在片段着色器中指定了highp,那么你将在片段着色器中得到highp或者它将无法编译。这违反了规范自动转换为mediump。但相反地,如果您指定mediump,则可能会得到highp。 - gman
顺便提一下,在WebGL2中,高精度浮点数保证为32位。 - David
1
至少在GLES 2.0中,Mali 4xx GPU的驱动程序(今天唯一重要的中等卡)会自动回退到mediump而不会出现错误,尽管规格如此。我还没有在WebGL中尝试过这个,但我认为这也可能是情况。 - Vlad Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接