Cesium几何和外观(七)

admin0条评论 8,502 次浏览

几何和外观

本教程将向您介绍可用于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
        })
    }
});

原始程度

在本教程中,我们去原语的引擎盖下,并期待在GeometryAppearance形成它们的类型。几何定义了图元的结构,即构成图元的三角形,线条或点。外观定义了基元的阴影,包括其完整的GLSL顶点和片段着色器以及渲染状态。

铯支持以下几何。

框几何 BoxGeometry 一个盒子
框大纲几何 BoxOutlineGeometry
圆几何 CircleGeometry 一个圆圈或挤出的圆圈
Cirlce轮廓几何 CircleOutlineGeometry
走廊几何 CorridorGeometry 垂直于表面的折线,宽度以米为单位,可选的拉伸高度
走廊轮廓几何 CorridorOutlineGeometry
Cyllinder几何 CylinderGeometry 圆柱体,圆锥体或截头圆锥体
Cyllinder轮廓几何 CylinderOutlineGeometry
椭圆几何 EllipseGeometry 椭圆或挤出的椭圆
椭圆轮廓几何 EllipseOutlineGeometry
椭圆几何 EllipsoidGeometry 椭圆体
椭圆体轮廓几何 EllipsoidOutlineGeometry
范围几何 RectangleGeometry 矩形或挤压矩形
程度轮廓几何 RectangleOutlineGeometry
多边形几何 PolygonGeometry 具有可选孔或挤出多边形的多边形
多边形轮廓几何 PolygonOutlineGeometry
折线几何 PolylineGeometry 以像素为单位的线段集合
Polyline Outlie几何 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 GeometryGeometryInstance其他实例是几何的容器。

为了创建矩形的几何图形,即覆盖矩形区域并适合地球曲率的三角形,我们创建一个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
    })
}));

椭球实例

更新每个实例的属性

在将几何图形添加到图元后,也可以更新每个实例的属性。每个实例的属性包括:

此示例显示如何更改几何实例的颜色:

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定义阴影大部分的外观。

Appearences

铯具有以下外观。

![材料](./图像/ material.png) 与所有的几何类型的作品,并支持的外观材料来形容阴影。
![Ellispoid曲面](./ images / ellipsoidSurface.png) 假定几何体的“MaterialAppearance”版本与地球表面平行,就像多边形一样,并且通过程序化地计算许多顶点属性来使用该假设来节省内存。
![PerInstanceColorAppearance](./图像/ perinstance.png) 使用每个实例的颜色来为每个实例着色。
![多段线材质](./ images / polylinematerial.png) 支持材质遮蔽Polyline。
![折线颜色](./ images / polylinecolor.png) 使用每顶点着色或每段着色来遮蔽多义线。

外观定义绘制基元时在GPU上执行的完整GLSL顶点和片段着色器。我们很少触及这些,除非我们正在编写自定义外观。外观还定义了完整的渲染状态,该状态控制绘制基元时GPU的状态。我们可以直接定义渲染状态或使用像更高级别的性能closedtranslucent,它的出现将转换为渲染状态。例如:

// 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属性。

大多数外观也具有flatfaceForward属性,它们间接控制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决定是否可以与其他几何结合。两个几何不必是相同的类型; 他们只需要匹配的顶点格式。

资源

在参考文档中,请参阅:

有关材料的更多信息,请参阅织物

有关未来计划,请参阅几何和外观路线图


分类目录