Cesium几何和外观(七)
几何和外观
本教程将向您介绍可用于Primitive API的几何和外观系统。这是将Cesium扩展为自定义网格,形状,体积和外观的高级主题,不适用于典型的Cesium用户。如果您有兴趣学习如何在地球上绘制各种形状和体积,请查看Visualizing Spatial Data教程。
铯可以使用实体创建不同的几何类型,例如多边形和椭圆体。例如,将以下内容复制并粘贴到Hello World Sandcastle示例中,以点阵模式在地球上创建一个矩形:
var 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
})
}
});
在本教程中,我们去原语的引擎盖下,并期待在Geometry
和Appearance
形成它们的类型。几何定义了图元的结构,即构成图元的三角形,线条或点。外观定义了基元的阴影,包括其完整的GLSL顶点和片段着色器以及渲染状态。
铯支持以下几何。
![]() |
BoxGeometry |
一个盒子 |
![]() |
BoxOutlineGeometry |
|
![]() |
CircleGeometry |
一个圆圈或挤出的圆圈 |
![]() |
CircleOutlineGeometry |
|
![]() |
CorridorGeometry |
垂直于表面的折线,宽度以米为单位,可选的拉伸高度 |
![]() |
CorridorOutlineGeometry |
|
![]() |
CylinderGeometry |
圆柱体,圆锥体或截头圆锥体 |
![]() |
CylinderOutlineGeometry |
|
![]() |
EllipseGeometry |
椭圆或挤出的椭圆 |
![]() |
EllipseOutlineGeometry |
|
![]() |
EllipsoidGeometry |
椭圆体 |
![]() |
EllipsoidOutlineGeometry |
|
![]() |
RectangleGeometry |
矩形或挤压矩形 |
![]() |
RectangleOutlineGeometry |
|
![]() |
PolygonGeometry |
具有可选孔或挤出多边形的多边形 |
![]() |
PolygonOutlineGeometry |
|
折线几何 | PolylineGeometry |
以像素为单位的线段集合 |
![]() |
SimplePolylineGeometry |
|
![]() |
PolylineVolumeGeometry |
沿着折线挤出的2D形状 |
![]() |
PolylineVolumeOutlineGeometry |
|
![]() |
SphereGeometry |
球体 |
![]() |
SphereOutlineGeometry |
|
![]() |
WallGeometry |
垂直于地球的墙 |
![]() |
WallOutlineGeometry |
使用几何和外观的好处是:
- 性能 – 在绘制大量静态基元时(比如美国每个邮政编码的多边形),使用几何直接允许我们将它们组合成单个几何体,以减少CPU开销并更好地利用GPU。在Web工作人员上完成原语组合以保持UI的响应。
- 灵活性 – 基元结合几何和外观。通过将它们解耦,我们可以独立地进行修改。我们可以添加与许多不同外观兼容的新几何图形,反之亦然。
- 低级访问 – 外观提供了近似于金属的渲染,无需担心
Renderer
直接使用的所有细节。外观使它容易:- 编写完整的GLSL顶点和片段着色器。
- 使用自定义渲染状态。
还有一些缺点:
- 直接使用几何图形和外观需要更多的代码和更深入的图形理解。基元处于适合绘制应用程序的抽象级别; 几何图形和外观具有更接近传统3D引擎的抽象级别。
- 组合几何对于静态数据非常有效,而不一定适用于动态数据。
我们使用几何和外观来重写最初的代码示例:
var viewer = new Cesium.Viewer('cesiumContainer');
var 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
// })
// }
//});
var 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
,它结合了几何实例和外观。现在,我们不会区分a Geometry
和GeometryInstance
其他实例是几何的容器。
为了创建矩形的几何图形,即覆盖矩形区域并适合地球曲率的三角形,我们创建一个RectangleGeometry
。
既然我们知道它在表面上,我们可以使用它EllipsoidSurfaceAppearance
。考虑到几何体位于椭球体表面或恒定高度上,这可以节省内存并支持所有材料。
结合几何
当我们使用一个图元绘制多个静态几何图形时,我们看到了性能优势。例如,我们画两个矩形。
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var 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
})
});
var 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
不同的颜色为每个实例着色。
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var 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)
}
});
var 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()
}));
每个实例都有一个Color
属性。然后使用a构造基元,该基元PerInstanceColorAppearance
知道使用每个实例的颜色属性来确定阴影。
结合几何形状,Cesium可以高效地绘制大量的几何图形。以下示例绘制了2592个唯一着色的矩形。它会优化几何图形,然后绘图非常快。
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var instances = [];
for (var lon = -180.0; lon < 180.0; lon += 5.0) {
for (var 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
可以是任何JavaScript类型:字符串,数字,具有自己属性的对象等。
以下示例使用a创建实例id
,并在单击时向控制台写入消息。
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var 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()
}));
var handler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
handler.setInputAction(function (movement) {
var pick = scene.pick(movement.position);
if (Cesium.defined(pick) && (pick.id === 'my rectangle')) {
console.log('Mouse clicked rectangle.');
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
使用id
而不是对实例本身的引用,允许原语(和我们的应用程序)避免在构造原语后在内存中保留对完整实例的引用,包括对几何的引用。由于一个几何体可以包含几个大型的数组,这使我们可以节省大量的内存。
几何实例
到目前为止,我们只将几何实例定义为几何的容器。另外,实例用于在场景的不同部分定位,缩放和旋转相同的几何图形。这是可能的,因为多个实例可以引用相同Geometry
,并且每个实例可以具有不同的modelMatrix。这使我们只能计算一次几何并重复使用它多次。
以下示例创建一个EllipsoidGeometry
和两个实例。每个实例引用相同的椭球体几何体,但是使用不同的modelMatrix
结果使一个椭球体位于另一个之上。
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var ellipsoidGeometry = new Cesium.EllipsoidGeometry({
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
radii : new Cesium.Cartesian3(300000.0, 200000.0, 150000.0)
});
var 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)
),
attributes : {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.CYAN)
}
});
var 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)
),
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
})
}));
更新每个实例的属性
在将几何图形添加到图元后,也可以更新每个实例的属性。每个实例的属性包括:
- 颜色:
ColorGeometryInstanceAttribute
确定实例的颜色。该原语必须有一个PerInstanceColorAppearance
。 - 显示:确定实例可见性的布尔值。适用于任何实例。
此示例显示如何更改几何实例的颜色:
var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var 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'
});
var primitive = new Cesium.Primitive({
geometryInstances : circleInstance,
appearance : new Cesium.PerInstanceColorAppearance({
translucent : false,
closed : true
})
});
scene.primitives.add(primitive);
setInterval(function() {
var attributes = primitive.getGeometryInstanceAttributes('circle');
attributes.color = Cesium.ColorGeometryInstanceAttribute.toValue(Cesium.Color.fromRandom({alpha : 1.0}));
},2000);
几何实例的属性可以使用从原语中检索primitive.getGeometryInstanceAttributes
。该属性attributes
可以直接更改。在这种情况下,我们改变attributes.color
为每2000毫秒等于一个新的随机生成的颜色。
出场
几何定义结构。基元的另一个关键属性appearance
定义了基元的底纹,即单个像素是如何着色的。基元可以有许多几何实例,但它只能有一个外观。根据外观类型的不同,外观会有一个material
定义阴影大部分的外观。
铯具有以下外观。
 |
