開門見山:tileset.modelMatrix
這個屬性可以在數據本身的基礎上再進行坐標變換,不熟悉轉換矩陣各個部分的含義的可參考圖形學有關資料。
此文不一定是最佳算法,但是提供一種思路。轉載請注明出處 全網@秋意正寒 。
平移思路
- 獲取當前瓦片數據集的包裹范圍(boundingSphere)中心(此時參考系是世界坐標)
- 計算當參考系是局部坐標時,此位置為原點的局部坐標系,到世界坐標的轉換矩陣(eastNorthUpToFixedFrame)
- 利用上一步的轉換矩陣,左乘一個局部平移向量,得到此平移向量在世界坐標系下的平移目標位置(矩陣×向量,結果是向量)
- 向量相減:世界坐標系下,指向平移目標點位的目標向量 - 指向數據集中心的向量,得到世界坐標系下的平移向量。
- 將世界坐標系下的平移向量轉換成平移矩陣,賦予
tileset.modelMatrix
代碼
tileset
.readyPromise
.then(tileset => {
const tileset_center = tileset.boundingSphere.center; // Cartesian3
const frompoint_to_world_matrix = Cesium.Transforms.eastNorthUpToFixedFrame(tileset_center); // Matrix4
const local_translation = new Cesium.Cartesian3(310, -140, 10); // 向模型中心為原點,正北為y,正東為x,地心朝上為z分別平移 310、-140、10米
const result = new Cesium.Cartesian3(0,0,0);
Cesium.Matrix4.multiplyByPoint(frompoint_to_world_matrix, local_translation, result); // 轉換矩陣左乘局部平移向量,結果存儲在 result 中,結果是世界坐標下的平移終點向量
const targetpoint_to_world_matrix = Cesium.Transforms.eastNorthUpToFixedFrame(result);
const world_translation = new Cesium.Cartesian3(
targetpoint_to_world_matrix[12] - frompoint_to_world_matrix[12],
targetpoint_to_world_matrix[13] - frompoint_to_world_matrix[13],
targetpoint_to_world_matrix[14] - frompoint_to_world_matrix[14],
); // 向量相減,得到世界坐標下的平移向量
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(world_translation); // 構造平移矩陣並賦值
viewer.zoomTo(tileset);
});
圖解
解釋:
- 紅點:frompoint(地表點)
- 藍點:targetpoint(frompoint局部坐標向東向北向上偏移各 310、-140、10米 后得到的目標點)
- 紅向量:地表點向量
- 藍向量:目標點向量
- 綠向量:平移向量。如果是局部坐標,那么就是 (310, -140, 10),如果是世界坐標下的,那就是 藍向量 - 紅向量
cesium 的場景數據最終都是世界坐標的,所以要求的是綠向量的世界坐標表達,然后構造平移矩陣。
現在是已知紅向量和局部坐標的綠向量,要先求藍向量,才能得到世界坐標的綠向量。
平移思路二
先平移到世界坐標中心,然后在世界坐標中心求平移,最后再移動回原點。
tileset.modelMatrix
\(= M_{backToOrigin}·M_{localTranslation}·M_{moveToWorldCenter}\)
讀者可自行實現。
旋轉思路
局部旋轉,應該先將模型移動到世界坐標中心,旋轉后,再移動到原來的地方
即
tileset.modelMatrix
\(= M_{backToOrigin}·M_{localRotate}·M_{moveToWorldCenter}\)
其中,\(M_{moveToWorldCenter}\) 是一個平移矩陣,只需使用模型中心向量取個負值即可
\(M_{backToOrigin}\) 則是從世界坐標中心再移動到模型原點
注意,這里是左乘優先順序,從右往左乘。
旋轉矩陣比較容易構造,就不細說了。
這個思路的計算量比較大!
效果圖(繞模型本身x軸轉90度)
代碼
tileset
.readyPromise
.then(tileset => {
const tileset_center = tileset.boundingSphere.center; // Cartesian3
//console.log(tileset_center);
const backto_matrix = Cesium.Matrix4.fromTranslation(tileset_center);
const moveto_vec = Cesium.Cartesian3.multiplyByScalar(tileset_center, -1, new Cesium.Cartesian3());
//console.log(moveto_vec);
const moveto_matrix = Cesium.Matrix4.fromTranslation(moveto_vec);
/* 繞x(即東方軸)轉90度 */
const cos_rotateX = Math.cos(Math.PI/2);
const sin_rotateX = Math.sin(Math.PI/2);
const arr = [1,0,0,0, 0, cos_rotateX, sin_rotateX,0, 0,-sin_rotateX,cos_rotateX,0, 0,0,0,1];
const rotateX_matrix = Cesium.Matrix4.fromArray(arr);
/* 計算最終矩陣 */
const temp = Cesium.Matrix4.multiply(rotateX_matrix, moveto_matrix, new Cesium.Matrix4());
const r = Cesium.Matrix4.multiply(backto_matrix, temp, new Cesium.Matrix4());
tileset.modelMatrix = r; // 構造平移矩陣並賦值
viewer.zoomTo(tileset);
});
可以看到會產生非常多中間變量,會引發 JS 的GC,而且矩陣計算本身也比較復雜。
縮放思路
縮放思路和旋轉類似,先移動到世界坐標系中心,縮放,然后再移動到原來的地方
tileset.modelMatrix
\(= M_{backToOrigin}·M_{localScale}·M_{moveToWorldCenter}\)
讀者可自行實現。
注
思路不一定是最佳算法,有更高性能的算法可在評論區指出。