Cesium 实现 FlowMap
<!-- 引入 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 材质系统)
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 + 噪声函数)
// 定义波浪材质(参考搜索结果)
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控制纹理加载优先级
- 降低

浙公网安备 33010602011771号