Cesium快速入门到精通系列教程十六:材质系统

Cesium 1.95 的材质系统是其可视化能力的核心,提供了丰富的材质类型和高度自定义能力。

一、材质基础概念

1. 材质组成要素

​​颜色/纹理​​:定义基础外观

​​光照响应​​:漫反射、镜面反射等

​​透明度​​:alpha通道控制

​​特殊效果​​:发光、法线贴图等

2、核心组件

​​MaterialProperty​​:材质属性基类

​​Material​​:运行时材质实例

​​Fabric Schema​​:材质定义规范

​​Shader Pipeline​​:着色器处理管线

3、渲染流程

材质定义 → 材质编译 → Uniform绑定 → 着色器生成 → 渲染执行

4、材质类型

基础材质类型

颜色材质 (Color)‌:通过CSS颜色字符串或 Cesium.Color 设置颜色,支持透明度调节。 ‌

图像材质 (Image)‌:使用纹理图像(如png)作为表面纹理。 ‌

棋盘材质 (Checkerboard)、 斑马线材质 (Stripe)、 网格材质 (Grid)‌:通过预设图案实现纹理效果。 ‌

自定义材质

通过 GLSL着色器 编写自定义材质,需定义 Fabric结构体 ,包含type、uniforms(统一值)和source(着色器代码)。例如:

复制代码
fabric : {  
  type : 'Color',  
  uniforms : {  
    color : 'rgba(255, 255, 255, 0.5)'  
  }  
}  

或直接使用 Cesium内置函数 如czm_getMaterial动态计算材质属性。

材质类型(type)
  • 标识材质的唯一名称
  • 可用于后续材质引用
统一变量(uniforms)
  • 定义材质参数,可在运行时修改
  • 支持的类型包括:颜色、数字、纹理等
着色器代码(source)
  • 使用GLSL编写
  • 可以访问Cesium提供的实用函数(czm_*)
  • 必须包含材质输出结构
Cesium 1.95 中的新特性
  • 改进的材质性能:优化了材质系统的渲染性能
  • 增强的纹理支持:更好的纹理压缩和流式加载
  • 扩展的uniform类型:支持更多类型的uniform变量
  • 调试工具改进:更好的材质调试支持
常用内置函数
  • Cesium 提供了许多有用的GLSL函数:
  • czm_getDefaultMaterial() - 获取默认材质
  • czm_materialInput - 包含材质输入信息(st, normal, position等)
  • czm_pixelToTextureCoordinates() - 像素坐标转纹理坐标
  • czm_eyeToWindowCoordinates() - 眼空间坐标转窗口坐标

5. 材质应用场景

实体(Entity)外观

通过:ml-search[materialProperty]设置材质(如Color.fromCssColorString)。

3D Tiles样式化

自定义图元(Primitive)

使用:ml-search[Material类]直接修改几何体材质,需定义 Fabric JSON 。 ‌

二、使用方式:

1. 创建与应用

应用于实体

复制代码
const entity = viewer.entities.add({
    polygon: {
        hierarchy: Cesium.Cartesian3.fromDegreesArray([...]),
        material: new Cesium.ColorMaterialProperty(Cesium.Color.RED)
    }
});

完整实例

复制代码
    const viewer = new Cesium.Viewer("cesiumContainer");

    viewer.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(116.3, 39.9, 100000),
      orientation: {
        heading: Cesium.Math.toRadians(0),
        pitch: Cesium.Math.toRadians(-60),
        roll: 0.0,
      },
    });

    viewer.entities.add({
      polygon: {
        hierarchy: Cesium.Cartesian3.fromDegreesArray([
          116.2, 39.8, 116.4, 39.8, 116.4, 40.0, 116.2, 40.0,
        ]),
        material: new Cesium.Color.BLUE.withAlpha(0.7),
        height: 0,
        extrudedHeight: 10000
      },
    });

应用于图元

