mcpField
C
cesiumjs-primitives

cesiumjs-primitives

github.com/cesiumgs开发193237 次安装6 周前更新
代码仓库

关于 cesiumjs-primitives

面向 CesiumJS 开发者的领域技能,说明低层 Primitive 几何、外观与批处理的使用方式。还涵盖缓冲集合和 GeoJSON 原语等面向大量矢量数据的路径。

主要功能

  1. 01Primitive 几何与外观配置
  2. 02批处理多个几何实例
  3. 03更新实例属性与可见性
  4. 04缓冲原语集合
  5. 05GeoJSON 原语加载

适用场景

  1. 01构建高性能静态几何场景
  2. 02批量渲染圆柱等体积图形
  3. 03加载大型静态 GeoJSON 数据集

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/cesiumgs/cesiumjs-skills --skill cesiumjs-primitives

安装后在 Claude Code 中输入/cesiumjs-primitives确认已加载。

详细介绍

CesiumJS 图元与几何体

适用范围: CesiumJS v1.144+(ES 模块导入,使用 ?? 替代 defaultValue)

架构

Primitive API 是 Entity API 底层的低级渲染层,以牺牲便捷性换取性能。

核心公式: Primitive = GeometryInstance[] + Appearance

  • GeometryInstance——在世界空间中定位 Geometry,并设置每个实例的属性(颜色、显示状态)。
  • Geometry——描述形状的顶点数据(多边形、立方体、椭球体等)。
  • Appearance——用于着色几何体的 GLSL 着色器、渲染状态,以及可选的 Material。

Primitive 首次渲染后不可变——几何体无法更改,但可通过 primitive.getGeometryInstanceAttributes(id) 更新每个实例的属性。

Primitive

import {
  Viewer, Primitive, GeometryInstance, EllipseGeometry,
  EllipsoidSurfaceAppearance, Material, Cartesian3, Math as CesiumMath,
} from "cesium";

const viewer = new Viewer("cesiumContainer");
const scene = viewer.scene;

const primitive = scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new EllipseGeometry({
      center: Cartesian3.fromDegrees(-100.0, 40.0),
      semiMinorAxis: 250000.0,
      semiMajorAxis: 400000.0,
      rotation: CesiumMath.PI_OVER_FOUR,
      vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT, // must match appearance
    }),
    id: "myEllipse", // returned by Scene.pick()
  }),
  appearance: new EllipsoidSurfaceAppearance({ material: Material.fromType("Stripe") }),
}));
主要选项
选项默认值用途
geometryInstances--单个实例或实例数组
appearance--着色(Appearance 子类)
showtrue切换可见性
modelMatrixMatrix4.IDENTITY变换所有实例
asynchronoustrue在 Web Worker 上构建几何体
releaseGeometryInstancestrueGPU 上传后释放几何体
allowPickingtrue设为 false 可节省 GPU 内存
shadowsShadowMode.DISABLED投射/接收阴影

批处理多个实例

同一个 Primitive 中的所有实例共用一次绘制调用。

import {
  Primitive, GeometryInstance, RectangleGeometry, EllipseGeometry,
  PerInstanceColorAppearance, ColorGeometryInstanceAttribute,
  Cartesian3, Rectangle, Color,
} from "cesium";

scene.primitives.add(new Primitive({
  geometryInstances: [
    new GeometryInstance({
      geometry: new RectangleGeometry({
        rectangle: Rectangle.fromDegrees(-140, 30, -100, 40),
        vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
      }),
      id: "rect",
      attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.RED.withAlpha(0.5)) },
    }),
    new GeometryInstance({
      geometry: new EllipseGeometry({
        center: Cartesian3.fromDegrees(-80, 35),
        semiMinorAxis: 200000.0,
        semiMajorAxis: 300000.0,
        vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
      }),
      id: "ellipse",
      attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.BLUE.withAlpha(0.5)) },
    }),
  ],
  appearance: new PerInstanceColorAppearance(),
}));
批处理体积几何体(CylinderGeometry 网格)

体积几何体(Cylinder、Box、Ellipsoid)必须通过每个 GeometryInstance 的 modelMatrix 来定位。使用 Matrix4.multiply 将世界空间锚点与局部偏移组合,然后将所有实例批处理到一个 Primitive 中。

