catalogue
Updating pre instance attributes
Geometry and appearance compatibility
The tutorial will introduce the geometry and appearance system provided by the original API. Extending Cesium with custom grids, shapes, containers, and skins is an advanced theme that is not suitable for specific Cesium users. If you are interested in drawing various shapes and containers on the ball, you can check it out Spatial data visualization(Visualizing Spatial Data )Tutorial
Cesium can use entities to create different geometric types, such as polygons and ellipses. For example, copy and paste the following code to Hello World Sandcastle Examlpe , create a rectangle with a light spot shape on the ball:
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 the tutorials in this chapter, we'll drill down on primitive types and look at the geometry that makes them up( Geometry )And appearance( Appearance )Type. Geometry defines the original structure, that is, triangles, lines and points, which constitute primitive. The appearance defines the original shape, including the full GLSL vertex and clip shaders, as well as the render state.
Cesium supports the following geometry:
- A box:
BoxGeometry BoxOutlineGeometry
- A circle or extruded circle
CircleGeometry CircleOutlineGeometry
- Broken line perpendicular to the surface, width in meters, with optional protrusion height
CorridorGeometry CorridorOutlineGeometry
- A cylinder cone, or truncatedcone
CylinderGeometry CylinderOutlineGeometry
- An ellipse or extruded ellipse
EllipseGeometry EllipseOutlineGeometry
- An ellipsoid
EllipsoidGeometry EllipsoidOutlineGeometry
- Rectangle or extruded rectangle
RectangleGeometry RectangleOutlineGeometry
- Polygons with optional holes or extruded polygons
PolygonGeometry PolygonOutlineGeometry
- A collection of line segments with pixel level width
PolylineGeometry SimplePolylineGeometry
- 2D shape extruded along polyline
- An sphere
SphereGeometry SphereOutlineGeometry
- Wall perpendicular to the ball
WallGeometry WallOutlineGeometry
The benefits of using geometry and appearance are:
- Performance - when drawing a large number of static primitives (for example, polygons represent each zip code in the United States), the direct use of geometry allows us to combine them into a single geometry to reduce CPU overhead and make better use of GPU. In web work, merge primitives to ensure the response of UI.
- Flexibility - Primitives combine geometry and appearance. By decoupling them, we can modify them individually. We can add new geometry that is compatible with many different appearances and vice versa.
- Low level access - appearance provides close to the metal access to rendering without worrying about using the renderer directly( Renderer )Details. Appearance makes it easy:
- Shader filled with GLSL vertices and fragments;
- Use a custom render state.
There are also some determinations:
- Using geometry and appearance directly requires more code and an in-depth understanding of graphics. The entity has an abstraction level suitable for mapping application apps; The geometric image and appearance have an abstraction level closer to the traditional 3D engine;
- Merged geometry is valid for static data, but not necessarily for dynamic data.
Let's rewrite the original code example using 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') }) }));
Instead of using a rectangular entity, we use a generic Primitive , which brings together geometric instances and appearances. At present, we do not distinguish between geometry( Geometry )And geometric instances( GeometryInstance )Unless it is an instance of the geometry of the container.
In order to create geometry, that is, a triangle covering a rectangular area, which conforms to the curvature of the earth, we create a rectangular geometry( RectangleGeometry).
Because we know it is on the surface, it can be used EllipsoidSurfaceAppearance . This saves memory and supports all materials because the geometry is on the surface or on an ellipsoid of fixed height.
When we use a primitive to draw multiple static geometry, we see the performance advantage. For example, draw two rectangles:
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 of a different rectangle, then provide them to primitive, and draw the two instances with the same appearance.
Some appearances allow each instance to provide unique properties. For example, use PerInstanceColorAppearance Add different colors to each instance.
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() }));
There is one for each instance Color Properties. Then use PerInstanceColorAppearance, which uses the color attribute of each instance to determine the shading.
Combining geometric images allows Cesium to draw many geometric shapes efficiently. The following example draws 2592 rectangles with unique colors. It will optimize the geometry and then draw very quickly:
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() }));
After the instances are combined, they can still be accessed independently. In particular, we can assign an id to the instance and use it to decide whether to use it Scene.pick To select an instance. id can be any JavaScript type: a string, a number, an object with attributes, and so on.
The following example creates an instance with id. when you click it, a message will be written to the terminal:
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 IDS instead of references to the entity itself allows primitive - our application - to avo id references to the entire instance, including geometry, in memory after the primitive is built. Because a geometry may contain several typed arrays, it can save a lot of memory.
So far, we have only defined geometric instances as several containers. In addition, instances are used to locate, scale, and rotate the same geometry in different parts of the scene. This may be because multiple instances can reference the same geometry, and each instance has a different model matrix. This allows us to calculate the geometry only once and reuse it many times.
The following code creates an ellipsegeometry and two instances. Each instance references the same ellipsoid geometry, but uses different model matrices, resulting in one ellipsoid on top of another.
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 }) }));
You can update each instance attribute of geometric images after they are added to primitive. Each attribute includes:
- Color: determines the color of the instance ColorGeometryInstanceAttribute . Primitive must have PerInstanceColorAppearance.
- Show: Boolean type that determines the visibility of an instance, for any instance.
The following example shows how to change the color of a geometric instance:
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);
Use primitive Getgeometryinstanceattributes can recover the attributes of geometric instances from primitive, and the characteristics of attributes can be changed directly. In this example, we are changing attributes Color is equal to randomly generating a new color every 2000 milliseconds.
Geometry defines the structure. Other key attributes of primitive, appearance, define the shape of primitive, for example, how each pixel is colored. A primitive may have many geometric instances, but it can only have one appearance. Depending on the type of appearance, the material of the appearance( material )Defines most shadows.
Cesium has the following appearance:
An appearance that uses all geometric types and supports materials to describe shadows.
Assuming that the geometry is parallel to the surface of the ball, the material appearance, like a polygon, calculates many vertex attributes through the program, and uses these assumptions to save memory.
Use each instance color to the shade instance.
Supports materials to shade a Polyline.
Use each vertex or segment to color the shape polyline.
Appearance defines the entire GLSL (OpenGL Shading language) vertex and fragment shaders, which are executed on the GPU when painting primitive. Unless we're writing custom looks, we rarely touch them. The appearance also defines the state of the entire renderer, which controls the state of the GPU when drawing the primitive. We can directly define the renderer state or use advanced properties such as closed and translucent , the appearance will be converted to render 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 creating the appearance, we can't change its renderState attribute, but we can change its material. Similarly, we can change the appearance properties of primitive.
Most appearances also have flat and faceForward Attributes that directly control GLSL shaders.
- flat-Flat shading. Lighting is not considered;
- faceForward - when illuminating, flip the normal so that it always faces the observer. Avoid black areas behind, such as the inside of a wall:
flat:true faceForward:false faceForward:true
We have seen that not all appearances are suitable for all geometry. For example, EllipsoidSurfaceAppearance is not suitable for wall geometry because the wall is perpendicular to the ball, not parallel.
In addition to such semantics, in order 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. When creating geometry, you can provide VertexFormat.
We can make things simple rather than inefficient and wasteful. By requiring geometry to calculate all vertex attributes, this will make the geometry compatible with all appearances (ignoring the attributes of each instance; see below).
var geometry = new Cesium.RectangleGeometry({ vertexFormat : Cesium.VertexFormat.ALL // ... });
For example, if we use EllipsoidSurfaceAppearance, we can only request the location:
var geometry = new Ceisum.RectangleGeometry({ vertexFormat : Ceisum.VertexFormat.POSITION_ONLY // ... });
In general, how do we know what vertex format fits a given appearance? Most appearances have vertexFormat Properties, even VERTEX_FORMAT Static constants.
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 // ... });
In addition, the vertexFormat of a geometry determines whether it can be combined with another geometry. Two geometric images need not be of the same type; They just need to match the vertex format.
In the reference document, please check:
For more information about materials, see Fabric.
For future plans, please review the geometry and appearance circuit diagram( Geometry and Appearances Roadmap).