Cesium中的图形技术:Primitive API 简介

自定义几何形状和外观编程

翻译自官方文档。canvas

这篇教程介绍的是 Primitive API 有关的资料,适合高级用户。想快速绘制各类形状,建议参考 Entity API。api

1. 几何概述

Cesium 能够建立 Entity API 建立各类几何图形,例如绘制一个矩形:app

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
    })
  }
});

这是一个很常见的例子。dom

在本教程中,将深刻研究 Primitive API:使用 Geometry 和 Appearance 来构造几何图形。性能

使用 Primitive API 中的 Geometry 和 Appearance 的好处是:翻译

  • 性能:当须要绘制大量静态图形时,使用 Primitive API 能够将图形组合成一个大图形,减小 CPU 的开销,更充分利用 GPU;使用 WebWorker 性能更佳。
  • 灵活性:Primitive 主要是由 Geometry 和 Appearance 构成的。解耦它们,就能够分别修改它们。
  • 更底层:Appearance 提供了接近底层的编程风格,可是你又不须要考虑 WebGL 的细节,能够轻松达到:
    • 编写完整的 GLSL 顶点、片元着色器
    • 自定义渲染

缺点也是有的:code

  • 须要更复杂的代码,也须要额外的图形学知识。Primitive API 更接近传统三维引擎的编程。
  • 组合图形对动态数据不必定有效。

使用 Primitive API 重写上面的代码:orm

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')
  })
}));

GeometryInstance 是 Geometry 的容器,当前只有一个 Geometry。对象

为了建立矩形,使用 RectangleGeometry:

由于它是地面的矩形,因此使用 EllipsoidSurfaceAppearance 这种外观,

2. 几何有哪些种类

十几种(有对应的线框模式),简单列举以下:

在沙盒中能够找到对应的示例代码(Geometries And Appearance):

3. 组合几何形状

当你须要绘制多个静态的几何图形时,Primitive API 就有性能优点了。例如,能够在一个 Primitive 中组合两个矩形:

const 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
  })
});

const 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')
  })
}));

建立了两个 geometry instance,共用了一个 appearance。

有一些 Appearance 容许使用 GeometryInstance 本身的属性来着色:

const instance = new Cesium.GeometryInstance({
  geometry : // 和上面同样
  attributes : {
    color : new Cesium.ColorGeometryInstanceAttribute(0.0, 0.0, 1.0, 0.8)
  }
});

const anotherInstance = new Cesium.GeometryInstance({
  geometry : // 和上面同样
  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()
}));

出来的效果:

每一个 GeometryInstance 都带了一个 color attribute。

组合图形让 Cesium 能绘制大量几何图形,下例是 2592 个独立颜色的矩形:

const instances = [];

for (let lon = -180.0; lon < 180.0; lon += 5.0) {
  for (let 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()
}));

4. 点击拾取

GeometryInstance 绘制后仍然是能够独立访问的,只需分配一个 id 便可。

const 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
  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);
	  // pick.id 即 GeometryInstance.id
    if (Cesium.defined(pick) && (pick.id === 'my rectangle')) {
      console.log('Mouse clicked rectangle.');
    }
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

5. 几何实例:instance 技术

Geometry 容许使用不一样的转换矩阵,构造不一样位置的 GeometryInstance,这样顶点数据只存了一份:

下面这个实例只建立一个椭球几何体,可是用不一样的矩阵来表示不一样的位置。

const ellipsoidGeometry = new Cesium.EllipsoidGeometry({
    vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
    radii : new Cesium.Cartesian3(300000.0, 200000.0, 150000.0)
});

const 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)
    }
});

const 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
    })
}));

6. 更新几何实例的属性

绘制几何完成后,能够更新 GeometryInstance 的属性,包括:

  • Color:肯定 GeometryInstance 颜色的 ColorGeometryInstanceAttribute 对象,使用此属性,Primitive 必须使用的是 PerInstanceColorAppearance
  • Show:布尔值,表达 GeometryInstance 是否可视。