import {
  Primitive, GeometryInstance, CylinderGeometry,
  PerInstanceColorAppearance, ColorGeometryInstanceAttribute,
  Cartesian3, Matrix4, Transforms, Color, Math as CesiumMath,
} from "cesium";

const center = Cartesian3.fromDegrees(-73.9857, 40.7580);
const anchorFrame = Transforms.eastNorthUpToFixedFrame(center, undefined, new Matrix4());
const instances = [];
const GRID = 10;
const SPACING = 50; // metres

for (let row = 0; row < GRID; row++) {
  for (let col = 0; col < GRID; col++) {
    const xOffset = (col - GRID / 2) * SPACING;
    const yOffset = (row - GRID / 2) * SPACING;
    // Combine anchor ENU frame with a local XYZ offset
    const modelMatrix = Matrix4.multiply(
      anchorFrame,
      Matrix4.fromTranslation(new Cartesian3(xOffset, yOffset, 100), new Matrix4()),
      new Matrix4(),
    );
    instances.push(new GeometryInstance({
      geometry: new CylinderGeometry({
        length: 200,
        topRadius: 8,
        bottomRadius: 8,
        vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
      }),
      modelMatrix,
      attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.fromRandom({ alpha: 1.0 })) },
    }));
  }
}

scene.primitives.add(new Primitive({
  geometryInstances: instances,
  appearance: new PerInstanceColorAppearance({ flat: true }),
}));

// Frame the grid so the full batch is visible -- a shallow pitch hides cylinders
// behind the foreground; a near-nadir pitch flattens them. Aim for ~-45° (-PI/4)
// at a range that covers the grid footprint (GRID * SPACING) with margin.
const range = GRID * SPACING * 3; // ~1500 m for a 10x10x50m grid
viewer.camera.lookAt(
  center,
  new Cartesian3(0, -range * 0.7, range * 0.7), // offset south + up for -45° pitch
);

关键模式:

  • Matrix4.multiply(anchorFrame, Matrix4.fromTranslation(offset, result), result)——将地理锚点处的 ENU 坐标系与局部东/北/上平移组合起来。
  • Color.fromRandom({ alpha: 1.0 }) 会生成完全不透明的随机颜色,适合创建彩虹色批次。
  • 视角很重要: 对于垂直体积构成的网格,建议使用约 3 * gridFootprint 的距离,并将俯仰角设为 ~-45°(-CesiumMath.PI_OVER_FOUR,约 -0.785 rad)。俯仰角浅于约 -0.6 rad 时,网格可能移出屏幕或被建筑物遮挡;从正下方俯视会使圆柱体变成小点,失去批处理“场”的效果。-0.61 rad 的俯仰角仍然足够浅,可能导致网格完全移出画面。
  • 确认可见性: primitive.ready 后,如果场景只要求“批次可见”,优先使用 viewer.camera.flyToBoundingSphere(primitive._boundingSpheres[0], { duration: 0 })(或 viewer.flyTo(primitive)),而不是手动调整偏移量。

更新每个实例的属性

import { ColorGeometryInstanceAttribute, ShowGeometryInstanceAttribute } from "cesium";

// Wait for async geometry compilation
const removeListener = scene.postRender.addEventListener(() => {
  if (!primitive.ready) return;
  const attrs = primitive.getGeometryInstanceAttributes("rect");
  attrs.color = ColorGeometryInstanceAttribute.toValue(Color.YELLOW);
  attrs.show = ShowGeometryInstanceAttribute.toValue(true);
  removeListener();
});

PrimitiveCollection

可嵌套的容器——scene.primitives 本身就是一个 PrimitiveCollection。

import { PrimitiveCollection, BillboardCollection, LabelCollection } from "cesium";

const group = new PrimitiveCollection();
group.add(new BillboardCollection());
group.add(new LabelCollection());
scene.primitives.add(group);
group.show = false; // toggle all children

选择矢量数据路径

需求使用
Entity 生命周期、聚类、每个 Entity 的样式设置、随时间变化的值cesiumjs-entities 中的 GeoJsonDataSource
单个大型 GeoJSON 对象,且需要较低开销和 Primitive 级性能此技能中的 GeoJsonPrimitive
瓦片矢量数据、3D Tiles LOD、元数据样式设置、要素拾取cesiumjs-3d-tiles 中的 MVTDataProvider
完全手动管理的高吞吐量点/折线/多边形缓冲区BufferPointCollection、BufferPolylineCollection、BufferPolygonCollection

