Cesium 实现 FlowMap
1 2 3 4 5 6 7 8 9 10 11 12 | <!-- 引入 Cesium 核心库 --> < link href="path/to/cesium/Widgets/widgets.css" rel="stylesheet"> < script src="path/to/cesium/Cesium.js"></ script > < div id="cesiumContainer"></ div > < script > // 初始化 Cesium Viewer const viewer = new Cesium.Viewer('cesiumContainer', { terrainProvider: Cesium.createWorldTerrain(), baseLayerPicker: false }); </ script > |
1. 创建 FlowMap 材质(基于 Fabric 材质系统)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | const flowMapMaterial = new Cesium.Material({ fabric: { type: 'FlowMapMaterial' , uniforms: { flowMapTexture: 'path/to/flowmap.png' , // FlowMap 纹理(RG通道存储流向) normalMap: 'path/to/waterNormals.jpg' , // 法线贴图 speed: 0.5, // 流动速度 time: 0, // 时间变量 amplitude: 1.0 // 波纹振幅 }, source: ` // GLSL 着色器代码 uniform sampler2D flowMapTexture; uniform sampler2D normalMap; uniform float speed; uniform float time; uniform float amplitude; varying vec2 v_st; varying vec3 v_positionEC; void main() { // 采样 FlowMap 获取流向 vec2 flowVector = texture2D(flowMapTexture, v_st).rg * 2.0 - 1.0; flowVector *= speed; // 计算双相位偏移 float phase0 = fract(time * 0.1); float phase1 = fract(time * 0.1 + 0.5); vec2 uvOffset0 = flowVector * phase0; vec2 uvOffset1 = flowVector * phase1; // 混合法线贴图 vec4 normalColor0 = texture2D(normalMap, v_st * 10.0 + uvOffset0); vec4 normalColor1 = texture2D(normalMap, v_st * 10.0 + uvOffset1); float lerpFactor = abs(0.5 - phase0) * 2.0; vec3 normal = mix(normalColor0.rgb, normalColor1.rgb, lerpFactor); // 计算光照与颜色 vec3 positionToEye = normalize(-v_positionEC); vec3 reflected = reflect(positionToEye, normal); vec4 baseColor = vec4(0.1, 0.3, 0.8, 0.8); // 基础水色 gl_FragColor = vec4(baseColor.rgb * (0.5 + dot(normal, positionToEye)), baseColor.a); } ` } }); |
(FlowMap + 噪声函数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | // 定义波浪材质(参考搜索结果) const waterMaterial = new Cesium.Material({ fabric: { type: "DynamicWater" , uniforms: { normalMap: './waterNormals.jpg' , // 法线贴图(需禁用压缩) flowMap: './flowmap.png' , // FlowMap 流向纹理(RG通道) noiseTexture: './noise.png' , // 噪声纹理 speed: 0.3, // 流动速度 amplitude: 5.0, // 波浪振幅 time: 0 // 时间变量 }, source: ` uniform sampler2D normalMap; uniform sampler2D flowMap; uniform sampler2D noiseTexture; uniform float speed; uniform float amplitude; uniform float time; varying vec2 v_st; varying vec3 v_positionEC; // 噪声函数(参考) float noise(vec2 p) { return texture2D(noiseTexture, p * 0.01).r; } void main() { // 采样 FlowMap 获取流向 vec2 flowDir = texture2D(flowMap, v_st).rg * 2.0 - 1.0; flowDir *= speed; // 双相位混合(参考) float phase0 = fract(time * 0.1); float phase1 = fract(time * 0.1 + 0.5); vec2 uvOffset0 = flowDir * phase0; vec2 uvOffset1 = flowDir * phase1; // 动态波浪计算(参考) vec3 normal0 = texture2D(normalMap, v_st * 10.0 + uvOffset0).rgb; vec3 normal1 = texture2D(normalMap, v_st * 10.0 + uvOffset1).rgb; vec3 finalNormal = mix(normal0, normal1, abs(0.5 - phase0) * 2.0); // 噪声增强细节(参考) float waveNoise = noise(v_st * 5.0 + time) * amplitude; finalNormal.xy += vec2(waveNoise * 0.1); // 光照与颜色 vec3 viewDir = normalize(-v_positionEC); float specular = pow(max(dot(viewDir, finalNormal), 0.0), 32.0); gl_FragColor = vec4(mix(vec3(0.1, 0.3, 0.8), vec3(1.0), specular), 0.8); } ` } }); // 创建水面几何体(参考) const waterPrimitive = viewer.scene.primitives.add( new Cesium.GroundPrimitive({ geometryInstances: new Cesium.GeometryInstance({ geometry: new Cesium.RectangleGeometry({ rectangle: Cesium.Rectangle.fromDegrees(-75.0, 40.0, -74.0, 41.0), vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT }) }), appearance: new Cesium.EllipsoidSurfaceAppearance({ material: waterMaterial, aboveGround: true }) }) ); // 更新时间变量(参考) viewer.scene.preUpdate.addEventListener(() => { waterMaterial.uniforms.time = Cesium.JulianDate.now().secondsOfDay; }); |
2. 应用材质到几何体
// 创建平面几何体 const rectangle = Cesium.Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0); const primitive = viewer.scene.primitives.add( new Cesium.Primitive({ geometryInstances: new Cesium.GeometryInstance({ geometry: new Cesium.RectangleGeometry({ rectangle: rectangle, vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT }) }), appearance: new Cesium.EllipsoidSurfaceAppearance({ aboveGround: false }) }) ); // 应用 FlowMap 材质 primitive.appearance.material = flowMapMaterial; // 更新时间变量 viewer.scene.preUpdate.addEventListener(() => { flowMapMaterial.uniforms.time = Cesium.JulianDate.now().secondsOfDay; });
关键参数说明
-
FlowMap 纹理制作:
- 使用工具(如 Houdini Labs 或 Flowmap Painter)生成 RG 通道编码的向量场纹理
- 确保纹理导入时禁用压缩(如设置
disableCompression: true
)
-
动态效果优化:
- 双相位插值:通过
phase0
和phase1
实现无缝循环流动,避免跳跃感 - 法线混合:叠加多层法线贴图增强波纹细节
- 双相位插值:通过
-
性能调优:
- 降低
frequency
(波纹密度)和amplitude
(振幅)以减轻 GPU 负载 - 使用
Cesium.RequestScheduler
控制纹理加载优先级
- 降低
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
2021-02-26 大疆 osgb 转 3dtile 报错