复制代码
const primitive = new Cesium.Primitive({
    geometryInstances: new Cesium.GeometryInstance({
        geometry: new Cesium.RectangleGeometry({...})
    }),
    appearance: new Cesium.MaterialAppearance({
        material: new Cesium.Material({
            fabric: {
                type: 'Color',
                uniforms: {
                    color: Cesium.Color.BLUE
                }
            }
        })
    })
});

完整实例

复制代码
    const viewer = new Cesium.Viewer("cesiumContainer");

    viewer.camera.setView({
      destination: Cesium.Cartesian3.fromDegrees(116.3, 39.9, 100000),
      orientation: {
        heading: Cesium.Math.toRadians(0),
        pitch: Cesium.Math.toRadians(-60),
        roll: 0.0,
      },
    });

    const positions = Cesium.Cartesian3.fromDegreesArray([
      117.3,
      38.9, // 北京大致位置
      118.4,
      37.9,
      116.4,
      36.8,
      119.3,
      39.8,
      115.3,
      37.9, // 闭合多边形
    ]);

    // 2. 创建 GeometryInstance
    const geometryInstance = new Cesium.GeometryInstance({
      geometry: new Cesium.PolygonGeometry({
        polygonHierarchy: new Cesium.PolygonHierarchy(positions),
        perPositionHeight: false, // 如果为 true,则 positions 中的每个点可以有不同的高度
        vertexFormat: Cesium.VertexFormat.POSITION_AND_ST, // 用于纹理等,如果不需要可以简化
      }),
    });

    // 3. 创建 Primitive 并添加到 Viewer 的 PrimitiveCollection 中
    const primitive = new Cesium.Primitive({
      geometryInstances: geometryInstance,
      appearance: new Cesium.MaterialAppearance({
        material: new Cesium.Material({
          fabric: {
            type: "Color",
            uniforms: {
              color: Cesium.Color.BLUE,
            },
          },
        }),
      }),
    });

    viewer.scene.primitives.add(primitive);

2. 动态材质更新

复制代码
// 使用CallbackProperty实现动态效果
const pulseMaterial = new Cesium.ColorMaterialProperty(
    new Cesium.CallbackProperty(function(time) {
        const t = Cesium.JulianDate.secondsDifference(time, startTime);
        const pulse = 0.5 + 0.5 * Math.sin(t);
        return Cesium.Color.RED.withAlpha(pulse);
    }, false)
);

// 直接更新uniforms
customMaterial.uniforms.speed = 2.0;

三、内置材质类型详解

1. ColorMaterialProperty (纯色材质)

复制代码
// 创建红色材质
const redMaterial = new Cesium.ColorMaterialProperty(
    Cesium.Color.RED.withAlpha(0.8) // 带80%透明度
);

// 动态变色示例
const dynamicColor = new Cesium.ColorMaterialProperty(
    new Cesium.CallbackProperty(function(time) {
        return Cesium.Color.fromHsl(
            Cesium.JulianDate.secondsDifference(time, startTime) * 0.1 % 1.0,
            1.0, 0.5
        );
    }, false)
);

2. ImageMaterialProperty (图像材质)

复制代码
const imageMaterial = new Cesium.ImageMaterialProperty({
    image: 'textures/brick.jpg',  // 图片路径/URL
    repeat: new Cesium.Cartesian2(4, 4), // 横向和纵向重复次数
    color: Cesium.Color.WHITE.withAlpha(0.9), // 颜色叠加
    transparent: true // 启用透明度
});

// 动态切换纹理
imageMaterial.image = new Cesium.CallbackProperty(function(time) {
    return time.getSeconds() % 2 === 0 ? 
        'textures/day.jpg' : 'textures/night.jpg';
}, false);

3. 图案材质组

(1) CheckerboardMaterialProperty (棋盘格)

复制代码
const checkerboard = new Cesium.CheckerboardMaterialProperty({
    evenColor: Cesium.Color.WHITE,
    oddColor: Cesium.Color.BLACK,
    repeat: new Cesium.Cartesian2(10, 10) // 10x10重复
});

(2) StripeMaterialProperty (条纹)