缓冲区基本图元集合(实验性,1.140+)

对于实体/数据源开销过高的大型矢量数据集,请使用 BufferPointCollection、BufferPolylineCollection 和 BufferPolygonCollection。这些 API 在 1.140(#13212)中引入,并在 1.142 中进一步完善;它们属于实验性功能,使用轻量对象:添加或遍历数千个项目时,应重复使用同一个 BufferPoint、BufferPolyline 或 BufferPolygon。

import {
  BlendOption,
  BoundingSphere,
  BufferPoint,
  BufferPointCollection,
  BufferPointMaterial,
  Cartesian3,
  Color,
} from "cesium";

const positions = [
  Cartesian3.fromDegrees(-75.16, 39.95),
  Cartesian3.fromDegrees(-73.98, 40.75),
];

const points = scene.primitives.add(new BufferPointCollection({
  primitiveCountMax: positions.length,
  allowPicking: true,
  blendOption: BlendOption.TRANSLUCENT,
  boundingVolume: BoundingSphere.fromPoints(positions), // world space in 1.142+
}));

const point = new BufferPoint();
const material = new BufferPointMaterial({
  color: Color.CYAN.withAlpha(0.65),
  outlineColor: Color.WHITE.withAlpha(0.9),
  outlineWidth: 2,
  size: 10,
});

positions.forEach((position, featureId) => {
  points.add({
    position,
    featureId,
    material,
  }, point);
});

const picked = scene.pick(windowPosition);
if (picked?.collection === points) {
  console.log(picked.index, picked.primitive.featureId);
}

破坏性变更(1.141,#13448): BufferPrimitiveCollection.modelMatrix、boundingVolume 和 boundingVolumeWC 现在为只读属性——你可以原地修改对象,但重新赋值属性(collection.modelMatrix = ...)会抛出异常。请更新现有矩阵/包围体,而不是替换为新的对象。

1.142 说明:

  • boundingVolume 现在采用世界坐标,而非局部/模型坐标。如果手动提供该值,需自行应用集合的 modelMatrix 变换。
  • 提供 boundingVolume 后会跳过自动重新计算;这有助于处理大型动画集合,但你需要自行确保包围体保持有效。
  • 三种缓冲区集合均支持 blendOption,并可启用来自 BufferPrimitiveMaterial#color 的 alpha;BufferPointCollection 也会遵循 outlineColor.alpha。
  • 仅当所有材质都完全不透明时,才使用 BlendOption.OPAQUE;alpha 会变化时,请使用 TRANSLUCENT 或混合模式。

在 1.143 中,当 outlineWidth 为 0 时,BufferPointCollection 不再将 outlineColor 泄漏到填充区域。将宽度设为 0 即可禁用轮廓;请移除会增加批处理复杂度的透明轮廓变通方案。

GeoJsonPrimitive(实验性,1.142+)

GeoJsonPrimitive 可将 GeoJSON 直接加载到缓冲区基本图元集合中,绕过 GeoJsonDataSource 和实体层。对于大型静态矢量数据集或批量更新的矢量数据集,优先使用它。如果需要实体层的便利功能、时间动态属性、聚类或 DataSource 生命周期集成,请继续使用 GeoJsonDataSource。

import { GeoJsonPrimitive } from "cesium";

const counties = await GeoJsonPrimitive.fromUrl("/data/counties.geojson", {
  allowPicking: true,
});
scene.primitives.add(counties);

console.log(counties.featureCount);
console.log(counties.points);    // BufferPointCollection | undefined
console.log(counties.polylines); // BufferPolylineCollection | undefined
console.log(counties.polygons);  // BufferPolygonCollection | undefined

// Picking returns the GeoJsonPrimitive pick object, including source properties.
const picked = scene.pick(windowPosition);
if (picked?.parentPrimitive === counties) {
  const featureId = picked.primitive.featureId;
  console.log(counties.getId(featureId));
  console.log(counties.getProperties(featureId));
}

如果 GeoJSON 已在内存中,可使用 GeoJsonPrimitive.fromGeoJson(parsedObject)。源要素 ID 可通过 ids/getId() 获取,源属性可通过 properties/getProperties() 获取。

内置几何类型(31)

所有几何体都需要形状参数,以及与外观匹配的 vertexFormat。大多数几何体都有对应的 *OutlineGeometry。轮廓需要单独的 Primitive。

填充 + 轮廓模式
import {
  Primitive, GeometryInstance, PolygonGeometry, PolygonOutlineGeometry,
  PolygonHierarchy, PerInstanceColorAppearance, ColorGeometryInstanceAttribute,
  Cartesian3, Color,
} from "cesium";

const positions = Cartesian3.fromDegreesArray([-115, 37, -115, 32, -107, 33, -102, 35]);

// Fill primitive
scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new PolygonGeometry({
      polygonHierarchy: new PolygonHierarchy(positions),
      vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
    }),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.CYAN.withAlpha(0.5)) },
  }),
  appearance: new PerInstanceColorAppearance(),
}));

