原文链接及内容

示例代码

本文是一个加载 glb、gltf 格式模型的示例。

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
<template>
<CesiumMap />
<div class="control">
<el-select
class="select"
v-model="value"
placeholder="空"
@change="handleSelectChange"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-button type="primary" @click="unlockCamera">
解除相机锁定
<el-icon class="el-icon--right"><i-ep-Unlock /></el-icon>
</el-button>
</div>
</template>

<script setup>
import useCSViewerStore from "@/stores/csViewer.js";

//#region --------------------- 定义变量----------------
const cvs = useCSViewerStore();
let viewer;

const value = ref("0");

const options = [
{ value: "0", label: "请选择要加载的模型" },
{ value: "1", label: "飞机" },
{ value: "2", label: "无人机" },
{ value: "3", label: "地面车辆" },
{ value: "4", label: "热气球" },
{ value: "5", label: "运奶开车" },
{ value: "6", label: "带有皮肤的角色" },
{ value: "7", label: "不发光的盒子" },
{ value: "8", label: "Draco压缩模型" },
{ value: "9", label: "KTX 2.0纹理压缩气球" },
{ value: "10", label: "实例箱模型" },
];

//#endregion

onMounted(() => {
viewer = cvs.viewer;
});

//#region --------------------- 方法区域----------------
function handleSelectChange(value) {
switch (value) {
case "1": {
createModel("./SampleData/models/CesiumAir/Cesium_Air.glb", 5000.0);
break;
}
case "2": {
createModel("./SampleData/models/CesiumDrone/CesiumDrone.glb", 150.0);
break;
}
case "3": {
createModel("./SampleData/models/GroundVehicle/GroundVehicle.glb", 0);
break;
}
case "4": {
createModel(
"./SampleData/models/CesiumBalloon/CesiumBalloon.glb",
1000.0
);
break;
}
case "5": {
createModel(
"./SampleData/models/CesiumMilkTruck/CesiumMilkTruck.glb",
0
);
break;
}
case "6": {
createModel("./SampleData/models/CesiumMan/Cesium_Man.glb", 0);
break;
}
case "7": {
createModel("./SampleData/models/BoxUnlit/BoxUnlit.gltf", 10.0);
break;
}
case "8": {
createModel(
"./SampleData/models/DracoCompressed/CesiumMilkTruck.gltf",
0
);
break;
}
case "9": {
if (!Cesium.FeatureDetection.supportsBasis(viewer.scene)) {
ElMessage.warning("此浏览器不支持 Basis Universal 压缩纹理");
} else {
createModel(
"../SampleData/models/CesiumBalloonKTX2/CesiumBalloonKTX2.glb",
1000.0
);
}
break;
}
case "10": {
createModel("./SampleData/models/BoxInstanced/BoxInstanced.gltf", 15);
break;
}
default: {
break;
}
}
}

function createModel(url, height) {
viewer.entities.removeAll();
const position = Cesium.Cartesian3.fromDegrees(
-123.0744619,
44.0503706,
height
);
const heading = Cesium.Math.toRadians(135);
const pitch = 0;
const roll = 0;
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
const orientation = Cesium.Transforms.headingPitchRollQuaternion(
position,
hpr
);
const entity = viewer.entities.add({
position: position,
orientation: orientation,
model: {
uri: url,
minimumPixelSize: 128,
maximumScale: 20000,
},
});
/**
*
*/
viewer.trackedEntity = entity;
}

function unlockCamera() {
viewer.trackedEntity = undefined;
}
//#endregion
</script>

<style lang="scss" scoped>
.control {
position: absolute;
top: 20px;
right: 20px;
.select {
width: 200px;
margin-right: 10px;
}
}
</style>

细嚼慢咽

  1. Draco 是一个用于压缩和解压缩 3D 几何网格和点云的库。它旨在改进 3D 图形的存储和传输。具体请参考:https://codelabs.developers.google.com/codelabs/draco-3d?hl=zh-cn#0

  2. KTX(Khronos Texture)是一种高效、轻量级的容器格式,用于可靠地将 GPU 纹理分发到各种平台和应用。KTX 2.0 增加了对 Basis Universal 超级压缩 GPU 纹理的支持。具体请参考官方介绍:https://www.khronos.org/ktx/

  3. Cesium.FeatureDetection.supportsBasis:如果浏览器支持 Web Assembly 模块且场景支持 Basis Universal 纹理,则为真;如果不支持,则为假。

  4. 加载模型的关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function createModel(url, height) {
viewer.entities.removeAll();
const position = Cesium.Cartesian3.fromDegrees(
-123.0744619,
44.0503706,
height
);
const heading = Cesium.Math.toRadians(135);
const pitch = 0;
const roll = 0;
const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
const orientation = Cesium.Transforms.headingPitchRollQuaternion(
position,
hpr
);
const entity = viewer.entities.add({
position: position,
orientation: orientation,
model: {
uri: url,
minimumPixelSize: 128,
maximumScale: 20000,
},
});
viewer.trackedEntity = entity;
}
  • HeadingPitchRoll:记得测绘中坐标系转换中七参数转换中就有3个坐标平移量,3个坐标轴的旋转角度以及一个尺度因子,这里的HeadingPitchRoll我们可以将其理解为旋转,官方文档翻译如下:

注:七参数百科:https://baike.baidu.com/item/%E4%B8%83%E5%8F%82%E6%95%B0/4660045

HeadingPitchRoll类

  • headingPitchRollQuaternion:直接参考官方文档,具体如下图所示:

headingPitchRollQuaternion类

  • trackedEntity:Cesium.Viewer 对象的trackedEntity属性用于指定当前被追踪的实体(Entity)。当设置了 trackedEntity 属性后,相机会自动跟随该实体,并在实体的位置发生变化时更新视角。

使用场景

  • 实体追踪:当你希望相机自动跟随某个实体(如飞机、车辆、船只等)时,可以将该实体赋值给 trackedEntity。
  • 取消追踪:如果你想取消对当前实体的追踪,可以将 trackedEntity 设置为 undefined 或 null。
    但是,频繁更新 trackedEntity 或实体的位置可能会影响性能,尤其是在实体移动速度较快或场景复杂度较高的情况下。