var circle = new Cesium.CircleGeometry({ center: Cesium.Cartesian3.fromDegrees(val.longitude, val.latitude), radius: 8000, segments: 32, extrudedHeight: 0, perPositionHeight: true }); var geometry = Cesium.CircleGeometry.createGeometry(circle); const instance = new Cesium.GeometryInstance({ geometry: geometry, }); const pline = this.getCircleLine(val.longitude,val.latitude,8000) const polylineGeometry = new Cesium.PolylineGeometry({ positions: pline, width: 4.0, vertexFormat: Cesium.PolylineMaterialAppearance.VERTEX_FORMAT }); const polylineInstance = new Cesium.GeometryInstance({ geometry: Cesium.PolylineGeometry.createGeometry(polylineGeometry) }); var primitives = new Cesium.Primitive({ geometryInstances: instance, appearance: new Cesium.MaterialAppearance({ material: new Cesium.Material({ fabric: { type: "Color", uniforms: { color: Cesium.Color.fromCssColorString("#2963C3").withAlpha(0.1) } }, }), translucent: true, // 设置renderState来控制深度测试 renderState: { depthTest: { enabled:true, // 是否启用深度测试 }, depthMask: false, } }), asynchronous: false, show:true, allowPicking: false, zIndex: 0 }) var primitivesLine = new Cesium.Primitive({ geometryInstances: polylineInstance, appearance: new Cesium.PolylineMaterialAppearance({ material: Cesium.Material.fromType("Color", { color: Cesium.Color.fromCssColorString("#2963C3").withAlpha(1) }), translucent: true, // 设置renderState来控制深度测试 renderState: { depthTest: { enabled:true, // 是否启用深度测试 }, depthMask: false, } }), asynchronous: false, show:true, allowPicking: false, zIndex: 1 }) this.circleEntityMap.set(val,primitives) this.circleEntityLineMap.set(val,primitivesLine) this.circleEntity = window.viewer.scene.primitives.add(primitives); this.circleEntityOutline = window.viewer.scene.primitives.add(primitivesLine);开启了深度监测。会有部分在地下怎么能贴地呢
时间: 2025-08-07 18:05:29 AIGC 浏览: 19
<think>我们有两个问题需要解决:
1. 确保创建的点位(Entity)显示在圆形(Primitive)之上。
2. 在开启深度测试的情况下,让圆形图形贴地渲染(即圆形要贴在地形表面,而不是部分显示在地下)。
针对第一个问题,我们已经讨论过可以通过调整渲染顺序和深度测试设置来解决。但第二个问题涉及到地形深度测试,我们需要确保圆形能够正确贴合地形表面。
解决方案:
一、确保点位在圆形之上(回顾并优化):
方法1:使用zIndex控制渲染层级(推荐)
- 将圆形的zIndex设置为较低的值(例如0),点位的zIndex设置为较高的值(例如1)。
- 注意:Entity本身不支持直接设置zIndex,但可以通过将其转换为Primitive并设置zIndex来实现(或者使用Entity的billboard和label的disableDepthTestDistance属性,但这样可能无法与地形深度测试兼容)。
方法2:调整添加顺序和深度测试设置
- 先添加圆形(Primitive),再添加点位(Entity)。因为Entity默认在Primitive之后渲染(Entity由DataSourceDisplay处理,它在PrimitiveCollection之后渲染)。
- 同时,为点位的label和billboard设置`disableDepthTestDistance: Number.POSITIVE_INFINITY`,这样它们将忽略深度测试,始终显示在最前面。
但是,如果开启了地形深度测试,圆形可能会因为地形深度测试而部分被地形遮挡(即第二个问题)。
二、解决圆形贴地问题(开启深度测试时):
圆形要贴地,我们需要获取圆形每个点在地形上的准确高度。由于我们使用Turf生成圆形时,生成的点是二维的(经度、纬度),高度为0(或者参考椭球面高度),但实际地形有起伏,所以需要将圆形上的点采样并设置到地形表面。
步骤:
1. 使用地形采样功能(sampleTerrain)获取圆形轮廓点在地形上的高度。
2. 在生成圆形轮廓点后,将这些点(二维经纬度)加上地形高度,得到三维坐标。
3. 使用这些三维坐标创建圆形。
具体实现:
1. 修改getCircle函数,使其支持地形采样(异步):
- 由于地形采样是异步的,我们需要在回调中处理。
- 注意:圆形覆盖范围较大时,需要适当增加采样点数量(Turf生成圆形时可以设置步数)。
2. 在创建圆形Primitive时,设置高度模式为`Cesium.HeightReference.CLAMP_TO_GROUND`(但Primitive不支持直接设置高度模式,所以需要显式采样地形)。
3. 或者,我们可以使用Entity的Polygon(它支持贴地),但这里我们使用的是Primitive,因此需要手动采样地形。
修改后的getCircle函数(异步):
```javascript
export const getCircle = (viewer, center, radius, callback) => {
let center84 = Cartesian3ToWgs84(center);
let centerP = [center84.lon, center84.lat];
let circleInfo = circle(centerP, radius / 1000.0, { steps: 100 }); // 增加步数使圆形更平滑
// 获取圆形轮廓点(二维经纬度)
const positions = circleInfo.geometry.coordinates[0].map(item => Cesium.Cartographic.fromDegrees(item[0], item[1]));
// 使用sampleTerrain获取地形高度
const terrainProvider = viewer.terrainProvider;
Cesium.sampleTerrain(terrainProvider, 11, positions).then(updatedPositions => {
// 将Cartographic转换为Cartesian3
const cartesianPositions = updatedPositions.map(cartographic =>
Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, cartographic.height)
);
callback(cartesianPositions);
});
};
```
4. 创建圆形Primitive时,使用这些采样后的点(cartesianPositions)来构建几何。
但是,由于圆形是面,我们还需要构建一个多边形几何。注意,我们使用采样后的点构建多边形,这样多边形就会贴在地形上。
5. 同时,为了解决深度测试开启后圆形被地形遮挡的问题,我们需要在Primitive的appearance中正确设置深度测试和深度写入。但注意,我们想要圆形贴地,所以不能完全禁用深度测试(否则会穿到地下),而是应该让深度测试正确工作,同时确保圆形不被地形遮挡。然而,由于我们已经将圆形顶点采样到地形表面,所以开启深度测试后,圆形应该能正确显示在地形上。
但是,由于我们之前为了确保点位在圆形之上,禁用了点位的深度测试(`disableDepthTestDistance: Number.POSITIVE_INFINITY`),所以点位会始终显示在最前面。而圆形则根据深度测试正常显示。
因此,综合解决方案:
步骤:
1. 先创建圆形(Primitive),但需要等待地形采样完成,所以是异步的。
2. 然后创建点位(Entity),并设置`disableDepthTestDistance`为无限大,使其不受深度测试影响,始终在最前面。
3. 注意:由于圆形是异步创建的,所以点位的创建可以放在圆形创建之前(因为圆形创建需要时间,而点位创建后立即显示,圆形稍后添加,但点位的层级设置会确保它在圆形之上)。
但是,在添加顺序上,我们通常先添加圆形(Primitive)到场景中,然后再添加点位(Entity)。因为Entity默认在Primitive之后渲染,所以即使圆形后添加到场景,Entity(点位)也会在圆形之上渲染(因为Entity由DataSourceDisplay处理,它在PrimitiveCollection之后渲染)。所以,我们不需要调整添加顺序,而是利用Cesium的渲染顺序。
然而,为了确保万无一失,我们可以为点位的billboard和label设置`zIndex`(在Entity的billboard和label中,zIndex是支持的,但它只影响同一EntityCollection内的顺序,而EntityCollection是独立的,所以通常点位会显示在Primitive之上)。
因此,我们不需要将Entity转换为Primitive,而是直接使用Entity,并设置`disableDepthTestDistance`即可。
具体代码结构:
1. 创建点位(Entity):
```javascript
var point = window.viewer.entities.add({
position: data.position3,
billboard: {
image: 'path/to/image',
disableDepthTestDistance: Number.POSITIVE_INFINITY,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 如果点位也需要贴地,则设置
},
label: {
text: '点',
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
});
```
2. 创建圆形(异步):
```javascript
getCircle(window.viewer, data.position3, radius, function(cartesianPositions) {
// 使用这些点构建圆形几何(多边形)
const instance = new Cesium.GeometryInstance({
geometry: new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(cartesianPositions),
height: 0 // 注意:由于点已经采样了地形高度,所以这里高度设为0,但实际顶点坐标已经包含高度
// 或者我们可以使用每个点的高度,所以不需要额外设置高度
})
});
// 创建Primitive并添加到场景
var primitives = new Cesium.Primitive({
geometryInstances: instance,
appearance: new Cesium.MaterialAppearance({
material: Cesium.Material.fromType('Color', {
color: Cesium.Color.fromCssColorString("#2963C3").withAlpha(0.1)
}),
translucent: true
}),
// 注意:这里不需要禁用深度测试,因为我们要贴地,需要深度测试正确工作
// 但是,为了确保圆形不被其他物体(如地形)遮挡,我们需要确保深度写入正确,并且圆形在深度测试中通过
// 由于我们采样的是地形高度,所以圆形应该在地形表面,深度测试会正常显示
// 如果圆形仍然被遮挡,可以尝试调整深度测试函数,但通常不需要
allowPicking: false
});
window.viewer.scene.primitives.add(primitives);
});
```
3. 轮廓线同理。
注意:如果圆形范围很大,地形采样可能会比较耗时,可以考虑使用Web Worker。
三、开启深度测试后圆形部分显示在地下的原因及解决:
原因:圆形轮廓点的高度没有正确设置到地形高度,导致部分点低于地形。
解决:通过地形采样将轮廓点设置到地形高度。
四、确保点位在圆形之上:
点位的billboard和label设置了`disableDepthTestDistance: Number.POSITIVE_INFINITY`,所以它们会忽略深度测试,始终显示在最前面。而圆形则正常参与深度测试,但不会遮挡点位。
总结:
1. 点位使用Entity,并设置billboard和label的`disableDepthTestDistance`为无限大,使其置顶。
2. 圆形使用Primitive,并利用地形采样将轮廓点设置到地形高度,然后构建多边形,这样开启深度测试后圆形能正确贴地。
3. 轮廓线同理。
注意:圆形和轮廓线的Primitive不需要设置深度测试禁用,因为我们需要它们贴地。
这样,我们既解决了圆形贴地问题,又确保了点位在圆形之上。
如果遇到性能问题(比如大量圆形),建议使用Primitive代替Entity,并合并几何实例(GeometryInstance)进行批量绘制。
相关问题:
1. 如何动态更新贴地圆形的位置和大小?
2. 在Cesium中如何高效绘制大量贴地圆形?
3. 如何为贴地圆形添加鼠标事件?</think>### 解决圆形贴地与点位置顶问题
在 Cesium 中实现圆形贴地渲染且确保点位置顶显示,需要同时处理**深度测试**和**渲染层级**两个关键点。以下是完整解决方案:
---
### 1. **圆形贴地渲染(解决深度测试问题)**
通过地形采样获取精确高度值,确保圆形贴地显示:
```javascript
// 获取圆形轮廓点(带地形高度)
const getCircleWithTerrain = async (center, radius) => {
// 获取中心点地形高度[^2]
const cartographic = Cesium.Cartographic.fromCartesian(center);
const height = await Cesium.sampleTerrainMostDetailed(
viewer.terrainProvider,
[cartographic]
);
// 调整中心点高度(避免Z-fighting)
const adjustedCenter = Cesium.Cartesian3.fromRadians(
cartographic.longitude,
cartographic.latitude,
height[0].height + 0.1 // 增加微小高度偏移
);
// 生成贴地圆形轮廓点[^1]
const positions = [];
for (let i = 0; i <= 360; i += 5) {
const pos = Cesium.Cartesian3.fromDegrees(
...turf.destination(
[cartographic.longitude, cartographic.latitude],
radius,
i
).geometry.coordinates,
height[0].height // 使用地形高度
);
positions.push(pos);
}
return positions;
};
// 创建贴地圆形
const createClampedCircle = async (center, radius) => {
const positions = await getCircleWithTerrain(center, radius);
return new Cesium.Primitive({
geometryInstances: new Cesium.GeometryInstance({
geometry: new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(positions),
height: 0, // 关键:高度设为0以贴地
perPositionHeight: true // 使用每个点的独立高度
})
}),
appearance: new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType("Color", {
color: Cesium.Color.BLUE.withAlpha(0.5)
})
}),
// 深度测试配置
depthTest: {
enabled: true, // 开启深度测试
func: Cesium.DepthFunction.LESS_OR_EQUAL // 允许等于地形深度
}
});
};
```
---
### 2. **确保点位置顶显示**
使用分层渲染策略控制显示优先级:
```javascript
// 创建高层级渲染容器(置顶层)
const topLayer = new Cesium.PrimitiveCollection();
topLayer.zIndex = 100; // 最高层级
viewer.scene.primitives.add(topLayer);
// 创建低层级渲染容器(地形层)
const groundLayer = new Cesium.PrimitiveCollection();
groundLayer.zIndex = 0; // 底层
viewer.scene.primitives.add(groundLayer);
// 添加圆形到地形层
const circle = await createClampedCircle(center, 1000);
groundLayer.add(circle);
// 添加点位到置顶层
const pointPrimitive = topLayer.add(new Cesium.PointPrimitiveCollection());
const point = pointPrimitive.add({
position: center,
color: Cesium.Color.RED,
pixelSize: 15,
// 关键:禁用深度测试确保置顶
disableDepthTestDistance: Number.POSITIVE_INFINITY
});
```
---
### 3. **深度测试配置原理**
| 配置项 | 值 | 作用 |
|----------------------------|-----------------------------|----------------------------------------------------------------------|
| `depthTest.enabled` | `true` | 开启深度检测,避免穿入地下 |
| `depthTest.func` | `LESS_OR_EQUAL` | 允许等于地形高度,实现贴地效果 |
| `perPositionHeight` | `true` | 使用每个顶点独立高度值 |
| `disableDepthTestDistance` | `Number.POSITIVE_INFINITY` | 点位无视深度测试,始终显示在最前 |
---
### 4. **性能优化建议**
1. **批量处理**:对多个圆形使用`GeometryInstance`合并渲染
```javascript
const instances = circles.map(circle =>
new Cesium.GeometryInstance({ geometry: circleGeometry })
);
groundLayer.add(new Cesium.Primitive({ geometryInstances: instances }));
```
2. **LOD控制**:根据视距动态调整圆形分段数
```javascript
const segments = Cesium.Math.clamp(
Math.ceil(2000 / viewer.camera.positionCartographic.height),
36, // 最小分段
180 // 最大分段
);
```
---
### 常见问题解决
1. **圆形闪烁问题**:
```javascript
// 解决方案:增加高度偏移
height[0].height + 0.1 // 在采样高度上增加10厘米偏移
```
2. **圆形边缘锯齿**:
```javascript
// 开启抗锯齿
viewer.scene.postProcessStages.fxaa.enabled = true;
```
> **关键点**:通过分离渲染层级(`PrimitiveCollection`)和精确地形采样,同时解决贴地需求与显示优先级问题[^1][^2]。
---
### 相关问题
1. 如何动态更新贴地圆形的位置和大小?
2. 在Cesium中如何实现圆形区域的交互式编辑?
3. 地形采样时出现性能瓶颈应如何优化?
4. 如何为贴地圆形添加渐变填充效果?
[^1]: 将世界坐标系转换为球面坐标系,并利用Turf生成圆形点位
[^^2]: 通过Scene.updateHeight和getHeight函数实现地形高度采样
阅读全文
相关推荐




