// Outline primitive (separate draw call)
scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new PolygonOutlineGeometry({ polygonHierarchy: new PolygonHierarchy(positions) }),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.WHITE) },
  }),
  appearance: new PerInstanceColorAppearance({ flat: true }),
}));
几何体目录

除特别注明外,每个 XxxGeometry 都有对应的 XxxOutlineGeometry。

表面(适用于 GroundPrimitive):CircleGeometry、CorridorGeometry、EllipseGeometry、PolygonGeometry、RectangleGeometry。

体积(需要 modelMatrix):BoxGeometry(fromDimensions())、CylinderGeometry(当 topRadius != bottomRadius 时为圆锥)、EllipsoidGeometry、SphereGeometry、FrustumGeometry、PlaneGeometry。

路径:CorridorGeometry(缓冲路径)、PolylineVolumeGeometry(沿路径挤出的 2D 形状)、WallGeometry(垂直帘幕)。

多边形:PolygonGeometry(通过 PolygonHierarchy 创建孔洞)、CoplanarPolygonGeometry(非地球表面)。

线(无轮廓):PolylineGeometry(像素宽度)、SimplePolylineGeometry(1px)、GroundPolylineGeometry(仅限 GroundPolylinePrimitive)。

在离地位置放置几何体

Box、Ellipsoid、Cylinder 和 Frustum 需要在 GeometryInstance 上设置 modelMatrix。

import { GeometryInstance, BoxGeometry, PerInstanceColorAppearance,
  ColorGeometryInstanceAttribute, Cartesian3, Matrix4, Transforms, Color } from "cesium";

const modelMatrix = Matrix4.multiplyByTranslation(
  Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(-105, 40)),
  new Cartesian3(0, 0, 250000), new Matrix4(),
);
new GeometryInstance({
  geometry: BoxGeometry.fromDimensions({
    dimensions: new Cartesian3(400000, 300000, 500000),
    vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
  }),
  modelMatrix,
  id: "floatingBox",
  attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.CORAL) },
});

外观(7 种)

外观用例是否需要材质?
PerInstanceColorAppearance每个实例使用单独颜色否
MaterialAppearance任意几何体 + 材质是
EllipsoidSurfaceAppearance曲面几何体 + 材质(属性较少)是
PolylineColorAppearance每条实例化折线使用单独颜色否
PolylineMaterialAppearance使用材质的折线是
DebugAppearance可视化顶点属性否
Appearance基类 / 自定义着色器可选

几何体的 vertexFormat 必须与外观匹配。请使用外观的静态 VERTEX_FORMAT。对于不启用光照的 PerInstanceColorAppearance,请使用 FLAT_VERTEX_FORMAT。

MaterialAppearance 示例
import { Primitive, GeometryInstance, WallGeometry, MaterialAppearance, Material, Cartesian3 } from "cesium";

scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new WallGeometry({
      positions: Cartesian3.fromDegreesArrayHeights([-115, 44, 200000, -110, 44, 200000, -105, 44, 200000]),
      vertexFormat: MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat,
    }),
  }),
  appearance: new MaterialAppearance({
    material: Material.fromType("Checkerboard"),
    faceForward: true, // shade both sides
  }),
}));

GroundPrimitive

将几何体贴合到地形 / 3D Tiles 上。支持:CircleGeometry、CorridorGeometry、EllipseGeometry、PolygonGeometry、RectangleGeometry。将其添加到 scene.primitives(而不是 scene.groundPrimitives——两者都可用,但使用不带 Ion 地形的公共底图时,通常使用 scene.primitives)。

