Cesium 自定义纹理

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

总结

自定义纹理的核心要点

  1. Entity 材质:适合简单场景,API 简洁
  2. Fabric 材质:功能最强大,支持 GLSL 着色器
  3. Canvas 纹理:适合动态 2D 内容
  4. 视频纹理:实时流媒体支持
  5. 性能关键:压缩格式、纹理图集、Mipmap

选择建议

  • 静态贴图 → ImageMaterialProperty
  • 动态效果 → Fabric + GLSL
  • 实时数据 → Canvas 纹理
  • 视频流 → Video 材质
  • 高性能需求 → 纹理压缩 + 图集
相关推荐
鹏程十八少1 小时前
9. 2026金三银四 面试官问不垮:Java VS Android 设计模式 16 讲
前端·后端·面试
Csvn1 小时前
前端监控体系
前端
张风捷特烈1 小时前
状态管理大乱斗#04 | Riverpod 源码评析 (上) - 核心架构
android·前端·flutter
djk88881 小时前
html table 分组合并 与导出分组后的数据
前端·html
FlyWIHTSKY1 小时前
router-viiew没有滚动条,如何修复
前端·vue.js·elementui
jinanwuhuaguo2 小时前
暗黑演化——记忆投毒、认知篡改与“数字精神分裂症”的安全悖论(第十四篇)
前端·人工智能·安全·重构·openclaw
靳向阳2 小时前
【无标题】
前端·javascript·vue.js
存在的五月雨2 小时前
uniapp 一些组件的使用
java·前端·uni-app
涵涵(互关)2 小时前
GoView各项目文件中的相关语法
前端·vue.js·typescript