复制代码
const stripeMaterial = new Cesium.StripeMaterialProperty({
    orientation: Cesium.StripeOrientation.HORIZONTAL, // 或VERTICAL
    evenColor: Cesium.Color.BLUE,
    oddColor: Cesium.Color.CYAN,
    repeat: 16, // 重复次数
    orientation: Cesium.StripeOrientation.VERTICAL, // 方向
    offset: 0.2 // 偏移量(0-1)
});

(3) GridMaterialProperty (网格)

复制代码
const gridMaterial = new Cesium.GridMaterialProperty({
    color: Cesium.Color.YELLOW,
    cellAlpha: 0.1, // 单元格透明度
    lineCount: new Cesium.Cartesian2(8, 8), // 行列数
    lineThickness: new Cesium.Cartesian2(1.0, 1.0), // 线宽
    lineOffset: new Cesium.Cartesian2(0.0, 0.0) // 偏移
});

4. 折线专用材质

(1) PolylineGlowMaterialProperty (发光折线)

复制代码
const glowLine = new Cesium.PolylineGlowMaterialProperty({
    color: Cesium.Color.BLUE,
    glowPower: 0.3, // 发光强度(0-1)
    taperPower: 0.5 // 锥度效果
});

(2) PolylineOutlineMaterialProperty (轮廓折线)

复制代码
const outlineLine = new Cesium.PolylineOutlineMaterialProperty({
    color: Cesium.Color.GREEN,
    outlineWidth: 3, // 轮廓宽度(像素)
    outlineColor: Cesium.Color.BLACK
});

(3) PolylineArrowMaterialProperty(折线箭头材质)

复制代码
new Cesium.PolylineArrowMaterialProperty(Cesium.Color.PURPLE);

(4) PolylineDashMaterialProperty(折线虚线材质)

复制代码
new Cesium.PolylineDashMaterialProperty({
            color: Cesium.Color.CYAN,
            dashLength: 16.0, // 虚线长度
            dashPattern: 255 // 虚线模式(二进制)
        })

四、高级材质技术

在 Cesium 1.95 中,创建自定义材质主要有两种方式:

1. 使用 Fabric 规范

复制代码
const customMaterial1 = new Cesium.Material({
    fabric: {
      type: "Water",
      uniforms: {
        specularMap: "../images/earthspec1k.jpg",
        normalMap: Cesium.buildModuleUrl("Assets/Textures/waterNormals.jpg"),
        frequency: 10000.0,
        animationSpeed: 0.01,
        amplitude: 1.0,
      },
    },
});

const customMaterial2 = new Cesium.Material({
          fabric: {
            type: "Image",
            uniforms: {
              image: "/Cesium_Logo_Color.jpg",
            },
          },
});

2、使用 GLSL 着色器

复制代码
var material = new Cesium.Material({
    fabric: {
        type: 'CustomShaderMaterial',
        uniforms: {
            texture: new Cesium.TextureUniform({
                url: 'path/to/texture.jpg'
            }),
            time: 0.0
        },
        source: `
            uniform sampler2D texture;
            uniform float time;
            
            void main() {
                // 自定义着色器代码
                vec2 st = czm_materialInput.st;
                vec4 color = texture2D(texture, st);
                gl_FragColor = color * abs(sin(time));
            }
        `
    }
});

3. PBR材质配置

复制代码
const pbrMaterial = new Cesium.Material({
    fabric: {
        type: 'PBR',
        uniforms: {
            baseColorTexture: 'textures/albedo.jpg',
            normalTexture: 'textures/normal.jpg',
            roughnessTexture: 'textures/roughness.jpg',
            metallicTexture: 'textures/metallic.jpg',
            ambientOcclusionTexture: 'textures/ao.jpg',
            baseColorFactor: [1.0, 1.0, 1.0, 1.0],
            metallicFactor: 0.5,
            roughnessFactor: 0.3
        }
    }
});

4. 材质混合

复制代码
const compositeMaterial = new Cesium.Material({
    fabric: {
        type: 'Composite',
        materials: {
            base: { type: 'Color', uniforms: { color: Cesium.Color.BLUE } },
            overlay: { type: 'Image', uniforms: { image: 'textures/overlay.png' } }
        },
        components: {
            diffuse: "mix(base.diffuse, overlay.diffuse, overlay.alpha)",
            alpha: "base.alpha * overlay.alpha"
        }
    }
});