import { GroundPrimitive, GeometryInstance, PolygonGeometry, PolygonHierarchy,
  PerInstanceColorAppearance, ColorGeometryInstanceAttribute, ClassificationType,
  Cartesian3, Color } from "cesium";

scene.primitives.add(new GroundPrimitive({
  geometryInstances: new GeometryInstance({
    geometry: new PolygonGeometry({
      polygonHierarchy: new PolygonHierarchy(
        Cartesian3.fromDegreesArray([-112, 36, -112, 36.1, -111.9, 36.1]),
      ),
    }),
    id: "groundPolygon",
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.RED.withAlpha(0.5)) },
  }),
  appearance: new PerInstanceColorAppearance({ flat: true, translucent: true }),
  classificationType: ClassificationType.TERRAIN, // TERRAIN, CESIUM_3D_TILE, or BOTH
}));

GroundPolylinePrimitive

将折线贴合到地形上。将其添加到 scene.primitives(而不是 scene.groundPrimitives)。

import { GroundPolylinePrimitive, GeometryInstance, GroundPolylineGeometry,
  PolylineColorAppearance, ColorGeometryInstanceAttribute, Cartesian3, Color } from "cesium";

scene.primitives.add(new GroundPolylinePrimitive({
  geometryInstances: new GeometryInstance({
    geometry: new GroundPolylineGeometry({
      positions: Cartesian3.fromDegreesArray([-112.13, 36.05, -112.09, 36.10, -112.13, 36.17]),
      width: 4.0,
      loop: true,
    }),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.LIME.withAlpha(0.7)) },
  }),
  appearance: new PolylineColorAppearance(),
}));

ClassificationPrimitive

高亮用于对地形或 3D Tiles 进行分类的体积。有效类型:BoxGeometry、CylinderGeometry、EllipsoidGeometry、PolylineVolumeGeometry、SphereGeometry,以及拉伸曲面几何体。

import { ClassificationPrimitive, GeometryInstance, BoxGeometry, PerInstanceColorAppearance,
  ColorGeometryInstanceAttribute, ClassificationType, Cartesian3, Transforms, Color } from "cesium";

scene.primitives.add(new ClassificationPrimitive({
  geometryInstances: new GeometryInstance({
    geometry: BoxGeometry.fromDimensions({
      dimensions: new Cartesian3(100, 100, 50),
      vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
    }),
    modelMatrix: Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(-75.59, 40.04, 25)),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.YELLOW.withAlpha(0.5)) },
  }),
  classificationType: ClassificationType.BOTH,
}));

BillboardCollection

与视口对齐的图像,GPU 效率高——大规模使用时的性能远胜实体。