|
与所有的几何类型的作品,并支持的外观材料来形容阴影。 |
 |
|
假定几何体的“MaterialAppearance”版本与地球表面平行,就像多边形一样,并且通过程序化地计算许多顶点属性来使用该假设来节省内存。 |
 |
|
使用每个实例的颜色来为每个实例着色。 |
 |
|
支持材质遮蔽Polyline。 |
 |
|
使用每顶点着色或每段着色来遮蔽多义线。 |
外观定义绘制基元时在GPU上执行的完整GLSL顶点和片段着色器。我们很少触及这些,除非我们正在编写自定义外观。外观还定义了完整的渲染状态,该状态控制绘制基元时GPU的状态。我们可以直接定义渲染状态或使用像更高级别的性能closed
和translucent
,它的出现将转换为渲染状态。例如:
// Perhaps for an opaque box that the viewer will not enter.
// - Backface culled and depth tested. No blending.
var appearance = new Cesium.PerInstanceColorAppearance({
translucent : false,
closed : true
});
// This appearance is the same as above
var anotherAppearance = new Cesium.PerInstanceColorAppearance({
renderState : {
depthTest : {
enabled : true
},
cull : {
enabled : true,
face : Cesium.CullFace.BACK
}
}
});
一旦创建了外观,我们不能改变它的renderState
属性,但我们可以改变它的属性material
。同样,我们也可以改变一个图元的appearance
属性。
大多数外观也具有flat
和faceForward
属性,它们间接控制GLSL着色器。
flat
– 平坦的阴影。不要考虑照明。faceForward
– 照明时,翻转法线,使其始终面向观看者。避免背面上的黑色区域,例如墙壁的内部。
平坦:真实 | faceForward:错误 | faceForward:真的 |
---|---|---|
![]() |
![]() |
![]() |
几何和外观兼容性
我们已经看到,并非所有外观都适用于所有几何形状。例如,EllipsoidSurfaceAppearance
不适合于WallGeometry
由于壁是垂直于全球,不平行的。
除了这样的语义之外,为了使外观与几何图形兼容,它们必须具有匹配的顶点格式,这意味着几何图形必须具有外观期望作为输入的数据。VertexFormat
创建几何时可以提供A.
我们可以通过请求几何计算所有顶点属性来使事情简单但效率低下且浪费,这将使几何与所有外观兼容(忽略每个实例的属性;参见下文)。
var geometry = new Cesium.RectangleGeometry({
vertexFormat : Cesium.VertexFormat.ALL
// ...
});
EllipsoidSurfaceAppearance
例如,如果我们正在使用,我们可以摆脱请求职位。
var geometry = new Ceisum.RectangleGeometry({
vertexFormat : Ceisum.VertexFormat.POSITION_ONLY
// ...
});
一般来说,我们如何知道用于给定外观的顶点格式?大多数外观都有一个vertexFormat
属性,甚至是一个VERTEX_FORMAT
静态常量。
var geometry = new Ceisum.RectangleGeometry({
vertexFormat : Ceisum.EllipsoidSurfaceAppearance.VERTEX_FORMAT
// ...
});
var geometry2 = new Ceisum.RectangleGeometry({
vertexFormat : Ceisum.PerInstanceColorAppearance.VERTEX_FORMAT
// ...
});
var appearance = new Ceisum.MaterialAppearance(/* ... */);
var geometry3 = new Ceisum.RectangleGeometry({
vertexFormat : appearance.vertexFormat
// ...
});
此外,几何的vertexFormat
决定是否可以与其他几何结合。两个几何不必是相同的类型; 他们只需要匹配的顶点格式。
资源
在参考文档中,请参阅:
有关材料的更多信息,请参阅织物。
有关未来计划,请参阅几何和外观路线图。