首页
/ Cesium中自定义材质与外观的GLSL注入实践

Cesium中自定义材质与外观的GLSL注入实践

2025-05-16 09:44:37作者:盛欣凯Ernestine

引言

在三维地理可视化领域,Cesium作为一款强大的开源JavaScript库,为开发者提供了丰富的图形渲染能力。其中,Material(材质)和MaterialAppearance(材质外观)系统是Cesium实现自定义渲染效果的核心机制。本文将深入探讨如何通过这两个系统向Primitive(图元)注入自定义GLSL代码,实现独特的视觉效果。

基本概念解析

Material系统

Material系统是Cesium中定义物体表面特性的核心组件,它通过Fabric规范来描述材质属性。开发者可以通过定义uniform变量、编写GLSL片段着色器代码等方式,创建完全自定义的材质效果。

MaterialAppearance系统

MaterialAppearance则提供了更底层的控制能力,允许开发者同时自定义顶点着色器和片段着色器。这使得我们能够控制几何体的顶点变换和最终像素着色两个关键阶段。

实现步骤详解

1. 创建几何体实例

首先需要创建一个几何体实例作为渲染对象。以立方体为例:

const boxGeometry = Cesium.BoxGeometry.fromDimensions({
    dimensions: new Cesium.Cartesian3(25000.0, 25000.0, 25000.0),
});

const boxInstance = new Cesium.GeometryInstance({
    geometry: boxGeometry,
    modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(
        Cesium.Cartesian3.fromDegrees(113, 33, 1250)
    ),
    attributes: {
        color: Cesium.ColorGeometryInstanceAttribute.fromColor(
            Cesium.Color.WHITE
        )
    }
});

2. 定义自定义材质

创建自定义材质时,关键是在fabric.source中编写GLSL片段着色器代码:

const customMaterial = new Cesium.Material({
    translucent: false,
    fabric: {
        type: 'CustomBoxShader',
        uniforms: {
            time: 0.0,
        },
        source: 
        "uniform float time;\n" +
        "czm_material czm_getMaterial(czm_materialInput materialInput) {\n" +
        "    czm_material material = czm_getDefaultMaterial(materialInput);\n" +
        "    material.diffuse  = vec3(1.0, 1.0, 0.0); \n" +
        "    material.alpha  = 1.0;\n" +
        "    return material;\n" +
        "}"
    }
});

3. 创建材质外观

MaterialAppearance允许我们完全自定义顶点和片段着色器:

const appearance = new Cesium.MaterialAppearance({
    material: customMaterial,
    flat: false,
    faceForward: true,
    translucent: true,
    closed: true,
    vertexShaderSource: 
        "in vec3 position3DHigh;\n" +
        "in vec3 position3DLow;\n" +
        "in vec3 normal;\n" +
        "in vec2 st;\n" +
        "in float batchId;\n" +
        "out vec3 v_positionEC;\n" +
        "out vec3 v_normalEC;\n" +
        "out vec2 v_st;\n" +
        "uniform float testAppUniform;\n" +
        "void main()\n" +
        "{\n" +
        "    vec4 p = czm_computePosition();\n" +
        "    v_positionEC = (czm_modelViewRelativeToEye * p).xyz * testAppUniform; \n" +
        "    v_normalEC = czm_normal * normal; \n" +
        "    v_st = st;\n" +
        "    gl_Position = czm_modelViewProjectionRelativeToEye * p;\n" +
        "}\n",
    fragmentShaderSource: 
        "in vec3 v_positionEC;\n" +
        "in vec3 v_normalEC;\n" +
        "in vec2 v_st;\n" +
        "uniform vec4 testAppColorUniform;\n"+
        "void main()\n" +
        "{\n" +
        "    vec3 positionToEyeEC = -v_positionEC;\n" +
        "    vec3 normalEC = normalize(v_normalEC);\n" +
        "#ifdef FACE_FORWARD\n" +
        "    normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);\n" +
        "#endif\n" +
        "    czm_materialInput materialInput;\n" +
        "    materialInput.normalEC = normalEC;\n" +
        "    materialInput.positionToEyeEC = positionToEyeEC;\n" +
        "    materialInput.st = v_st;\n" +
        "    czm_material material = czm_getMaterial(materialInput);\n" +
        "#ifdef FLAT\n" +
        "    out_FragColor = vec4(material.diffuse + material.emission, material.alpha);\n" +
        "#else\n" +
        "    out_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);\n" +
        "#endif\n" +
        "    out_FragColor.rgb = out_FragColor.rgb * testAppColorUniform.rgb; \n" +
        "    out_FragColor.a = testAppColorUniform.a; \n" +
        "}\n",
    materialCacheKey: "my-box-material-appearance",
});