破坏性变更(1.140,#13253): BillboardCollection 和 LabelCollection 现在需要 WebGL 2,或支持 ANGLE_instanced_arrays 且 MAX_VERTEX_TEXTURE_IMAGE_UNITS > 0 的 WebGL 1。在不支持的设备上,它们将不再 渲染——如果仍需支持旧版 WebGL 1 硬件,请通过 scene.context.webgl2 判断(或检测扩展)。

基本用法

兼容性修复(1.143): 应用替换全局 Promise 实现后,公告牌图像加载不再崩溃。请使用带 URL、已加载图像或画布的公共 image 属性;对于 Resource 或回调,请使用 setImage。不要根据原生 Promise 的身份进行分支,也不要为此问题保留兼容性垫片。

import { BillboardCollection, Cartesian3, Color, NearFarScalar,
  HeightReference, HorizontalOrigin, VerticalOrigin } from "cesium";

const billboards = scene.primitives.add(new BillboardCollection({ scene }));
const b = billboards.add({
  position: Cartesian3.fromDegrees(-75.59, 40.04),
  image: "marker.png",
  horizontalOrigin: HorizontalOrigin.CENTER,
  verticalOrigin: VerticalOrigin.BOTTOM,
  heightReference: HeightReference.CLAMP_TO_GROUND,
  scaleByDistance: new NearFarScalar(1000, 1.5, 1e7, 0.3),
});
b.position = Cartesian3.fromDegrees(-75.60, 40.05); // update dynamically
billboards.remove(b);
PinBuilder——程序化图钉图像

PinBuilder 可在运行时生成基于画布的图钉图标,无需外部图像文件。使用 fromColor 创建纯色图钉,或使用 fromText 创建带标签的图钉。将返回的画布作为公告牌的 image 传入。

如果场景指定了城市 / 项目的有序列表和按索引着色的方案,请按给定顺序遍历源数组,并直接将循环索引用作 HSL 色相索引。重新排列源列表(例如按纬度排序)会导致城市与颜色的对应关系发生变化,无法通过视觉检查。

import { BillboardCollection, PinBuilder, Cartesian3, Color, VerticalOrigin } from "cesium";

const pinBuilder = new PinBuilder();
const cities = [
  { name: "Boston",       lng: -71.0589, lat: 42.3601 },
  { name: "New York",     lng: -74.0060, lat: 40.7128 },
  { name: "Philadelphia", lng: -75.1652, lat: 39.9526 },
  { name: "Washington DC",lng: -77.0369, lat: 38.9072 },
  { name: "Miami",        lng: -80.1918, lat: 25.7617 },
];

const billboards = scene.primitives.add(new BillboardCollection({ scene }));

cities.forEach((city, index) => {
  billboards.add({
    position: Cartesian3.fromDegrees(city.lng, city.lat),
    // Color.fromHsl(hue 0-1, saturation, lightness) produces evenly-spaced hues
    image: pinBuilder.fromColor(Color.fromHsl(index / cities.length, 0.8, 0.5), 48),
    verticalOrigin: VerticalOrigin.BOTTOM,
  });
});

// Text label pin: pinBuilder.fromText("A", Color.ROYALBLUE, 48)
// fromColor / fromText return a canvas -- pass directly as image

Color.fromHsl(hue, saturation, lightness)——通过改变 hue(0–1,循环覆盖完整色环)生成光谱中的颜色。适用于为 N 个项目生成彩虹色:Color.fromHsl(i / n, 0.8, 0.5)。Color.fromRandom({ alpha })——随机生成色相 / 饱和度 / 亮度,并固定透明度。

LabelCollection

import { LabelCollection, Cartesian3, Cartesian2, Color, LabelStyle, VerticalOrigin } from "cesium";

const labels = scene.primitives.add(new LabelCollection({ scene }));
labels.add({
  position: Cartesian3.fromDegrees(-75.59, 40.04, 300),
  text: "Philadelphia",
  font: "16px sans-serif",
  fillColor: Color.WHITE,
  outlineColor: Color.BLACK,
  outlineWidth: 2,
  style: LabelStyle.FILL_AND_OUTLINE,
  verticalOrigin: VerticalOrigin.BOTTOM,
  pixelOffset: new Cartesian2(0, -10),
});

PointPrimitiveCollection

import { PointPrimitiveCollection, Cartesian3, Color, NearFarScalar } from "cesium";

const points = scene.primitives.add(new PointPrimitiveCollection());
points.add({
  position: Cartesian3.fromDegrees(-75.59, 40.04),
  pixelSize: 10,
  color: Color.YELLOW,
  outlineColor: Color.BLACK,
  outlineWidth: 2,
  scaleByDistance: new NearFarScalar(1000, 1.0, 1e7, 0.1),
});

CloudCollection 和 PolylineCollection

import { CloudCollection, PolylineCollection, Cartesian3, Cartesian2, Color, Material } from "cesium";

// Procedural cumulus clouds
const clouds = scene.primitives.add(new CloudCollection());
clouds.add({
  position: Cartesian3.fromDegrees(-75.59, 40.04, 1500),
  scale: new Cartesian2(40, 12),
  maximumSize: new Cartesian3(40, 12, 15),
  slice: 0.36,
});

// Low-level polyline collection
const polylines = scene.primitives.add(new PolylineCollection());
polylines.add({
  positions: Cartesian3.fromDegreesArray([-75, 40, -70, 42, -65, 38]),
  width: 3.0,
  material: Material.fromType("Color", { color: Color.AQUA }),
});

通过 Primitive 绘制折线

import { Primitive, GeometryInstance, PolylineGeometry, PolylineColorAppearance,
  ColorGeometryInstanceAttribute, Cartesian3, Color, ArcType } from "cesium";

scene.primitives.add(new Primitive({
  geometryInstances: new GeometryInstance({
    geometry: new PolylineGeometry({
      positions: Cartesian3.fromDegreesArray([0, 0, 5, 0]),
      width: 10.0,
      vertexFormat: PolylineColorAppearance.VERTEX_FORMAT,
      arcType: ArcType.GEODESIC, // GEODESIC, RHUMB, or NONE
    }),
    attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.WHITE) },
  }),
  appearance: new PolylineColorAppearance({ translucent: false }),
}));

