Cesium 自定义纹理实战指南
在 Cesium 中,纹理是提升视觉真实感的关键。从简单的图片贴图到动态纹理,我将为您梳理完整的自定义纹理方案。
一、基础纹理:图片贴图
1. Entity 纹理(最简单)
javascript
// 1.1 在球面贴纹理
const polygon = viewer.entities.add({
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray([
-110.0, 30.0,
-100.0, 30.0,
-100.0, 40.0,
-110.0, 40.0
]),
material: new Cesium.ImageMaterialProperty({
image: './textures/grass.jpg', // 图片路径
repeat: new Cesium.Cartesian2(4, 4) // 重复次数
})
}
});
// 1.2 3D模型纹理
const model = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(-123.0745, 44.0505, 100.0),
model: {
uri: './models/CesiumAir.glb',
// 纹理在模型内部定义
}
});
2. Primitive 纹理(高性能)
javascript
// 创建带纹理的矩形
const rectangle = Cesium.Rectangle.fromDegrees(-110.0, 30.0, -100.0, 40.0);
const geometry = new Cesium.RectangleGeometry({
rectangle: rectangle,
height: 0,
vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL
});
const instance = new Cesium.GeometryInstance({
geometry: geometry,
id: 'textured-rectangle'
});
const primitive = new Cesium.Primitive({
geometryInstances: instance,
appearance: new Cesium.MaterialAppearance({
material: new Cesium.Material({
fabric: {
type: 'Image', // 使用内置 Image 材质
uniforms: {
image: './textures/water.jpg',
repeat: new Cesium.Cartesian2(8, 4)
}
}
})
})
});
viewer.scene.primitives.add(primitive);
二、材质系统:Fabric 自定义材质
Fabric 是 Cesium 的材质定义语言,支持 GLSL 片段着色器。
1. 基础 Fabric 材质
javascript
// 创建简单的纹理材质
const customMaterial = new Cesium.Material({
fabric: {
type: 'CustomTexture',
uniforms: {
image: './textures/brick.jpg',
speed: 1.0,
time: 0.0
},
source: `
czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);
// 获取纹理坐标
vec2 st = materialInput.st;
// 采样纹理
vec4 color = texture2D(image, st);
// 修改颜色
color.rgb *= 0.8; // 变暗
material.diffuse = color.rgb;
material.alpha = color.a;
return material;
}
`
}
});
// 应用到多边形
polygon.polygon.material = customMaterial;
2. 动态纹理(随时间变化)
javascript
// 动态水流纹理
const waterMaterial = new Cesium.Material({
fabric: {
type: 'AnimatedWater',
uniforms: {
baseWaterColor: new Cesium.Color(0.2, 0.3, 0.6, 0.8),
blendColor: new Cesium.Color(0.0, 0.5, 0.8, 0.5),
time: 0.0
},
source: `
uniform vec4 baseWaterColor;
uniform vec4 blendColor;
uniform float time;
czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st * 10.0; // 缩放纹理
// 创建波纹效果
float wave = sin(st.x * 3.0 + time) * 0.1
+ sin(st.y * 2.0 + time * 1.5) * 0.1;
// 混合颜色
vec4 color = mix(baseWaterColor, blendColor, wave);
material.diffuse = color.rgb;
material.alpha = color.a;
return material;
}
`
}
});
// 更新 uniform
viewer.scene.postRender.addEventListener(() => {
waterMaterial.uniforms.time = Date.now() / 1000.0;
});
三、Canvas 动态纹理
用 Canvas 2D 生成动态纹理,适合实时数据可视化。
javascript
// 1. 创建 Canvas
const canvas = document.createElement('canvas');
canvas.width = 512;
canvas.height = 512;
const ctx = canvas.getContext('2d');
// 2. 绘制动态内容
function updateCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制网格
ctx.strokeStyle = '#00ff00';
ctx.lineWidth = 2;
for (let i = 0; i <= 20; i++) {
const pos = i * 20;
ctx.beginPath();
ctx.moveTo(0, pos);
ctx.lineTo(canvas.width, pos);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(pos, 0);
ctx.lineTo(pos, canvas.height);
ctx.stroke();
}
// 绘制动态点
const time = Date.now() / 1000;
const x = (Math.sin(time) * 0.5 + 0.5) * canvas.width;
const y = (Math.cos(time) * 0.5 + 0.5) * canvas.height;
ctx.fillStyle = '#ff0000';
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI * 2);
ctx.fill();
}
// 3. 创建纹理材质
const canvasMaterial = new Cesium.Material({
fabric: {
type: 'CanvasTexture',
uniforms: {
canvas: canvas,
repeat: new Cesium.Cartesian2(4, 4)
},
source: `
uniform sampler2D canvas;
uniform vec2 repeat;
czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = fract(materialInput.st * repeat);
vec4 color = texture2D(canvas, st);
material.diffuse = color.rgb;
material.alpha = color.a;
return material;
}
`
}
});
// 4. 动态更新
viewer.scene.postRender.addEventListener(() => {
updateCanvas();
// 通知 Cesium 纹理已更新
canvasMaterial.uniforms.canvas.needsUpdate = true;
});
// 5. 应用材质
const entity = viewer.entities.add({
polygon: {
hierarchy: Cesium.Cartesian3.fromDegreesArray([...]),
material: canvasMaterial
}
});
四、视频纹理
将视频作为动态纹理,适合监控、实况等场景。
javascript
// 1. 创建视频元素
const video = document.createElement('video');
video.src = './videos/earth.mp4';
video.crossOrigin = 'anonymous';
video.loop = true;
video.muted = true; // 自动播放需要静音
video.play();
// 2. 创建视频纹理材质
const videoMaterial = new Cesium.Material({
fabric: {
type: 'Video',
uniforms: {
video: video,
repeat: new Cesium.Cartesian2(1, 1),
color: new Cesium.Color(1.0, 1.0, 1.0, 1.0)
}
}
});
// 3. 应用到几何体
const videoRectangle = viewer.entities.add({
rectangle: {
coordinates: Cesium.Rectangle.fromDegrees(-120, 30, -110, 40),
material: videoMaterial
}
});
// 4. 控制视频播放
document.getElementById('play').addEventListener('click', () => video.play());
document.getElementById('pause').addEventListener('click', () => video.pause());
五、WebGL 高级纹理
1. 渲染到纹理(Render to Texture)
javascript
// 创建帧缓冲纹理
const framebufferTexture = new Cesium.Texture({
context: viewer.scene.context,
width: 1024,
height: 1024
});
// 自定义渲染通道
const customTexture = new Cesium.Material({
fabric: {
type: 'RenderTexture',
uniforms: {
texture: framebufferTexture
},
source: `
uniform sampler2D texture;
czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);
// 应用后处理效果
vec4 color = texture2D(texture, materialInput.st);
color.rgb = 1.0 - color.rgb; // 反色效果
material.diffuse = color.rgb;
material.alpha = color.a;
return material;
}
`
}
});
2. 噪声纹理(Procedural Texture)
javascript
// 创建柏林噪声纹理
const noiseMaterial = new Cesium.Material({
fabric: {
type: 'ProceduralNoise',
uniforms: {
scale: 10.0,
time: 0.0
},
source: `
// 柏林噪声函数
float noise(vec2 p) {
return sin(p.x * 10.0) * sin(p.y * 10.0);
}
czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);
vec2 st = materialInput.st * scale;
float n = noise(st + time);
// 根据噪声值生成颜色
vec3 color = vec3(0.2, 0.5, 0.8) * (n * 0.5 + 0.5);
material.diffuse = color;
material.alpha = 1.0;
return material;
}
`
}
});
六、纹理坐标与映射
1. 自定义 UV 坐标
javascript
// 创建自定义几何体时指定纹理坐标
const geometry = new Cesium.PolygonGeometry({
polygonHierarchy: new Cesium.PolygonHierarchy(
Cesium.Cartesian3.fromDegreesArray([...])
),
vertexFormat: Cesium.VertexFormat.POSITION_AND_NORMAL,
stRotation: Cesium.Math.toRadians(45), // 旋转纹理
textureCoordinates: new Cesium.PolygonPipeline.TEXCOORD_DEFAULT
});
// 手动指定每个顶点的 UV
const positions = [...]; // 顶点坐标
const sts = [...]; // 对应的 UV 坐标 [u1, v1, u2, v2, ...]
const geometryWithST = new Cesium.Geometry({
attributes: {
position: new Cesium.GeometryAttribute({
componentDatatype: Cesium.ComponentDatatype.DOUBLE,
componentsPerAttribute: 3,
values: positions
}),
st: new Cesium.GeometryAttribute({ // 纹理坐标属性
componentDatatype: Cesium.ComponentDatatype.FLOAT,
componentsPerAttribute: 2,
values: sts
})
},
// ... 其他几何信息
});
2. 立方体贴图(环境映射)
javascript
// 创建立方体贴图材质
const cubeMapMaterial = new Cesium.Material({
fabric: {
type: 'CubeMap',
uniforms: {
cubeMap: new Cesium.CubeMap({
positiveX: './textures/px.jpg',
negativeX: './textures/nx.jpg',
positiveY: './textures/py.jpg',
negativeY: './textures/ny.jpg',
positiveZ: './textures/pz.jpg',
negativeZ: './textures/nz.jpg'
})
}
}
});
// 应用到球体
const sphere = viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(0, 0, 1000000),
ellipsoid: {
radii: new Cesium.Cartesian3(500000, 500000, 500000),
material: cubeMapMaterial
}
});
七、性能优化技巧
1. 纹理压缩
javascript
// 使用压缩纹理格式
const compressedTexture = new Cesium.Texture({
context: scene.context,
source: './textures/compressed.ktx', // KTX/DDS 格式
width: 1024,
height: 1024
});
// WebGL 2.0 支持更多压缩格式
if (scene.context.webgl2) {
// 可以使用 ASTC, ETC2 等压缩
}
2. 纹理图集(Atlas)
javascript
// 合并多个小纹理为一张大图
const atlasMaterial = new Cesium.Material({
fabric: {
type: 'Atlas',
uniforms: {
image: './textures/atlas.png',
atlasSize: new Cesium.Cartesian2(4, 4), // 4x4 网格
tileIndex: 5 // 使用第5个格子
},
source: `
uniform sampler2D image;
uniform vec2 atlasSize;
uniform float tileIndex;
czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);
// 计算纹理坐标
vec2 tileSize = 1.0 / atlasSize;
float tileX = mod(tileIndex, atlasSize.x);
float tileY = floor(tileIndex / atlasSize.x);
vec2 tileOffset = vec2(tileX, tileY) * tileSize;
// 映射到对应的小格子
vec2 st = materialInput.st * tileSize + tileOffset;
vec4 color = texture2D(image, st);
material.diffuse = color.rgb;
material.alpha = color.a;
return material;
}
`
}
});
3. Mipmap 与过滤
javascript
// 创建带 Mipmap 的纹理
const textureWithMipmaps = new Cesium.Texture({
context: scene.context,
source: image,
pixelFormat: Cesium.PixelFormat.RGBA,
sampler: new Cesium.Sampler({
minificationFilter: Cesium.TextureMinificationFilter.LINEAR_MIPMAP_LINEAR,
magnificationFilter: Cesium.TextureMagnificationFilter.LINEAR,
wrapS: Cesium.TextureWrap.REPEAT,
wrapT: Cesium.TextureWrap.REPEAT
})
});
八、实战案例:热力图纹理
javascript
// 生成热力图纹理
function createHeatmapTexture(points, width = 256, height = 256) {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// 创建渐变
const gradient = ctx.createLinearGradient(0, 0, width, 0);
gradient.addColorStop(0, 'blue');
gradient.addColorStop(0.5, 'yellow');
gradient.addColorStop(1, 'red');
// 绘制点
points.forEach(point => {
const x = point.x * width;
const y = point.y * height;
const radius = point.intensity * 20;
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fillStyle = gradient;
ctx.fill();
});
// 添加模糊效果
ctx.filter = 'blur(10px)';
ctx.drawImage(canvas, 0, 0);
return canvas;
}
// 应用热力图纹理
const heatmapCanvas = createHeatmapTexture([
{x: 0.2, y: 0.3, intensity: 0.8},
{x: 0.6, y: 0.5, intensity: 0.5}
]);
const heatmapMaterial = new Cesium.Material({
fabric: {
type: 'Heatmap',
uniforms: {
image: heatmapCanvas,
opacity: 0.7
}
}
});
九、调试与监控
javascript
// 1. 纹理内存监控
scene.postRender.addEventListener(() => {
const bytes = scene.context.texture.getTextureBytes();
console.log(`纹理内存: ${(bytes / 1024 / 1024).toFixed(2)} MB`);
});
// 2. 纹理数量监控
console.log(`加载的纹理数量: ${scene.context._textures.length}`);
// 3. 手动释放纹理
texture.destroy();
总结
自定义纹理的核心要点:
- Entity 材质:适合简单场景,API 简洁
- Fabric 材质:功能最强大,支持 GLSL 着色器
- Canvas 纹理:适合动态 2D 内容
- 视频纹理:实时流媒体支持
- 性能关键:压缩格式、纹理图集、Mipmap
选择建议:
- 静态贴图 →
ImageMaterialProperty - 动态效果 →
Fabric + GLSL - 实时数据 →
Canvas 纹理 - 视频流 →
Video 材质 - 高性能需求 → 纹理压缩 + 图集