const 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'
});
const 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);

这个例子每隔2秒,颜色就会变化一次。

使用 primitive.getGeometryInstanceAttributes('id') 能够获取某个 GeometryInstance 的属性,固然,它的返回值的属性能够直接修改,像代码中的 attributes.color = ... 同样。

7. 几何的外观

几何只定义告终构,外观负责颜值。Primitive 能够有若干图几何,可是只能有一种外观。

Cesium 自带的外观以下:

  • MaterialAppearance:适用于任何几何的外观,支持描述阴影的材质
  • EllipsoidSurface:MaterialAppearance 的另外一个版本,它假设几何形状是贴地的,这样计算顶点属性的时候能够节约一些内存
  • PerInstanceColorAppearance:每一个 GeometryInstance 的颜色是独立控制的外观
  • PolylineMaterialAppearance:支持对折线进行着色
  • PolylingColorAppearance:能够控制折线的点或者线段的颜色进行着色的外观

能够定义渲染状态,或者使用更高级的属性 closedtranslucent,例如:

// 不透明
var appearance  = new Cesium.PerInstanceColorAppearance({
  translucent : false,
  closed : true
});

// 这是比较底层的写法,效果与上面同样
var anotherAppearance  = new Cesium.PerInstanceColorAppearance({
  renderState : {
    depthTest : {
      enabled : true
    },
    cull : {
      enabled : true,
      face : Cesium.CullFace.BACK
    }
  }
});

建立外观后,是没办法改变它的 renderState 的,可是能够修改材质。

能够更改图元的外观属性,大多数外观有 flat 和 faceForward 属性,这两个属性间接控制着 GLSL 着色器。

  • flat - 不考虑照明阴影的平面着色
  • faceForward:当照明一个图元时,反转三角面的法线(也就是顶点法线),使背面看起来像正面,好比墙的内面着色。

8. 几何与外观的兼容性

并非全部的外观都适合全部的几何。好比,EllipsoidSurfaceAppearance 不适合 WallGeometry,由于墙面是垂直地面的,并不贴地。

为了使得外观与几何图形兼容,它们的顶点格式必须匹配。指定 Geometry 的 vertexFormat 属性便可。

下图能够说明这个问题,当外观须要有 st(即纹理坐标)而 Geometry 没有时,就会报错:

外观类有 VertexFormat 静态属性,或外观对象有 vertexFormat 属性可供使用。

几何的 vertexFormat 是组合几何图形的因素,几何的类型能够不一样,可是顶点的格式必须是同样的。

const geometry = new Ceisum.RectangleGeometry({
  vertexFormat : Ceisum.EllipsoidSurfaceAppearance.VERTEX_FORMAT
  // ...
});

const geometry2 = new Ceisum.RectangleGeometry({
  vertexFormat : Ceisum.PerInstanceColorAppearance.VERTEX_FORMAT
  // ...
});

const appearance = new Ceisum.MaterialAppearance(/* ... */);
const geometry3 = new Ceisum.RectangleGeometry({
  vertexFormat : appearance.vertexFormat
  // ...
});

译者注:顶点格式,即顶点的坐标、法线、uv(即st)、颜色等组合状况

9. 我的补充 Entity API vs Primitive API

Entity 用的是 Graphic,参数化建立图形,并能够参数符号化,几何形状和外观耦合在一块儿

Primitive 用的是 Geometry + Appearance,能够分别修改几何形状和外观。虽然有预约义的 Geometry,可是 Primitive API 提供的是更接近 WebGL 的接口,构造 Geometry 彻底可使用与 WebGL 十分接近的逻辑,传入顶点、法线等素材建立不可思议的形状。

Entity 拥有 Property 进行时间插值,Primitive 没有时间插值,须要深刻着色器。

除此以外,Entity 在数据量特别大的状况下性能比 Primitive 差。

相关文章
相关标签/搜索