枚举

枚举值用于
ArcTypeGEODESIC、RHUMB、NONEPolylineGeometry、PolygonGeometry
CornerTypeROUNDED、MITERED、BEVELEDCorridorGeometry、PolylineVolumeGeometry
ClassificationTypeTERRAIN、CESIUM_3D_TILE、BOTHGroundPrimitive、ClassificationPrimitive
PrimitiveTypePOINTS、LINES、TRIANGLES 等底层 Geometry
CloudTypeCUMULUSCloudCollection

基元场景的相机取景

程序化检查(基元数量、相机位置)可能通过,但视觉输出却完全没有呈现出几何体。视觉评估中反复出现的一些经验法则:

  • 体积几何网格(圆柱体/立方体): 使用约 -PI/4(约 -0.785 弧度,-45°)的俯仰角,距离约为 3 * gridFootprint。俯仰角浅于约 -0.6 弧度时,网格可能落在相机后方或画面之外;俯仰角接近 -PI/2 时,垂直范围会被压扁,整批对象看起来像点。
  • 铺设在地形上的表面多边形(GroundPrimitive): 俯仰角较陡(约 -50°)且距离较远时,多边形会缩成画面底部边缘的一条细带。缩短距离或采用更平缓的俯仰角,让多边形位于画面中央。
  • 大陆级折线(66 号公路、跨洲路线): 从质心上方约 5–6 Mm 处以接近天底的俯视角观察,可让整条路径保持可见。
  • 沿海岸/路线排列的标记链: 将相机对准标记链的中点,并设置足以在四周留出余量、涵盖标记链包围盒的距离。

如有疑问,在 primitive.ready 后调用 viewer.flyTo(primitive) 或 viewer.camera.flyToBoundingSphere(primitive._boundingSpheres[0]),即可自动为整批对象取景。

性能提示

  1. 尽可能批处理。 将数千个 GeometryInstances 合并到一个 Primitive 中,以实现一次绘制调用。
  2. 每个实例只需不同颜色时,使用 PerInstanceColorAppearance。
  3. 无需光照时,在 PerInstanceColorAppearance 上设置 flat: true;这会使用 FLAT_VERTEX_FORMAT。
  4. 对于永远不会被选取的 Primitive,设置 allowPicking: false 以节省 GPU 内存。
  5. 保持 asynchronous: true(默认值)。 访问实例属性前,检查 primitive.ready。
  6. Billboard、Label 和 PointPrimitive 尽量使用较少的大型集合。 按更新频率分组。
  7. 所有项目均为不透明时,在 BillboardCollection/PointPrimitiveCollection 上使用 BlendOption.OPAQUE(最高可提升 2 倍)。
  8. 对于大型矢量数据,如果可以接受轻量级更新,请使用缓冲区基元集合。
  9. 对于大型动画集合,预先计算缓冲区集合的包围体积;但请记住,在 1.142+ 中它们采用世界坐标。
  10. 地形贴合使用 GroundPrimitive,而不是实体的 heightReference。
  11. 将填充和轮廓拆分为两个 Primitive——它们无法共用一次绘制调用。
  12. 让 vertexFormat 与外观完全匹配,以跳过未使用的顶点属性计算。
  13. 表面几何使用 EllipsoidSurfaceAppearance,而不是 MaterialAppearance——前者所需的顶点属性更少。

另请参阅

  • cesiumjs-entities -- 高级 Entity API,封装了带有时间动态属性的基元。
  • cesiumjs-3d-tiles -- 使用 MVTDataProvider 将分块矢量数据作为运行时 3D Tiles。
  • cesiumjs-materials-shaders -- 外观和后期处理所使用的 Material(Fabric)系统。
  • cesiumjs-spatial-math -- 用于几何体定位的 Cartesian3、Matrix4、Transforms 和坐标转换。

评论

支持 Markdown