This tutorial will introduce you to a system that provides geometry and appearance using the Primitive API. This is an advanced theme for extending Cesium JS with custom mesh, shape, volume, and appearance, rather than for general Cesium users. If you are interested in learning how to draw various shapes and volumes on the earth, please see Create entity course. CesiumJS can create different geometry types using solids such as polygons and ellipses. For example, copy and paste the following code into Hello World Sandcastle Example to create a rectangle with a point pattern on a sphere:
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 }) } });
In this tutorial, we'll drill down under the sunshades to see the geometry and appearance types that make up them. Geometry defines the Primitive structure, that is, the triangles, lines, or points that make up the Primitive. Appearance defines Primitive shading, including its complete GLSL vertex and patch shading, and rendering state.
The benefits of using geometry and appearance are:
- Performance: when drawing a large number of Primitive (such as the polygon of each zip code in the United States), the use of geometry directly allows us to combine them into a single geometry to reduce CPU overhead and make better use of GPU. The combination of Primitive is completed on the Web worker to maintain the responsiveness of the UI.
- Flexibility: Primitive combines geometry and appearance. By separating them, we can modify each one independently. We can add new geometry compatible with many different appearances and vice versa.
- Low level access: the appearance provides rendering access close to metal without worrying about all the details of using the Renderer directly. Appearance makes it easy to:
- Write all GLSL vertex and patch shaders
- Use custom render state
Of course, there are some disadvantages:
- Using geometry and appearance directly requires more code and a deeper understanding of graphics. The entity is at an abstraction level suitable for the mapping application; The abstraction level of geometry and appearance is closer to the traditional 3D engine.
- Combined geometry is effective for static data, but not necessarily for dynamic data.
Let's rewrite the initial code example with geometry and appearance:
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') }) }));
We use Primitive (Primitive) instead of rectangular solid, which combines Geometry and appearance. For now, we will not distinguish between Geometry and GemometryInstance. An instance is not only an instance of Geometry, but also its container.
In order to create rectangular geometry, that is, triangles covering the rectangular area and triangles suitable for the curvature of the sphere, we created a rectangle geometry.
Because it is on the surface, we can use ellipse surface appearance. This saves memory by assuming a constant height of the geometry on the surface or above the ellipsoid.
Geometry types
CesiumJS provides the following geometry:
Composite geometry
When we use a Primitive to draw multiple static geometry, we see a performance advantage. For example, draw two rectangles in a Primitive.
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') }) }));
We create another instance with a different rectangle, and then provide the two instances to Primitive. This will draw both instances with the same appearance.
Some appearances allow each instance to provide unique properties. For example, we can use PerinstanceColorAppearance to color each instance with a different color.
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() }));
Each instance has a color attribute. Primitive is constructed with PerinstanceColorAppearance, which uses the color attribute of each instance to determine the shading.
Combining geometry allows CesiumJS to draw many geometry effectively. The following example draws 2592 rectangles with unique colors.
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() }));
Picking
Instances can be accessed independently after merging. Assign an ID to the instance and use it to determine whether to pick the instance using Scene.Pick.
The following example creates an instance with an id and writes a message to the console when the instance is clicked.
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);
Using id avoids referencing the entire instance, including geometry, in memory after Primitive construction.
Geometry instances
Instances can be used to position, scale, and rotate the same Geometry in different parts of the scene. This is possible because multiple instances can reference the same Geometry, and each instance can have a different modelMatrix. This allows us to calculate the Geometry only once and reuse it many times.
The following example creates an ellipsegeometry and two instances. Each instance references the same ellipsoid geometry, but uses a different modelMatrix to place it, resulting in one ellipsoid on top of the other.
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), new Cesium.Matrix4() ), 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), 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 }) }));
Updating per instance attributes
After adding geometry to Primitive, update each instance property of the geometry to change the visualization. The properties of each instance include:
- Color: ColorGeometryInstanceAttribute determines the color instance. Primitive must have PerInstanceColorAppearance.
- The Show: boolean type determines whether the instance is visible. All instances have this property.
The following shows how to change the color of geometry instances:
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);
The attributes of geometry instances can be retrieved using primitive.getGeometryInstanceAttributes. The attributes of attributes can be changed directly.
appearance
Geometry defines the structure. Another key attribute of primitive, appearance, defines the texture of primitive, that is, the color of a single pixel. Primitive can have multiple geometric instances, but only one appearance. Depending on the type of appearance, the appearance will have a material that defines the body of the shading.
CesiumJS has the following appearance:
Appearances define the full GLSL vertex and patch shaders that are executed on the GPU when painting Primitive. Appearance also defines the complete rendering state, which controls the state of the GPU when primitvie is drawn. We can directly define the rendering state, or use more advanced attributes, such as closed and translucent, to convert the appearance to the rendering state. For example:
// 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 } } });
After you create an appearance, you cannot change its renderState property, but you can change its material. We can also change the appearance property of primitive.
Most appearances also have flat and faceForward attributes, which indirectly control GLSL shaders.
- Flat: flat shadow. Do not consider lighting.
- faceForward: when lighting, flip the normal so that it is always facing the audience. Avoid black areas on the back, such as the inside of a wall.
Geometry and appearance compatibility
Not all appearances apply to all geometry. For example, the EllipsoidSurfaceAppearance appearance does not apply to WallGeometry geometry because the wall is not on the surface of the sphere.
For the appearance to be compatible with the geometry, they must have a matching vertex format, which means that the geometry must have the input data expected by the appearance. vertexFormat can be provided when creating geometry.
The vertexFormat of the geometry determines whether it can be combined with other geometry. The two geometries do not have to be of the same type, but they need to match the vertex format.
For convenience, the appearance has either a vertexFormat attribute or a vertex that can be passed in as a geometry option_ Format static constant.
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 // ... });
Resources resources
Reference documents:
For more information, please visit: Fabric For more future plans, visit: Geometry and Appearances Roadmap
Cesium Chinese network QQ group: 838036184