在 Cesium 中从实景模型(如倾斜摄影模型或 3D Tiles 模型)中采样点,通常是指获取模型表面的坐标、法线或其他属性。这可以通过以下步骤实现:
方法一:使用 sampleHeight
函数
Cesium 提供了 sampleHeight
方法,可以从支持高度查询的地形或 3D Tiles 数据中获取指定位置的高度。
示例代码:
viewer.scene.sampleHeight({
positions: [Cesium.Cartographic.fromDegrees(longitude, latitude)],
objectsToExclude: [], // 可选,排除某些模型或对象
}).then(function(samples) {
let height = samples[0].height; // 获取第一个位置的高度
console.log("Sampled height: " + height);
});
要点:
- 需要提供一个或多个
Cartographic
位置。 - 结果包含对应的高度值。
- 此方法适用于模型具有支持采样的功能(如地形或某些 3D Tiles)。
方法二:使用 scene.pick
和射线投射
通过 scene.pick
和 scene.pickPosition
,可以通过屏幕上的点击或射线投射与模型交互,获取相应的三维坐标。
示例代码:
// 创建一个射线
let ray = viewer.camera.getPickRay(screenPosition);
// 获取模型表面点
let pickedPosition = viewer.scene.pickPosition(ray);
if (pickedPosition) {
console.log("Picked position: " + pickedPosition);
} else {
console.log("No position picked.");
}
要点:
screenPosition
是屏幕上的像素坐标(例如鼠标点击位置)。pickPosition
返回世界坐标系(Cartesian3)中的点。- 需要模型启用了深度缓冲,否则可能无法拾取。
方法三:直接访问 3D Tiles 数据
如果需要更高精度或更多信息,可以直接操作 3D Tiles
的数据结构,例如 TileContent
或 TileFeatures
。
示例代码:
let tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url: "URL_TO_YOUR_3DTILES"
}));
tileset.readyPromise.then(function(tileset) {
let boundingVolume = tileset.boundingVolume.boundingVolume;
console.log("Bounding Volume: ", boundingVolume);
// 访问单个 tile 的内容
tileset.tileVisible.addEventListener(function(tile) {
let content = tile.content;
console.log("Tile content: ", content);
});
});
要点:
- 需要熟悉 3D Tiles 数据结构。
- 可以获取更详细的模型元数据,但解析较复杂。
方法四:结合地形和模型
如果模型覆盖了地形,可能需要从地形和模型中分别采样高度,并进行对比。
示例代码:
// 地形采样
Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, [
Cesium.Cartographic.fromDegrees(longitude, latitude)
]).then(function(samples) {
let terrainHeight = samples[0].height;
console.log("Terrain height: " + terrainHeight);
});
// 实景模型采样(结合 pickPosition)
let ray = viewer.camera.getPickRay(screenPosition);
let modelPosition = viewer.scene.pickPosition(ray);
if (modelPosition) {
console.log("Model surface height: ", Cesium.Cartesian3.fromElements(modelPosition));
}
注意事项
- 深度缓冲区:要确保
viewer.scene.globe.depthTestAgainstTerrain
为true
,以提高采样精度。 - 数据支持:只有部分类型的 3D Tiles 数据支持直接采样。
- 性能:大规模采样可能需要批量处理,以避免性能瓶颈。
选择具体方法取决于您的实际应用需求和数据类型。
如果仅有三维实景模型(例如倾斜摄影生成的 3D Tiles 模型),需要通过 Cesium 提供的功能从模型表面采样所有区域的点。这种需求通常用于重建表面网格、分析地形等。以下是可能的实现步骤:
方法概述
-
确定采样区域和分辨率
确定需要采样的地理范围(如经纬度范围)以及采样的分辨率(步长)。 -
生成采样网格
在地理范围内生成规则的经纬度网格点。 -
使用
sampleHeightMostDetailed
或pickPosition
获取模型表面点
将经纬度网格点逐一映射到模型表面,得到三维坐标点。 -
处理和存储采样数据
存储采样结果,例如输出为 CSV、GeoJSON,或直接用于进一步处理。
实现步骤
1. 确定采样区域和分辨率
定义感兴趣区域的经纬度范围和步长。例如:
const west = -123.1; // 最西经度
const east = -122.9; // 最东经度
const south = 37.7; // 最南纬度
const north = 37.9; // 最北纬度
const step = 0.001; // 经纬度步长
2. 生成采样网格
根据区域范围和步长,生成一组经纬度点。
function generateGrid(west, east, south, north, step) {
const points = [];
for (let lat = south; lat <= north; lat += step) {
for (let lon = west; lon <= east; lon += step) {
points.push(Cesium.Cartographic.fromDegrees(lon, lat));
}
}
return points;
}
const gridPoints = generateGrid(west, east, south, north, step);
3. 采样表面高度
利用 scene.sampleHeightMostDetailed
或 pickPosition
对每个网格点进行采样。
使用 sampleHeightMostDetailed
sampleHeightMostDetailed
可以批量采样 3D Tiles 表面点高度。
viewer.scene.sampleHeightMostDetailed(gridPoints).then((samples) => {
const results = samples.map(sample => ({
longitude: Cesium.Math.toDegrees(sample.cartographic.longitude),
latitude: Cesium.Math.toDegrees(sample.cartographic.latitude),
height: sample.height
}));
console.log(results);
// 处理或保存结果
});
使用射线 pickPosition
如果 sampleHeightMostDetailed
不适用,可以通过 pickPosition
获取模型表面坐标。
const sampledPositions = [];
gridPoints.forEach(cartographic => {
const cartesian = Cesium.Cartographic.toCartesian(cartographic, viewer.scene.globe.ellipsoid);
const ray = new Cesium.Ray(cartesian, Cesium.Cartesian3.UNIT_Z); // 朝向模型的射线
const position = viewer.scene.pickPosition(ray);
if (position) {
const cartographicPosition = Cesium.Cartographic.fromCartesian(position);
sampledPositions.push({
longitude: Cesium.Math.toDegrees(cartographicPosition.longitude),
latitude: Cesium.Math.toDegrees(cartographicPosition.latitude),
height: cartographicPosition.height
});
}
});
console.log(sampledPositions);
4. 存储采样结果
可以将结果保存为 GeoJSON、CSV 或其他格式:
保存为 CSV
function saveAsCSV(data) {
const csvContent = "longitude,latitude,height\n" +
data.map(row => `${row.longitude},${row.latitude},${row.height}`).join("\n");
const blob = new Blob([csvContent], { type: "text/csv" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "sampled_points.csv";
a.click();
}
saveAsCSV(sampledPositions);
注意事项
-
精度与性能权衡
- 小步长会增加采样点数量,但可能导致性能下降。
- 如果范围较大,可分区域并行处理。
-
数据要求
sampleHeightMostDetailed
和pickPosition
依赖模型提供表面高度数据。- 模型需要加载到内存中,确保分块可见。
-
深度缓冲区
在使用pickPosition
时,确保启用深度缓冲区:viewer.scene.globe.depthTestAgainstTerrain = true;
-
大规模采样
对大范围区域,可以使用分布式方法(如 Web Workers)加速采样。
通过上述方法,可以从 3D Tiles 实景模型中采样出所有区域的点并进行进一步处理。
如果采样范围过大且时间过慢,可以通过优化采样方法和利用 GPU 加速来提高效率。以下是改进建议和具体方法:
1. 优化采样方法
1.1 自适应采样
根据模型的复杂度动态调整采样分辨率:
- 在地形平坦区域使用较大的步长。
- 在复杂区域(如建筑物密集处)使用较小的步长。
示例代码:
function adaptiveSampling(west, east, south, north, coarseStep, fineStep, complexityThreshold) {
const points = [];
for (let lat = south; lat <= north; lat += coarseStep) {
for (let lon = west; lon <= east; lon += coarseStep) {
const complexity = Math.random(); // 模拟复杂度分析
const step = complexity > complexityThreshold ? fineStep : coarseStep;
points.push(Cesium.Cartographic.fromDegrees(lon, lat));
}
}
return points;
}
const gridPoints = adaptiveSampling(-123.1, -122.9, 37.7, 37.9, 0.01, 0.001, 0.5);
实现复杂度分析:可通过分块的法向变化、梯度计算或其他属性来判断地形复杂度。
1.2 按块加载采样
将大范围划分为小块,逐块加载模型并采样,降低内存消耗和计算开销。
示例代码:
function splitRegion(west, east, south, north, step) {
const regions = [];
for (let lat = south; lat < north; lat += step) {
for (let lon = west; lon < east; lon += step) {
regions.push({ west: lon, east: lon + step, south: lat, north: lat + step });
}
}
return regions;
}
const regions = splitRegion(-123.1, -122.9, 37.7, 37.9, 0.1);
regions.forEach(region => {
const gridPoints = generateGrid(region.west, region.east, region.south, region.north, 0.001);
viewer.scene.sampleHeightMostDetailed(gridPoints).then(samples => {
console.log("Sampled points: ", samples);
});
});
1.3 视野裁剪
只对屏幕范围内可见区域进行采样,减少不必要的计算。
示例代码:
const boundingRectangle = viewer.camera.computeViewRectangle();
const gridPoints = generateGrid(
Cesium.Math.toDegrees(boundingRectangle.west),
Cesium.Math.toDegrees(boundingRectangle.east),
Cesium.Math.toDegrees(boundingRectangle.south),
Cesium.Math.toDegrees(boundingRectangle.north),
0.001
);
2. 利用 GPU 加速
Cesium 本身在大部分操作中已经利用了 GPU,但可以通过以下方式进一步优化:
2.1 WebGL 批量计算
利用 pickPosition
的同时通过 WebGL 批量处理大量点。例如:
- 批量生成射线,直接渲染采样区域。
- 通过着色器进行复杂度检测。
示例框架:Cesium + custom WebGL
-
创建自定义着色器:
使用 Cesium 的customShader
功能来处理采样逻辑。 -
示例代码:
const customShader = new Cesium.CustomShader({
vertexShaderText: `
void main() {
gl_Position = ...; // 自定义顶点处理
}
`,
fragmentShaderText: `
void main() {
float height = ...; // 根据输入点计算高度
gl_FragColor = vec4(height, 0.0, 0.0, 1.0);
}
`,
});
tileset.customShader = customShader;
- 收集结果:
通过帧缓冲或pickPosition
获取数据。
2.2 GPU 计算框架
借助 GPU 计算框架(如 WebGPU 或三方库)对采样点进行并行计算:
3. 分布式采样
3.1 多线程 (Web Workers)
将采样任务分配给多个 Web Worker 并行处理,减少主线程负担。
示例代码:
const worker = new Worker('samplingWorker.js');
// 发送任务
worker.postMessage({ points: gridPoints });
// 接收结果
worker.onmessage = (event) => {
console.log("Sampled results: ", event.data.samples);
};
4. 选择合适的数据结构和存储方式
采样过程中,数据存储会影响性能:
- 空间索引优化:对采样点建立空间索引(如四叉树、R树)以加速检索。
- 流式处理:将采样结果流式写入文件(如 CSV)或直接传输至后端。
5. 总结
在范围过大或时间过慢时:
- 优化采样分辨率,按复杂度动态调整。
- 分块处理,逐块加载和采样。
- 利用 GPU 并行计算,加速大量点的处理。
- 分布式任务处理,充分利用多核 CPU 和 Web Workers。
- 结合视野裁剪与空间索引,减少不必要的计算。
根据具体需求和硬件条件,灵活组合这些方法可以显著提高效率。
标签:采样,const,模型,点集,let,Cesium,height From: https://blog.csdn.net/m0_55049655/article/details/144605603