我正在尝试使用 opengl-es 2.0 计算 gpu 上图像的梯度矢量场。我为它找到了一个 cpu 实现,我用它来与我的 gpu 实现进行比较。这里的挑战是 cpu 实现依赖于 java 类型的 float(32 位),而我的 gpu 实现使用的是 lowp float(8 位)。我知道我可以使用 mediump 或 highp 来获得更好的结果,但我仍然想继续使用 lowp float 以确保我的代码能够在最差的硬件上运行。
计算梯度向量场的前几个步骤非常简单:
- 计算归一化灰度(红+绿+蓝)/3.0
- 计算边缘图(右像素-左像素)/2.0 和(上像素-下像素)/2.0
- 计算拉普拉斯算子(稍微复杂一点,但现在不需要详细说明)
目前,无需做任何花哨的事情,我就可以准确地模仿第 1 步,以使 cpu 实现的图像结果与 gpu 的图像结果相同。
不幸的是,我已经停留在第 2 步,因为我的边缘图计算在 gpu 上不够准确。
所以我尝试实现一个扩展的精度浮点,灵感来自http://andrewthall.org/papers/df64_qf128.pdf。
我对opengl-es还很陌生,所以我什至不确定我在这里做的是否正确,但下面是我打算编写的操作,以解决我目前正在遭受的这种精度损失。
vec2 split(float a)
{
float t = a * (2e-8+1.0);
float aHi = t - (t -a);
float aLo = a - aHi;
return vec2(aHi,aLo);
}
vec2 twoProd(float a, float b)
{
float p = a * b;
vec2 aS = split(a);
vec2 bS = split(b);
float err = ( ( (aS.x * bS.x) - p) + (aS.x * bS.y) + (aS.y * bS.x) ) + (aS.y * bS.y);
return vec2(p,err);
}
vec2 FMAtwoProd(float a,float b)
{
float x = a * b;
float y = a * b - x;
return vec2(x,y);
}
vec2 div(vec2 a, vec2 b)
{
float q = a.x / b.x;
vec2 res = twoProd( q , b.x );
float r = ( a.x - res.x ) - res.y ;
return vec2(q,r);
}
vec2 div(vec2 a, float b)
{
return div(a,split(b));
}
vec2 quickTwoSum(float a,float b)
{
float s = a + b;
float e = b - (s-a);
return vec2(s,e);
}
vec2 twoSum(float a,float b)
{
float s = a + b;
float v = s - a;
float e = ( a - (s - v)) + ( b - v );
return vec2(s,e);
}
vec2 add(vec2 a, vec2 b)
{
vec2 s = twoSum(a.x , b.x);
vec2 t = twoSum(a.y , b.y);
s.y += t.x;
s = quickTwoSum(s.x,s.y);
s.y += t.y;
s = quickTwoSum(s.x,s.y);
return s;
}
vec2 add(vec2 a,float b)
{
return add(a,split(b));
}
vec2 mult2(vec2 a,vec2 b)
{
vec2 p = twoProd(a.x,b.x);
p.y += a.x * b.y;
p.y += a.y * b.x;
p = quickTwoSum(p.x,p.y);
return p;
}
vec2 mult(vec2 a,float b)
{
return mult2(a, split(b));
}
显然,我一定是在这里做错了什么,或者错过了一些非常基本的概念,因为无论我使用简单操作还是扩展浮点操作,我都会得到相同的结果......