什么是Cesium.js?
Cesium.js是一个用于创建3D地球和地图的JavaScript库,它支持全球范围内的地形、影像、矢量数据和3D模型的可视化。Cesium利用了现代浏览器中的WebGL技术,使得开发者可以构建高性能、高保真度的应用程序。此外,Cesium还提供了丰富的API和插件系统,极大地扩展了其功能。
自定义材质的实现
在Cesium中,材质可以通过Cesium.Material
类进行定义。我们可以通过编写GLSL代码来自定义材质的外观,例如改变颜色、添加动画效果或模拟不同的表面属性。为了实现这一点,我们需要定义一个新的材质类型,并提供相应的着色器代码。
定义自定义材质
首先,我们定义了一个名为CustomMaterialProperty
的类,该类继承了Cesium的材质属性接口。这个类负责设置材质的颜色、持续时间等属性,并根据当前时间动态计算材质的值。通过重写getValue
方法,我们可以让材质随时间变化,从而产生动画效果。
class CustomMaterialProperty {
/**
* @param {Object=} options 配置项
*/
constructor(options = {}) {
this._definitionChanged = new Cesium.Event();
this._color = undefined;
this._colorSubscription = undefined;
this.color = options.color || Cesium.Color.RED;
this.duration = options.duration || 2000;
this._time = performance.now();
}
/**
* @return {boolean}
*/
get isConstant() {
return false;
}
/**
* @return {Cesium.Event}
*/
get definitionChanged() {
return this._definitionChanged;
}
/**
* @return {string}
*/
getType() {
return MATERIAL_TYPE;
}
/**
* @param {Cesium.JulianDate} time
* @param {Object=} result
* @return {Object}
*/
getValue(time, result = {}) {
result.color = Cesium.Property.getValueOrUndefined(this.color, time);
result.time =
((performance.now() - this._time) % this.duration) / this.duration;
return result;
}
/**
* @param {CustomMaterialProperty} other
* @return {boolean}
*/
equals(other) {
return (
this === other ||
(other instanceof CustomMaterialProperty && this._color === other._color)
);
}
}
接着,我们使用Cesium.Material._materialCache.addMaterial
注册我们的自定义材质。这里,我们指定了材质的类型名称MATERIAL_TYPE
,以及包含着色器代码的fabric
对象。着色器代码定义了如何根据输入的参数(如颜色和时间)计算材质的最终显示效果。
Cesium.Material._materialCache.addMaterial(MATERIAL_TYPE, {
fabric: {
type: MATERIAL_TYPE,
uniforms: {
color: new Cesium.Color(1, 1, 0, 1),
time: 1,
spacing: 40,
width: 1,
},
source: `
uniform vec4 color;
czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
float alpha = distance(st, vec2(.5));
material.alpha = color.a * alpha * 1.5;
material.diffuse = color.rgb * 1.3;
return material;
}`,
},
translucent: () => true,
});
初始化Viewer并加载GeoJSON数据
为了展示自定义材质的效果,我们需要初始化一个Cesium.Viewer
实例,并加载一些地理数据。在这个例子中,我们使用了ArcGIS提供的世界影像图层作为底图,并从GitHub上加载了一个描述广东省边界的GeoJSON文件。
const viewer = new Cesium.Viewer("box");
async function addMaterial() {
const dataSource = await Cesium.GeoJsonDataSource.load('url');
// 设置材质...
viewer.dataSources.add(dataSource);
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(113.280637, 23.125178, 20000),
duration: 3,
});
}
addMaterial();
动态设置材质颜色
为了让每个区域都有独特的颜色,我们预定义了一系列颜色配置,并将它们应用到加载的GeoJSON实体上。我们遍历所有实体,并根据索引分配颜色,确保不同区域之间有明显的区分。
const COLOR_CONFIGS = [
[15, 176, 255],
[18, 76, 154],
[64, 196, 228],
[66, 178, 190],
[51, 176, 204],
[140, 183, 229],
[0, 244, 188],
[19, 159, 240],
];
// 在addMaterial函数内...
entities.forEach((entity, index) => {
entity.polygon.extrudedHeight = 10000;
entity.polygon.outline = false;
entity.polygon.material = new CustomMaterialProperty({
color: colors[index % colors.length],
});
});
完整代码
import * as Cesium from "cesium";
/**
* 自定义材质类型名称
* @const {string}
*/
const MATERIAL_TYPE = "Custom";
/**
* 自定义材质属性类
* @class
*/
class CustomMaterialProperty {
/**
* @param {Object=} options 配置项
*/
constructor(options = {}) {
this._definitionChanged = new Cesium.Event();
this._color = undefined;
this._colorSubscription = undefined;
this.color = options.color || Cesium.Color.RED;
this.duration = options.duration || 2000;
this._time = performance.now();
}
/**
* @return {boolean}
*/
get isConstant() {
return false;
}
/**
* @return {Cesium.Event}
*/
get definitionChanged() {
return this._definitionChanged;
}
/**
* @return {string}
*/
getType() {
return MATERIAL_TYPE;
}
/**
* @param {Cesium.JulianDate} time
* @param {Object=} result
* @return {Object}
*/
getValue(time, result = {}) {
result.color = Cesium.Property.getValueOrUndefined(this.color, time);
result.time =
((performance.now() - this._time) % this.duration) / this.duration;
return result;
}
/**
* @param {CustomMaterialProperty} other
* @return {boolean}
*/
equals(other) {
return (
this === other ||
(other instanceof CustomMaterialProperty && this._color === other._color)
);
}
}
// 定义颜色属性
Object.defineProperty(
CustomMaterialProperty.prototype,
"color",
Cesium.createPropertyDescriptor("color")
);
// 注册自定义材质
Cesium.Material._materialCache.addMaterial(MATERIAL_TYPE, {
fabric: {
type: MATERIAL_TYPE,
uniforms: {
color: new Cesium.Color(1, 1, 0, 1),
time: 1,
spacing: 40,
width: 1,
},
source: `
uniform vec4 color;
czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st;
float alpha = distance(st, vec2(.5));
material.alpha = color.a * alpha * 1.5;
material.diffuse = color.rgb * 1.3;
return material;
}`,
},
translucent: () => true,
});
/**
* 初始化 viewer
* @type {Cesium.Viewer}
*/
const viewer = new Cesium.Viewer("box", {
animation: false,
baseLayerPicker: false,
baseLayer: Cesium.ImageryLayer.fromProviderAsync(
Cesium.ArcGisMapServerImageryProvider.fromUrl(
"https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer"
)
),
fullscreenButton: false,
timeline: false,
infoBox: false,
});
/**
* 预定义的颜色配置
* @type {Array<Array<number>>}
*/
const COLOR_CONFIGS = [
[15, 176, 255],
[18, 76, 154],
[64, 196, 228],
[66, 178, 190],
[51, 176, 204],
[140, 183, 229],
[0, 244, 188],
[19, 159, 240],
];
/**
* 添加材质到地图
* @async
*/
async function addMaterial() {
const dataSource = await Cesium.GeoJsonDataSource.load(
"https://z2586300277.github.io/three-editor/dist/files/font/guangdong.json"
);
const entities = dataSource.entities.values;
const colors = COLOR_CONFIGS.map(
([r, g, b]) => new Cesium.Color(r / 255, g / 255, b / 255, 1)
);
entities.forEach((entity, index) => {
entity.polygon.extrudedHeight = 10000;
entity.polygon.outline = false;
entity.polygon.material = new CustomMaterialProperty({
color: colors[index % colors.length],
});
});
viewer.dataSources.add(dataSource);
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(113.280637, 23.125178, 20000),
orientation: {},
duration: 3,
});
}
addMaterial();
实现效果
结论
通过上述步骤,我们成功地创建了一个带有自定义材质行政区。Cesium.js不仅让我们能够以极高的精度和细节展示地理信息,还为我们提供了灵活的工具来增强用户的视觉体验。无论是开发教育工具、城市规划软件还是娱乐应用,Cesium都是一个非常有价值的选择。