【大坐标处理】
大坐标处理
- Cesium
- UE
- 总结
Cesium
首先想到的就是查看Cesium的代码看看他是怎么解决的对吧
这里有个最简代码片段,我们顺着往里找
* void main()
* {
* vec4 p = czm_translateRelativeToEye(positionHigh, positionLow);
* gl_Position = czm_modelViewProjectionRelativeToEye * p;
* }
vec4 czm_translateRelativeToEye(vec3 high, vec3 low)
{
vec3 highDifference = high - czm_encodedCameraPositionMCHigh;
// This check handles the case when NaN values have gotten into `highDifference`.
// Such a thing could happen on devices running iOS.
if (length(highDifference) == 0.0) {
highDifference = vec3(0);
}
vec3 lowDifference = low - czm_encodedCameraPositionMCLow;
return vec4(highDifference + lowDifference, 1.0);
}
这里可以看见他把camera的坐标也传进来了,然后high low分别和camera的high low减一下再合并到一起,就得到了相对相机的坐标系
也就是说,他不仅把坐标拆成了high low,还减去了相机坐标,也就是减去相机之后的坐标还拆,这确实是能支持很大的数据了
czm_modelViewProjectionRelativeToEye 这个值的创建就是普通的modelViewMatirx只是把translate清空了,如下代码
czm_modelViewRelativeToEye: new AutomaticUniform({
size: 1,
datatype: WebGLConstants.FLOAT_MAT4,
getValue: function (uniformState) {
return uniformState.modelViewRelativeToEye;
},
}),
modelViewRelativeToEye: {
get: function () {
cleanModelViewRelativeToEye(this);
return this._modelViewRelativeToEye;
},
}
function cleanModelViewRelativeToEye(uniformState) {
if (uniformState._modelViewRelativeToEyeDirty) {
uniformState._modelViewRelativeToEyeDirty = false;
const mv = uniformState.modelView;
const mvRte = uniformState._modelViewRelativeToEye;
mvRte[0] = mv[0];
mvRte[1] = mv[1];
mvRte[2] = mv[2];
mvRte[3] = mv[3];
mvRte[4] = mv[4];
mvRte[5] = mv[5];
mvRte[6] = mv[6];
mvRte[7] = mv[7];
mvRte[8] = mv[8];
mvRte[9] = mv[9];
mvRte[10] = mv[10];
mvRte[11] = mv[11];
mvRte[12] = 0.0;
mvRte[13] = 0.0;
mvRte[14] = 0.0;
mvRte[15] = mv[15];
}
}
modelViewProjectionRelativeToEye只是一并在CPU做完几个乘法
modelViewProjectionRelativeToEye: {
get: function () {
cleanModelViewProjectionRelativeToEye(this);
return this._modelViewProjectionRelativeToEye;
},
},
function cleanModelViewProjectionRelativeToEye(uniformState) {
if (uniformState._modelViewProjectionRelativeToEyeDirty) {
uniformState._modelViewProjectionRelativeToEyeDirty = false;
Matrix4.multiply(
uniformState._projection,
uniformState.modelViewRelativeToEye,
uniformState._modelViewProjectionRelativeToEye,
);
}
}
这个是high low的解释
https://help.agi.com/AGIComponents/html/BlogPrecisionsPrecisions.htm
这是Cesium里的实现
EncodedCartesian3.encode = function (value, result) {
//>>includeStart('debug', pragmas.debug);
Check.typeOf.number("value", value);
//>>includeEnd('debug');
if (!defined(result)) {
result = {
high: 0.0,
low: 0.0,
};
}
let doubleHigh;
if (value >= 0.0) {
doubleHigh = Math.floor(value / 65536.0) * 65536.0;
result.high = doubleHigh;
result.low = value - doubleHigh;
} else {
doubleHigh = Math.floor(-value / 65536.0) * 65536.0;
result.high = -doubleHigh;
result.low = value + doubleHigh;
}
return result;
};
这是CPP代码
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);
}
}
UE
https://dev.epicgames.com/documentation/en-us/unreal-engine/large-world-coordinates-rendering-in-unreal-engine-5
这里是UE的文档
之前就听说UE有这个支持,好奇他怎么搞的
这里是文档里最后一段话,看起来也是类似Cesium的解法
不过他还有一个空间,是纠正了旋转不只是减去offset,建议能做就做,那估计就是没做:)
FDFScalar(double Input)
{
float High = (float)Input;
float Low = (float)(Input - High);
}
总结
这种做法每个坐标都需要存储两倍大小的坐标信息high low两个vector3,但是似乎如果要支持double的话,没有其他好法子