【Cesium 入门】系列是将CesiumJS官网教程文章翻译为中文,同时对文章进行了一定的补充,并且提供Vue3版本的代码GitHub - cesiumjs教程的vue3版本 。希望这个系列可以帮助你更好的学习cesium。
本教程将向您介绍 Primitive API 提供的几何和外观系统。这是一个使用自定义网格、形状、体积和外观扩展 CesiumJS的高级主题。 如果你只是要在Cesium中绘制简单的立体形状,可以直接使用Entity实体类(Entity,下文中实体都是),可参考实体教程,Primitive在下文中称为基元。
下文中特殊名称说明:
- 基元: Primitive, Cesium中图形的低级原始API
- 实体:Entity , 基于Primitive API封装的高级类Entity
- 几何实例: Primitive中的geometryInstances属性
- 外观:图形的外观,也是Primitive中的appearence属性
本教程将讲述以下几点:
- 几何概述
- 几何类型
- 组合几何形状
- 拾取
- 几何实例
- 更新每个实例的属性
- 外观
- 几何和外观的兼容性
几何概述
CesiumJS 可以使用实体创建不同的几何类型,例如多边形和椭球体。例如,将以下内容复制并粘贴到 Hello World Sandcastle 示例中,以在地球上创建一个带有点图案的矩形:
const viewer = new Cesium.Viewer("cesiumContainer");
viewer.entities.add({
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
material: new Cesium.StripeMaterialProperty({
evenColor: Cesium.Color.WHITE,
oddColor: Cesium.Color.BLUE,
repeat: 5,
}),
},
});
在本教程中,我们将深入了解构成它们的几何和外观类型。几何定义了图元的结构,即组成图元的三角形、线或点。外观定义图元的着色,包括其完整的 GLSL 顶点和片段着色器以及渲染状态。
使用几何形状和外观的好处是:
- 性能 - 当绘制大量静态图元(例如美国每个邮政编码的多边形)时,直接使用几何图形可以让我们将它们组合成单个几何图形,以减少 CPU 开销并更好地利用 GPU。组合基元 (Combining primitives) 是在 Web Worker 上完成的,以保持 UI 的响应能力。
- 灵活性 - 基元结合了几何形状和外观。通过将它们解耦,我们可以独立地修改它们。我们可以添加与许多不同外观兼容的新几何形状,反之亦然。
- 低级访问 - 外观提供了近乎真实的渲染访问,而无需担心直接使用渲染器的所有细节.
外观使您可以轻松地:- 编写完整的 GLSL 顶点和片段着色器。
- 使用自定义渲染状态。
也有一些缺点:
- 直接使用几何和外观需要更多的代码和对图形更深入的理解。实体处于适合地图应用程序的抽象级别;几何形状和外观具有更接近传统 3D 引擎的抽象级别。
- 组合几何对于静态数据有效,但对于动态数据不一定有效。
让我们使用几何和外观重写最初的代码示例:
const viewer = new Cesium.Viewer("cesiumContainer");
const scene = viewer.scene;
// original code
//viewer.entities.add({
// rectangle : {
// coordinates: Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
// material: new Cesium.StripeMaterialProperty({
// evenColor: Cesium.Color.WHITE,
// oddColor: Cesium.Color.BLUE,
// repeat: 5
// })
// }
//});
const instance = new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
}),
});
scene.primitives.add(
new Cesium.Primitive({
geometryInstances: instance,
appearance: new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType("Stripe"),
}),
})
);
我们没有使用矩形实体,而是使用了通用Primitive,它结合了几何实例(geometryInstances)和外观(appearance)。目前,我们不会区分 Geometry 和 GeometryInstance,除非实例是几何图形的容器。
为了创建矩形的几何形状,即覆盖矩形区域并适合地球曲率的三角形,我们创建一个 RectangleGeometry。
几何类型
上面代码使用了new Cesium.RectangleGeometry创建了满足地球曲率的矩形,对于几何实例geometryInstances还有其他设定好的几何类,如下表所示:
| Geometry | Outline | Description |
|---|---|---|
BoxGeometry | BoxOutlineGeometry | 盒子 |
CircleGeometry | CircleOutlineGeometry | 圆或挤压圆 |
CorridorGeometry | CorridorOutlineGeometry | 垂直于表面的多段线,宽度以米为单位,可选的拉伸高度 |
CylinderGeometry | CylinderOutlineGeometry | 圆柱体、圆锥体或截锥体 |
EllipseGeometry | EllipseOutlineGeometry | 椭圆或挤压椭圆 |
EllipsoidGeometry | EllipsoidOutlineGeometry | 椭球体 |
RectangleGeometry | RectangleOutlineGeometry | 矩形或拉伸矩形 |
PolygonGeometry | PolygonOutlineGeometry | 具有可选孔或拉伸多边形的多边形 |
PolylineGeometry | SimplePolylineGeometry | 宽度以像素为单位的线段集合 |
PolylineVolumeGeometry | PolylineVolumeOutlineGeometry | 沿多段线挤压的 2D 形状 |
SphereGeometry | SphereOutlineGeometry | 球体 |
WallGeometry | WallOutlineGeometry | 垂直于地球的墙 |
组合几何形状
当我们使用一个基元绘制多个静态几何图形时,我们会看到性能优势。例如,在一个图元中绘制两个矩形。
const viewer = new Cesium.Viewer("cesiumContainer");
const scene = viewer.scene;
const instance = new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
}),
});
const anotherInstance = new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(-85.0, 20.0, -75.0, 30.0),
vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
}),
});
scene.primitives.add(
new Cesium.Primitive({
geometryInstances: [instance, anotherInstance],
appearance: new Cesium.EllipsoidSurfaceAppearance({
material: Cesium.Material.fromType("Stripe"),
}),
})
);
我们创建了两个不同位置的矩形实例,然后将这两个实例提供给基元。这会以相同的外观绘制两个实例。
某些外观允许每个实例提供独特的属性。例如,我们可以使用 PerInstanceColorAppearance 用实例自定义颜色为每个实例着色。
const viewer = new Cesium.Viewer("cesiumContainer");
const scene = viewer.scene;
const instance = new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
}),
attributes: {
color: new Cesium.ColorGeometryInstanceAttribute(0.0, 0.0, 1.0, 0.8),
},
});
const anotherInstance = new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(-85.0, 20.0, -75.0, 30.0),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
}),
attributes: {
color: new Cesium.ColorGeometryInstanceAttribute(1.0, 0.0, 0.0, 0.8),
},
});
scene.primitives.add(
new Cesium.Primitive({
geometryInstances: [instance, anotherInstance],
appearance: new Cesium.PerInstanceColorAppearance(),
})
);
每个实例都有一个颜色属性。该基元是使用 PerInstanceColorAppearance 构建的,它使用每个实例的颜色属性来确定外观。
组合几何图形允许 CesiumJS 有效地绘制大量几何图形。以下示例绘制 2,592 个独特颜色的矩形。
const viewer = new Cesium.Viewer("cesiumContainer");
const scene = viewer.scene;
const instances = [];
for (let lon = -180.0; lon < 180.0; lon += 5.0) {
for (let lat = -85.0; lat < 85.0; lat += 5.0) {
instances.push(
new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(
lon,
lat,
lon + 5.0,
lat + 5.0
),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
Cesium.Color.fromRandom({ alpha: 0.5 })
),
},
})
);
}
}
scene.primitives.add(
new Cesium.Primitive({
geometryInstances: instances,
appearance: new Cesium.PerInstanceColorAppearance(),
})
);
拾取
几何实例合并后可以独立访问。为几何实例分配一个ID,并使用它来确定是否使用 Scene.pick 选取了该实例。 以下示例创建一个具有 ID 的实例,并在单击该实例时向控制台写入一条消息。
const viewer = new Cesium.Viewer("cesiumContainer");
const scene = viewer.scene;
const instance = new Cesium.GeometryInstance({
geometry: new Cesium.RectangleGeometry({
rectangle: Cesium.Rectangle.fromDegrees(-100.0, 30.0, -90.0, 40.0),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
}),
id: "my rectangle",
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED),
},
});
scene.primitives.add(
new Cesium.Primitive({
geometryInstances: instance,
appearance: new Cesium.PerInstanceColorAppearance(),
})
);
const handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction((movement) => {
const pick = scene.pick(movement.position);
if (Cesium.defined(pick) && pick.id === "my rectangle") {
console.log("Mouse clicked rectangle.");
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
使用 id 可以避免在构造基元后在内存中保留对完整实例(包括几何图形)的引用。
几何实例
实例可用于在场景的不同部分中定位、缩放和旋转相同的几何体。因为多个实例可以引用相同的几何图形,并且每个实例可以有不同的 modelMatrix。这使得我们只需计算一次几何图形即可多次重复使用。
以下示例创建一个 EllipsoidGeometry 和两个实例。每个实例都引用相同的椭球体几何形状,但使用不同的 modelMatrix 放置它,从而导致一个椭球体位于另一个椭球体之上。
const viewer = new Cesium.Viewer("cesiumContainer");
const scene = viewer.scene;
const ellipsoidGeometry = new Cesium.EllipsoidGeometry({
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
radii: new Cesium.Cartesian3(300000.0, 200000.0, 150000.0),
});
const cyanEllipsoidInstance = new Cesium.GeometryInstance({
geometry: ellipsoidGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.eastNorthUpToFixedFrame(
Cesium.Cartesian3.fromDegrees(-100.0, 40.0)
),
new Cesium.Cartesian3(0.0, 0.0, 150000.0),
new Cesium.Matrix4()
),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.CYAN),
},
});
const orangeEllipsoidInstance = new Cesium.GeometryInstance({
geometry: ellipsoidGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.eastNorthUpToFixedFrame(
Cesium.Cartesian3.fromDegrees(-100.0, 40.0)
),
new Cesium.Cartesian3(0.0, 0.0, 450000.0),
new Cesium.Matrix4()
),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.ORANGE),
},
});
scene.primitives.add(
new Cesium.Primitive({
geometryInstances: [cyanEllipsoidInstance, orangeEllipsoidInstance],
appearance: new Cesium.PerInstanceColorAppearance({
translucent: false,
closed: true,
}),
})
);
更新每个实例的属性
将几何图形添加到图元后更新其每个实例属性以更改可视化效果。每个实例的属性包括:
- Color :确定实例颜色的 ColorGeometryInstanceAttribute。该图元必须具有 PerInstanceColorAppearance。
- Show :确定实例可见性的布尔值。适用于任何实例。
此示例显示如何更改几何实例的颜色:
const viewer = new Cesium.Viewer("cesiumContainer");
const scene = viewer.scene;
const circleInstance = new Cesium.GeometryInstance({
geometry: new Cesium.CircleGeometry({
center: Cesium.Cartesian3.fromDegrees(-95.0, 43.0),
radius: 250000.0,
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(
new Cesium.Color(1.0, 0.0, 0.0, 0.5)
),
},
id: "circle",
});
const primitive = new Cesium.Primitive({
geometryInstances: circleInstance,
appearance: new Cesium.PerInstanceColorAppearance({
translucent: false,
closed: true,
}),
});
scene.primitives.add(primitive);
setInterval(() => {
const attributes = primitive.getGeometryInstanceAttributes("circle");
attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(
Cesium.Color.fromRandom({ alpha: 1.0 })
);
}, 2000);
可以使用primitive.getGeometryInstanceAttributes 从图元中检索几何实例的属性。可以直接更改属性的属性。
外观
几何定义结构。图元的另一个关键属性外观定义了图元的阴影,即各个像素的着色方式。一个图元可以有许多几何实例,但它只能有一种外观。根据外观的类型,外观将具有定义大部分阴影的材质(bulk of the shading)。
| Appearance | Description |
|---|---|
MaterialAppearance | 适用于所有几何类型并支持描述阴影的材质的外观。 |
EllipsoidSurfaceAppearance | MaterialAppearance 的一个版本,假设几何体与地球表面平行,如多边形,并使用此假设通过程序计算许多顶点属性来节省内存。 |
PerInstanceColorAppearance | 使用每个实例的颜色来为每个实例着色。 |
PolylineMaterialAppearance | 支持对折线进行着色的材质。 |
PolylineColorAppearance | 使用每顶点或每段着色来为折线着色。 |
外观定义了绘制图元时在 GPU 上执行的完整 GLSL 顶点和片段着色器。外观还定义了完整的渲染状态,它控制绘制图元时 GPU 的状态。我们可以直接定义渲染状态或使用更高级别的属性,例如关闭和半透明,外观将转换为渲染状态。例如:
// Perhaps for an opaque box that the viewer will not enter.
// - Backface culled and depth tested. No blending.
const appearance = new Cesium.PerInstanceColorAppearance({
translucent: false,
closed: true,
});
// This appearance is the same as above
const anotherAppearance = new Cesium.PerInstanceColorAppearance({
renderState: {
depthTest: {
enabled: true,
},
cull: {
enabled: true,
face: Cesium.CullFace.BACK,
},
},
});
创建外观后,我们无法更改其 renderState 属性,但我们可以更改其材质。我们还可以更改图元的外观属性。 大多数外观还具有 flat 和faceForward 属性,它们间接控制 GLSL 着色器。
- flat - 平面阴影。不考虑照明。
- faceForward - 照明时,翻转法线,使其始终面向观察者。避免背面出现黑色区域,例如墙的内部。
几何和外观兼容性
并非所有外观都适用于所有几何形状。例如,EllipsoidSurfaceAppearance 不适用于 WallGeometry,因为墙壁不在地球表面。
为了使外观与几何体兼容,它们必须具有匹配的顶点格式,这意味着几何体必须具有外观期望作为输入的数据。创建几何图形时可以提供 VertexFormat。
几何图形的 vertexFormat 决定它是否可以与另一个几何图形组合。两个几何图形不必是相同的类型,但它们需要匹配的顶点格式。
为了方便起见,外观具有 vertexFormat 属性或 VERTEX_FORMAT 静态常量,可以作为几何体的选项传递。
const geometry = new Cesium.RectangleGeometry({
vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
// ...
});
const geometry2 = new Cesium.RectangleGeometry({
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
// ...
});
const appearance = new Cesium.MaterialAppearance(/* ... */);
const geometry3 = new Cesium.RectangleGeometry({
vertexFormat: appearance.vertexFormat,
// ...
});
到这里本教程就结束啦
源码可从_GitHub - cesiumjs教程的vue3版本_下载
如果文章对你有帮助,希望可以收获你的点赞收藏加关注,我会持续带来更多有价值的内容!