4. 设置uniform变量

为材质和外观设置uniform变量是实现动态效果的关键:

let color = new Cesium.Color(1.0, 1.0, 0.0, 1.0);
appearance.uniforms = {
    testAppUniform: 1.0, 
    testAppColorUniform: color, 
}; 

5. 创建图元并添加到场景

最后将配置好的几何体实例和外观组合成图元,并添加到场景中:

const primitive = new Cesium.Primitive({
    geometryInstances: boxInstance,
    appearance: appearance,
    asynchronous: false
});

viewer.scene.primitives.add(primitive);

6. 实现动画效果

通过修改uniform变量实现动态效果:

function animate() {
    customMaterial.uniforms.time++;
    const t = customMaterial.uniforms.time;
    appearance.uniforms.testAppColorUniform =
        new Cesium.Color(t * 0.01 % 1.0, t * 0.02 % 1.0, 0.0, t * 0.03 % 1.0);
    requestAnimationFrame(animate);
}
animate();

技术要点与最佳实践

  1. uniform变量更新:直接创建新的Color对象赋值比单独修改属性更可靠。

  2. 着色器代码组织:保持GLSL代码清晰可读,合理使用Cesium内置函数(如czm_*系列)。

  3. 性能考虑:避免在动画循环中频繁创建新对象,尽量复用已有对象。

  4. 调试技巧:可以先使用简单颜色验证着色器基本功能,再逐步添加复杂效果。

常见问题解决方案

  1. 效果不显示:检查uniform变量是否正确定义和赋值,确保着色器编译没有错误。

  2. 性能问题:复杂的GLSL计算可能会影响性能,应考虑优化算法或使用更简单的近似方法。

  3. 光照异常:确保正确处理法线向量,必要时使用FACE_FORWARD宏。

结语

通过Cesium的Material和MaterialAppearance系统,开发者可以充分发挥WebGL的潜力,创造出丰富多样的三维可视化效果。掌握GLSL注入技术后,可以实现从简单的颜色变化到复杂的物理模拟等各种视觉效果。本文介绍的方法为Cesium高级渲染技术提供了基础,开发者可以在此基础上进一步探索更复杂的着色器编程技术。

登录后查看全文
热门项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
22
6
docsdocs
OpenHarmony documentation | OpenHarmony开发者文档
Dockerfile
164
2.05 K
nop-entropynop-entropy
Nop Platform 2.0是基于可逆计算理论实现的采用面向语言编程范式的新一代低代码开发平台,包含基于全新原理从零开始研发的GraphQL引擎、ORM引擎、工作流引擎、报表引擎、规则引擎、批处理引引擎等完整设计。nop-entropy是它的后端部分,采用java语言实现,可选择集成Spring框架或者Quarkus框架。中小企业可以免费商用
Java
8
0
leetcodeleetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
Java
60
16
RuoYi-Vue3RuoYi-Vue3
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
952
560
apintoapinto
基于golang开发的网关。具有各种插件,可以自行扩展,即插即用。此外,它可以快速帮助企业管理API服务,提高API服务的稳定性和安全性。
Go
22
0
openHiTLSopenHiTLS
旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
C
1.01 K
396
HarmonyOS-ExamplesHarmonyOS-Examples
本仓将收集和展示仓颉鸿蒙应用示例代码,欢迎大家投稿,在仓颉鸿蒙社区展现你的妙趣设计!
Cangjie
407
387
ohos_react_nativeohos_react_native
React Native鸿蒙化仓库
C++
199
279
giteagitea
喝着茶写代码!最易用的自托管一站式代码托管平台,包含Git托管,代码审查,团队协作,软件包和CI/CD。
Go
17
0