对于gis这种大坐标的引擎,精度问题是很关键的,特别是使用了gpu渲染场景,现在的gpu的精度都是float的,相较于double。
缺陷:
1、float是32位浮点数,布局是【1符号位+8指数位+23小数位】精度较低;
2、最大范围在cm级时为131072.00,也就是100公里的范围,相较于地球半径6,356,750米差的很远;
Float:比特数为32,有效数字为6-7,数值范围为 -3.4E+38 ~ 3.4E+38
如何尽量消除上面的问题,对于基于gpu渲染的场景来说很重要;
存在几种解决方法,参考文章1中,使用了RTC、RTE方式,这是一些常规的使用相对位置减少精度损失的方法,参考文章1中改进了RTE的方式,首先说明一下RTE就是目标位置相对于相机位置进行目标偏移,是以相机(或者眼睛)为中心的,下面主要关注这个方面。
计算步骤如下:
a、将目标位置P、和相机位置Q的double类型坐标转换成两个单精度float类型
double(x,y,z)=高位double(x,y,z)+低位double(x,y,z),
void CDoubleToTwoFloats::Convert(double doubleValue,
float& floatHigh, float& floatLow)
{
if (doubleValue >= 0.0)
{
double doubleHigh = floor(doubleValue / 65536.0) * 65536.0;
floatHigh = (float)doubleHigh;
floatLow = (float)(doubleValue - doubleHigh);
}
else
{
double doubleHigh = floor(-doubleValue / 65536.0) * 65536.0;
floatHigh = (float)-doubleHigh;
floatLow = (float)(doubleValue + doubleHigh);
}
}
在将高位double(x,y,z)强转换转换成float类型时,低位丢失只留下高位,其中为什么要除以65536,而不是别的值呢,因为float值在cm级时是131072.00,是65536的2倍,但是由于计算机系统精度的不确定性(理论上是7位,实际6~9位,因为float的小数是使用泰勒级数的思想近似计算的,不能准确拟合出真实的值),所以选用了65536这个值。
b、将目标位置P减去相机位置Q,计算出相对位置
uniform vec3 uViewerHigh;
uniform vec3 uViewerLow;
void main(void)
{
vec3 highDifference = vec3(vPositionHigh.xyz - uViewerHigh);
vec3 lowDifference = vec3(vPositionLow.xyz - uViewerLow);
gl_Position = gl_ModelViewProjectionMatrix * vec4(highDifference + lowDifference, 1.0);
}
目标位置和相机位置的高位、低位分别相减,计算出相对位置
c、将上述相减的位置在相加就是关键
分两种情况讨论:
1、当相机和目标相对比较近(小于131072.00)时,它们的高位都是0,所以高位+低位=低位的结果;
2、当相机的目标位置相对较远的时候(大于131072.00)时,相加后的结果中低位精度中的不精确值可以忽略。
如上图所示精度不准确的位不会影响很大,因为距离很远,即使有点抖动也看不出来。
参考文章:
https://help.agi.com/AGIComponents/html/BlogPrecisionsPrecisions.htm