五、性能优化技巧

​​1、纹理优化​​:

复制代码
// 使用压缩纹理
viewer.scene.primitives.add(
    new Cesium.createTextureAtlas({
        images: ['textures/atlas.png'],
        context: viewer.scene.context,
        pixelFormat: Cesium.PixelFormat.RGBA,
        compression: Cesium.TextureCompression.WEBP // 或PVRTC
    })
);

2、​​材质复用​​:

复制代码
// 创建材质池
const materialCache = {};

function getMaterial(type, options) {
    const key = JSON.stringify([type, options]);
    if (!materialCache[key]) {
        materialCache[key] = new Cesium[type + 'MaterialProperty'](options);
    }
    return materialCache[key];
}

3、动态更新策略​​:

复制代码
// 使用requestAnimationFrame优化频繁更新
function updateMaterial() {
    if (!viewer.scene.isDestroyed()) {
        material.uniforms.time = Date.now() * 0.001;
        Cesium.requestAnimationFrame(updateMaterial);
    }
}

六、常见问题解决方案

​​1、材质不显示​​:

  • 检查图片路径是否正确
  • 验证颜色值是否有效(0-255或0-1范围)
  • 确认实体或图元的show属性为true

​​2、性能问题​​:

复制代码
// 诊断材质性能
viewer.scene.debugShowFramesPerSecond = true;
Cesium.Material._materialCache.count; // 查看材质缓存数量

3、​​移动端适配​​:

复制代码
// 根据设备能力降级材质
const useSimpleMaterial = !Cesium.FeatureDetection.supportsWebGL2(viewer.scene.context);
entity.material = useSimpleMaterial ? simpleMaterial : advancedMaterial;

七、最佳实践总结

​​材质设计原则​​:

  • 重用材质:尽可能重用材质实例而不是频繁创建新实例
  • 优化uniform更新:避免每帧更新所有uniform
  • 使用内置函数:优先使用Cesium提供的GLSL函数
  • 测试性能:复杂材质可能影响性能,需进行性能测试

​​性能黄金法则​​:

复制代码
// 批处理相似材质
const batchTable = new Cesium.BatchTable();
batchTable.addTexture('diffuse', texture1);
batchTable.addTexture('normal', texture2);

// 使用实例化渲染
const instances = new Cesium.GeometryInstance({
    geometry: geometry,
    attributes: {
        color: Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
    },
    id: 'instance1'
});

​​内存管理​​:

复制代码
// 及时释放资源
viewer.scene.primitives.remove(primitive);
material.destroy();
texture.destroy();

// 监控内存使用
Cesium.MemoryTracker.getStatistics();

Cesium 1.95的材质系统通过这套完整的工具链,可以构建从简单可视化到影视级渲染的各种效果。掌握这些技术后,您将能够创建出既美观又高性能的3D地理应用。

相关推荐
青山Coding4 分钟前
Cesium基础(七):Camera(相机)常用的API及飞行漫游
gis·cesium
YGY_Webgis糕手之路3 小时前
Cesium 快速入门(十) JulianDate(儒略日期)详解
前端·gis·cesium
YGY_Webgis糕手之路4 小时前
Cesium 快速入门(三)Viewer:三维场景的“外壳”
前端·gis·cesium
YGY_Webgis糕手之路1 天前
Cesium 快速入门(十二)数据加载详解
前端·gis·cesium
YGY_Webgis糕手之路1 天前
Cesium 快速入门(十三)事件系统
前端·gis·cesium
YGY_Webgis糕手之路1 天前
Cesium 快速入门(六)实体类型介绍
前端·gis·cesium
YGY_Webgis糕手之路1 天前
Cesium 快速入门(四)相机控制完全指南
前端·gis·cesium
伟大的兔神4 天前
cesium绘制动态柱状图
前端·gis·cesium
整点可乐4 天前
cesium实现鹰眼图
javascript·cesium