Cesium 解决primitive相互遮挡问题

在Cesium中遇到面遮挡城市边界的显示问题,发现显示顺序由添加顺序决定,可以通过`raiseToTop`方法调整primitive的层级,将边界线提升至顶层。若无效果,可启用深度测试进行优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

let primitives = viewer.scene.primitives;
 
let primitive1 = primitives.add(new Cesium.Primitive({
  // 资料...
}));
 
let primitive2 = primitives.add(new Cesium.Primitive({
  // 资料...
}));
 
// 提升primitive2至顶层
primitives.raiseToTop(primitive2);

在做Cesium时,添加了一个面和一个城市的边界,后来发现添加的面总是遮住了边界线。在添加时候找配置想设置有关z-index的配置并没有找到。
后来知道这个显示层级是根据前后添加的顺序决定的。后添加的primitive就在上层。
另外还有一个raiseToTop方法可以直接将某个primitive置于顶层。

(如果没有效果尝试开启深度测试)

<think>我们正在使用CesiumPrimitive API来绘制椭球体,并希望为这个椭球体添加网格线(网状线)。这通常用于增强椭球体的可视化效果,使其表面结构更清晰。在Cesium中,几何图形(Geometry)和外观(Appearance)是Primitive的两个核心组成部分。几何图形定义了形状,而外观定义了着色方式。要为椭球体添加网格线,我们可以考虑以下两种方法:方法一:使用线框模式(wireframe)我们可以通过设置外观(Appearance)的`renderState`中的`wireframe`属性为`true`来显示线框。但是,这种方法会将整个几何体显示为线框,包括底面和内部结构,可能不是我们想要的仅表面网格线效果。方法二:自定义几何图形(Geometry)和外观(Appearance)我们可以创建一个自定义的几何图形,该几何图形既包含椭球体的表面(使用三角网格)又包含网格线(使用线几何)。然后,我们可以使用两个Primitive:一个用于填充表面,另一个用于绘制网格线。或者,我们可以使用一个Primitive,但使用两个几何图形实例(一个为三角形,一个为线)并分别指定外观。然而,更常见的做法是创建两个Primitive:-第一个Primitive使用EllipsoidGeometry(或类似)和PerInstanceColorAppearance(或MaterialAppearance)来绘制填充表面。-第二个Primitive使用自定义的线几何来绘制网格线。这个线几何需要根据椭球体的参数生成网格线。如何生成椭球体表面的网格线?我们可以通过参数化椭球体方程来生成网格线。椭球体的参数方程通常为:$$x =a \cos(\theta)\sin(\phi)$$$$y =b \sin(\theta)\sin(\phi)$$$$z =c \cos(\phi)$$其中,$a,b,c$分别为椭球体在x、y、z方向的半径,$\theta$为经度角(0到$2\pi$),$\phi$为纬度角(0到$\pi$)。我们可以通过固定$\phi$并变化$\theta$来生成纬线,固定$\theta$并变化$\phi$来生成经线。步骤:1.计算网格线的顶点:我们需要计算一系列经线和纬线的顶点。2.将这些顶点组织成线几何(使用PolylineGeometry或PolylineCollection)?注意:PolylineGeometry不支持在球面上直接绘制曲线,它是在笛卡尔坐标系中绘制直线段。因此,我们需要用足够多的线段来近似曲线。但是,使用Primitive API,我们可以创建一个自定义的GeometryInstance,其中包含线几何(PolylineGeometry)的多个实例,或者我们可以使用一个GeometryInstance来包含所有的线(但需要将所有的线合并成一个几何)。另一种方法是使用GroundPolylinePrimitive,但它主要用于贴地线。对于椭球体表面的网格线,我们可以在椭球体表面生成线,然后使用简单的线几何。具体实现:1.创建椭球体表面的填充部分:```javascriptvarellipsoidSurface= newCesium.Primitive({geometryInstances:new Cesium.GeometryInstance({geometry: newCesium.EllipsoidGeometry({radii: newCesium.Cartesian3(a, b, c), //椭球体的三个半径//其他参数,如顶点格式等}),attributes: {color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE.withAlpha(0.5))//半透明填充}}),appearance: newCesium.PerInstanceColorAppearance({translucent: true,closed: true})});```2.创建网格线部分:我们需要生成网格线的顶点。假设我们将经度(theta)分为n等份,纬度(phi)分为m等份。然后,对于每一圈纬线(固定phi),我们生成一个圆(由n条线段组成),对于每一条经线(固定theta),我们生成从北极到南极的半圆(由m条线段组成)。但是,注意:这样生成的线数量会很多,可能会影响性能。因此,我们可以适当减少分段数。生成纬线:```javascriptvarpositions =[];for (var i=0;i <=m;i++){var phi= i* Math.PI/ m; //0到pifor (var j=0;j <=n;j++){var theta= j*2 *Math.PI /n;varx =a *Math.cos(theta)* Math.sin(phi);vary =b *Math.sin(theta)* Math.sin(phi);varz =c *Math.cos(phi);positions.push(newCesium.Cartesian3(x,y,z));}}//然后,对于每个phi(除极点的phi=0和phi=pi),我们连接该圈上的所有点形成一条闭合线//但是,注意:PolylineGeometry需要连续的线段,所以我们可以为每个纬圈创建一个PolylineGeometry实例(或者使用一个GeometryInstance,但需要将所有的线合并成一个几何体,这比较复杂)```另一种思路:使用一个GeometryInstance,但使用多个线段。我们可以创建一个包含所有线段的Geometry,使用Geometry和GeometryAttributes中的位置属性,并设置索引。然而,Cesium提供了SimplePolylineGeometry,它可以从一组点创建一条折线。但如果我们想要多条线,我们需要多个GeometryInstance。我们可以使用一个GeometryInstance,但需要将所有的线段(每两个点一条线段)合并到一个几何中。这需要我们自己构建几何(使用GeometryFactory或自定义)。为了方便,我们可以使用多个Primitive(每个纬圈和经圈分别用一个Primitive),但这样效率较低。更好的方法是使用一个Primitive,包含一个包含所有线段的几何。我们可以使用PolylineGeometry的聚合:PolylineGeometry.combine,但它已经过时。推荐使用GeometryInstance的聚合。示例:创建一个包含所有网格线的GeometryInstance:```javascript//生成所有网格线的顶点(每两个点构成一条线段)var positions= []; //所有线段的顶点列表(每两个点一条线段,所以线段数量为顶点数/2)//生成纬线for(vari=0;i <=m;i++){var phi= i* Math.PI/ m;for(varj =0;j <n;j++){var theta1 =j *2* Math.PI/ n;var theta2 =(j+1) *2* Math.PI/ n;//计算两个点varx1= a* Math.cos(theta1)* Math.sin(phi);vary1= b* Math.sin(theta1)* Math.sin(phi);varz1= c* Math.cos(phi);varx2= a* Math.cos(theta2)* Math.sin(phi);vary2= b* Math.sin(theta2)* Math.sin(phi);varz2= c* Math.cos(phi);positions.push(newCesium.Cartesian3(x1, y1,z1));positions.push(new Cesium.Cartesian3(x2,y2, z2));}}//生成经线for (var j=0;j <n;j++){var theta= j*2 *Math.PI /n;for(vari =0;i <m;i++){var phi1 =i *Math.PI /m;varphi2= (i+1)* Math.PI/ m;var x1 =a *Math.cos(theta)* Math.sin(phi1);var y1 =b *Math.sin(theta)* Math.sin(phi1);var z1 =c *Math.cos(phi1);varx2= a* Math.cos(theta) *Math.sin(phi2);vary2= b* Math.sin(theta) *Math.sin(phi2);varz2= c* Math.cos(phi2);positions.push(new Cesium.Cartesian3(x1,y1, z1));positions.push(newCesium.Cartesian3(x2, y2,z2));}}//创建线几何实例var lineGeometryInstance= newCesium.GeometryInstance({geometry:new Cesium.SimplePolylineGeometry({positions:positions,colors: [Cesium.Color.RED],//可以为每条线段设置颜色,但这里所有线段同色colorsPerVertex: false,vertexFormat:Cesium.PolylineColorAppearance.VERTEX_FORMAT}),attributes: {color: newCesium.ColorGeometryInstanceAttribute(1.0,0.0,0.0,1.0) //红色}});//创建线Primitivevar linePrimitive= newCesium.Primitive({geometryInstances: lineGeometryInstance,appearance:new Cesium.PolylineColorAppearance(),asynchronous: false//同步创建,以便立即显示});```注意:上面的SimplePolylineGeometry只能创建一条折线(由多个连续线段组成),但我们的网格线是多条不连续的线段。所以我们需要使用多个SimplePolylineGeometry实例(每个线段一个实例?这样效率太低)或者使用PolylineGeometry的另一种方式。实际上,我们可以使用一个GeometryInstance,但使用一个包含所有线段的几何(使用索引和多个折线)。但SimplePolylineGeometry不支持多条折线。替代方案:使用自定义几何。我们可以创建一个Geometry,其中包含所有线段的顶点,并使用线段(LINES)图元。这需要手动构建几何属性(位置和颜色)和索引。但是,Cesium提供了PolylineVolumeGeometry,但也不适合。我们可以考虑使用PrimitiveCollection,分别添加每条线段,但这样性能较差。另一种思路:使用一个GeometryInstance,但使用PolylineGeometry的聚合方法(已过时)或使用GeometryPipeline.combineInstances合并多个GeometryInstance。我们可以创建多个GeometryInstance(每个线段一个实例),然后合并成一个GeometryInstance数组,再创建一个Primitive。但是,当线段数量很大时,可能会影响性能。折中方案:我们可以将网格线分成两类(经线和纬线),每类用一个GeometryInstance(即每个圈一个GeometryInstance),这样我们只需要n+m个GeometryInstance,然后放入一个Primitive中。```javascriptvarinstances =[];//纬线:每个纬圈是一个圆(由n条线段组成)for(vari =0; i<= m; i++) {varphi =i *Math.PI /m;varpositions =[];for (var j=0;j <=n;j++){var theta= j*2 *Math.PI /n;varx =a *Math.cos(theta)* Math.sin(phi);vary =b *Math.sin(theta)* Math.sin(phi);varz =c *Math.cos(phi);positions.push(new Cesium.Cartesian3(x, y, z));}instances.push(new Cesium.GeometryInstance({geometry: newCesium.SimplePolylineGeometry({positions: positions,//闭合,但SimplePolylineGeometry不自动闭合,所以我们需要手动添加第一个点//但上面已经计算了n+1个点(0到n),最后一个点和第一个点相同吗?不,我们只计算了0到2pi,所以最后一个点不是第一个点。所以我们需要在最后添加第一个点?//或者设置闭合为false,因为椭圆是闭合的?实际上,我们计算的最后一个点与第一个点并不重合(因为theta=0和theta=2pi是同一个点,但计算时我们只计算了0到2pi,不包括2pi,所以最后一个点就是第一个点?不对,j<=n,所以j=n时theta=2pi,所以最后一个点就是第一个点。因此,我们不需要额外添加。//注意:SimplePolylineGeometry会将点依次连接,所以这样就是一个闭合的圆(除了极点,因为极点phi=0时,所有点都是同一个点,所以会退化成一个点,phi=pi同理)//对于极点,我们跳过(因为phi=0或pi时,所有点都是同一个点,所以不绘制)//如果phi=0或pi,则positions中的所有点都是同一个点,那么SimplePolylineGeometry会绘制一个点,但不会显示。所以我们可以跳过极点。}),attributes:{color:Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)}}));}//经线:每条经线是一个半圆(从北极到南极)for(varj =0;j <n;j++){vartheta =j *2* Math.PI/ n;var positions= [];for(vari =0; i<= m; i++) {varphi =i *Math.PI /m;varx =a *Math.cos(theta)* Math.sin(phi);vary =b *Math.sin(theta)* Math.sin(phi);varz =c *Math.cos(phi);positions.push(new Cesium.Cartesian3(x, y, z));}instances.push(new Cesium.GeometryInstance({geometry: newCesium.SimplePolylineGeometry({positions: positions}),attributes:{color:Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)}}));}varlinePrimitive =new Cesium.Primitive({geometryInstances:instances,appearance: newCesium.PolylineColorAppearance({translucent: false}),asynchronous:false});```这样,我们创建了一个Primitive,其中包含多个GeometryInstance(每个网格圈一个)。注意:当网格线较多时,性能可能会受到影响,但通常网格线不会太密集。3.将两个Primitive添加到viewer.scene.primitives中:```javascriptviewer.scene.primitives.add(ellipsoidSurface);viewer.scene.primitives.add(linePrimitive);```注意:椭球体表面的填充部分和网格线部分可能会重叠,我们可以调整它们的渲染顺序(先填充后线条)以避免线条被遮挡。另外,我们也可以考虑使用PolylineCollection,但PolylineCollection属于Entity API,通常用于动态更新的线。而Primitive API更底层,性能更好,尤其对于静态网格。总结:我们通过两个Primitive来实现:一个用于椭球体表面填充,一个用于网格线。网格线通过生成多个线几何实例(每个经圈和纬圈一个)并合并到一个Primitive中。注意:椭球体的半径单位是米,但Cesium的坐标系是WGS84,所以如果椭球体很大(比如地球大小),则应该使用Cesium.Ellipsoid.WGS84。但这里我们绘制的是任意椭球体,所以使用自定义的半径。参考Cesium的EllipsoidGeometry文档:https://cesium.com/docs/cesiumjs-ref-doc/EllipsoidGeometry.html以上代码仅为示例,实际应用中可能需要调整参数和优化。引用[1]中提到了几何图形和外观系统,这正是我们使用PrimitiveAPI的基础。希望这能帮助您在Cesium中为primitive绘制的椭球体添加网格线。§§相关问题§§1.如何在Cesium中自定义几何图形(Geometry)?2. CesiumPrimitiveAPI和Entity API有什么区别?3.如何优化Cesium中大量几何图形的性能?</think>### 在 Cesium 中为 Primitive 绘制的椭球体添加网格线 在 Cesium 中为 Primitive 绘制的椭球体添加网格线,需要通过自定义几何图形和外观系统实现。以下是详细步骤: #### 核心思路 1. **分离绘制**:创建两个 Primitive - 第一个 Primitive 绘制椭球体表面(使用 `EllipsoidGeometry`) - 第二个 Primitive 绘制网格线(自定义线几何) 2. **网格线生成**:根据椭球体参数方程计算网格顶点 - 经线:固定经度变化纬度 - 纬线:固定纬度变化经度 3. **坐标转换**:将局部坐标转换为世界坐标 #### 代码实现 ```javascript // 1. 创建椭球体表面 Primitive const ellipsoidSurface = new Cesium.Primitive({ geometryInstances: new Cesium.GeometryInstance({ geometry: new Cesium.EllipsoidGeometry({ radii: new Cesium.Cartesian3(2.0, 3.0, 1.5), // 椭球体三轴半径 vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL }), modelMatrix: Cesium.Matrix4.IDENTITY }), appearance: new Cesium.PerInstanceColorAppearance({ translucent: true, closed: true }) }); // 2. 生成网格线顶点 const generateGridLines = (radii, segments = 32) => { const positions = []; const a = radii.x, b = radii.y, c = radii.z; // 经线(固定经度) for (let lon = 0; lon < segments; lon++) { const theta = lon * 2 * Math.PI / segments; for (let lat = 0; lat <= segments; lat++) { const phi = lat * Math.PI / segments; const x = a * Math.cos(theta) * Math.sin(phi); const y = b * Math.sin(theta) * Math.sin(phi); const z = c * Math.cos(phi); positions.push(new Cesium.Cartesian3(x, y, z)); } } // 纬线(固定纬度) for (let lat = 1; lat < segments; lat++) { const phi = lat * Math.PI / segments; for (let lon = 0; lon <= segments; lon++) { const theta = lon * 2 * Math.PI / segments; const x = a * Math.cos(theta) * Math.sin(phi); const y = b * Math.sin(theta) * Math.sin(phi); const z = c * Math.cos(phi); positions.push(new Cesium.Cartesian3(x, y, z)); } } return positions; }; // 3. 创建网格线 Primitive const gridPositions = generateGridLines(new Cesium.Cartesian3(2.0, 3.0, 1.5)); const gridPrimitive = new Cesium.Primitive({ geometryInstances: new Cesium.GeometryInstance({ geometry: new Cesium.PolylineGeometry({ positions: gridPositions, width: 1.0, vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT }), attributes: { color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLACK) } }), appearance: new Cesium.PolylineColorAppearance() }); // 4. 添加到场景 viewer.scene.primitives.add(ellipsoidSurface); viewer.scene.primitives.add(gridPrimitive); ``` #### 关键参数说明 - **`radii`**:椭球体三轴半径(单位:米) - **`segments`**:网格分段数(值越大线条越平滑) - **`vertexFormat`**:顶点格式,网格线需用 `PolylineColorAppearance.VERTEX_FORMAT` - **`width`**:网格线宽度(像素) #### 效果优化建议 1. **深度测试**:避免网格线被地表遮挡 ```javascript gridPrimitive.appearance.renderState = { depthTest: { enabled: true }, lineWidth: Cesium.ContextLimits.maximumAliasedLineWidth }; ``` 2. **透明度控制**:使网格线和表面融合更自然 ```javascript const surfaceColor = new Cesium.Color(0.3, 0.5, 1.0, 0.7); // 半透明表面 ``` 3. **性能优化**:减少分段数(`segments`)或使用 `GroundPolylinePrimitive` > **引用说明**:此实现基于 CesiumPrimitive API 几何系统,通过自定义线几何生成网格结构[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值