Cesium 是一个用于创建基于 Web 的地理信息系统(GIS)应用的开源 JavaScript 库。材质和着色器在 Cesium 中起着重要作用,它们能让你自定义地理场景的外观。
以下是一个简单的示例,展示如何在 Cesium 中使用自定义材质和着色器来创建一个具有动态颜色变化的矩形。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>Cesium Material and Shader Exampletitle>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Cesium.js">script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<style>
html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
style>
head>
<body>
<div id="cesiumContainer">div>
<script>
// 初始化 Cesium Viewer
const viewer = new Cesium.Viewer('cesiumContainer');
// 定义自定义材质
const customMaterial = new Cesium.Material({
fabric: {
type: 'CustomMaterial',
uniforms: {
time: 0
},
source: `
czm_material czm_getMaterial(czm_materialInput materialInput)
{
czm_material material = czm_getDefaultMaterial(materialInput);
// 根据时间计算颜色
vec3 color = vec3(sin(time), cos(time), 0.5);
material.diffuse = color;
return material;
}
`
},
translucent: false
});
// 创建矩形实体并应用自定义材质
const rectangle = viewer.entities.add({
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(-100.0, 20.0, -90.0, 30.0),
material: customMaterial
}
});
// 动画更新时间
const clock = viewer.clock;
clock.onTick.addEventListener(function () {
const currentTime = Cesium.JulianDate.toSeconds(clock.currentTime);
customMaterial.uniforms.time = currentTime;
});
script>
body>
html>
new Cesium.Viewer('cesiumContainer')
创建一个 Cesium 查看器,将其挂载到 cesiumContainer
元素上。Cesium.Material
创建一个自定义材质。fabric
对象包含材质的类型、制服(uniforms)和着色器源代码。uniforms
中的 time
是一个可变的全局变量,用于控制颜色的变化。source
中的着色器代码 czm_getMaterial
函数计算材质的颜色。这里根据时间 time
计算颜色,使颜色随时间动态变化。viewer.entities.add
创建一个矩形实体,并将自定义材质应用到矩形上。onTick
事件,在每一帧更新 time
制服的值,从而实现颜色的动态变化。这个示例展示了如何在 Cesium 中使用自定义材质和着色器来创建一个具有动态效果的地理对象。你可以根据需要修改着色器代码,实现更复杂的视觉效果。
在 Cesium 里,交互和事件能让用户与地理场景进行互动,进而实现像点击、拖动、鼠标移动等操作。下面为你详细介绍常见的交互和事件以及对应的示例代码。
LEFT_CLICK
)、左键双击(LEFT_DOUBLE_CLICK
)、鼠标移动(MOUSE_MOVE
)、右键点击(RIGHT_CLICK
)等。PINCH_START
)、触摸移动(PINCH_MOVE
)、触摸结束(PINCH_END
)等。CAMERA_MOVE_START
)、相机移动结束(CAMERA_MOVE_END
)等。DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>Cesium Interaction and Events Exampletitle>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Cesium.js">script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<style>
html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
style>
head>
<body>
<div id="cesiumContainer">div>
<script>
// 初始化 Cesium Viewer
const viewer = new Cesium.Viewer('cesiumContainer');
// 鼠标左键点击事件
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
handler.setInputAction(function (movement) {
const ray = viewer.camera.getPickRay(movement.position);
const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
if (cartesian) {
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
console.log(`Clicked at longitude: ${longitude}, latitude: ${latitude}`);
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 鼠标移动事件
handler.setInputAction(function (movement) {
const ray = viewer.camera.getPickRay(movement.endPosition);
const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
if (cartesian) {
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
console.log(`Mouse moved to longitude: ${longitude}, latitude: ${latitude}`);
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 相机移动结束事件
viewer.scene.camera.moveEnd.addEventListener(function () {
const position = viewer.scene.camera.positionCartographic;
const longitude = Cesium.Math.toDegrees(position.longitude);
const latitude = Cesium.Math.toDegrees(position.latitude);
const height = position.height;
console.log(`Camera moved to longitude: ${longitude}, latitude: ${latitude}, height: ${height}`);
});
script>
body>
html>
new Cesium.Viewer('cesiumContainer')
创建一个 Cesium 查看器,将其挂载到 cesiumContainer
元素上。Cesium.ScreenSpaceEventHandler
创建一个事件处理器。setInputAction
方法为左键点击事件(Cesium.ScreenSpaceEventType.LEFT_CLICK
)绑定处理函数。viewer.camera.getPickRay
获取鼠标点击位置的射线,再使用 viewer.scene.globe.pick
得到射线与地球表面的交点坐标,最后将其转换为经纬度并输出。setInputAction
方法为鼠标移动事件(Cesium.ScreenSpaceEventType.MOUSE_MOVE
)绑定处理函数。viewer.scene.camera.moveEnd.addEventListener
为相机移动结束事件绑定处理函数。这些示例展示了在 Cesium 中如何处理常见的交互和事件,你可以根据需求添加更多的交互逻辑。
在使用 Cesium 开发地理信息系统(GIS)应用时,性能优化是至关重要的,它能显著提升用户体验,特别是在处理大规模地理数据和复杂场景时。以下从多个方面介绍 Cesium 性能优化的方法:
ImageryLayer
和 TerrainProvider
等类的相关方法,实现数据的分块加载和动态更新。localStorage
或 IndexedDB
)对已加载的数据进行缓存,当用户再次访问相同数据时,直接从本地缓存中读取,减少网络请求。通过以上多个方面的优化,可以显著提升 Cesium 应用的性能,为用户提供更加流畅和高效的使用体验。
下面为你提供一些基于前面提到的性能优化方法的具体 Cesium 案例应用代码及解释。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>Cesium Data Loading Optimizationtitle>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Cesium.js">script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<style>
html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
style>
head>
<body>
<div id="cesiumContainer">div>
<script>
// 初始化 Cesium Viewer
const viewer = new Cesium.Viewer('cesiumContainer');
// 添加影像图层,Cesium 会自动处理分块加载
const imageryProvider = new Cesium.UrlTemplateImageryProvider({
url: 'https://your - tile - server-url/{z}/{x}/{y}.png'
});
viewer.imageryLayers.addImageryProvider(imageryProvider);
script>
body>
html>
此代码借助 UrlTemplateImageryProvider
添加影像图层,Cesium 会依据用户视野范围自动处理分块加载,仅加载当前可见区域的影像瓦片,从而减少数据加载量。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>Cesium Frustum Cullingtitle>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Cesium.js">script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<style>
html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
style>
head>
<body>
<div id="cesiumContainer">div>
<script>
const viewer = new Cesium.Viewer('cesiumContainer');
// 添加多个实体
for (let i = 0; i < 100; i++) {
const longitude = Cesium.Math.toRadians(-100 + i);
const latitude = Cesium.Math.toRadians(30);
const position = Cesium.Cartesian3.fromRadians(longitude, latitude);
viewer.entities.add({
position: position,
point: {
pixelSize: 10,
color: Cesium.Color.RED
}
});
}
script>
body>
html>
代码中添加了多个点实体,Cesium 会自动进行视锥体剔除,仅渲染位于相机视锥体内的实体,避免对不可见实体进行渲染计算。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>Cesium Event Throttletitle>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Cesium.js">script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.95/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<style>
html,
body,
#cesiumContainer {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
style>
head>
<body>
<div id="cesiumContainer">div>
<script>
const viewer = new Cesium.Viewer('cesiumContainer');
// 节流函数
function throttle(func, delay) {
let timer = null;
return function () {
if (!timer) {
func.apply(this, arguments);
timer = setTimeout(() => {
timer = null;
}, delay);
}
};
}
const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
const throttledMouseMove = throttle(function (movement) {
const ray = viewer.camera.getPickRay(movement.endPosition);
const cartesian = viewer.scene.globe.pick(ray, viewer.scene);
if (cartesian) {
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
console.log(`Mouse moved to longitude: ${longitude}, latitude: ${latitude}`);
}
}, 200);
handler.setInputAction(throttledMouseMove, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
script>
body>
html>
该代码定义了一个节流函数 throttle
,用于处理鼠标移动事件。通过节流,避免了鼠标移动事件过于频繁触发处理函数,减少了不必要的计算,提升了性能。