|
@@ -1,5 +1,8 @@
|
|
|
import { actions } from '../store/store.js';
|
|
import { actions } from '../store/store.js';
|
|
|
|
|
|
|
|
|
|
+// 存储水流动画回调,用于清理
|
|
|
|
|
+const flowCallbacks = new Map();
|
|
|
|
|
+
|
|
|
// 验证并清理坐标数据,移除无效坐标和重复点
|
|
// 验证并清理坐标数据,移除无效坐标和重复点
|
|
|
function validateAndCleanCoordinates(coords) {
|
|
function validateAndCleanCoordinates(coords) {
|
|
|
if (!coords || !Array.isArray(coords)) {
|
|
if (!coords || !Array.isArray(coords)) {
|
|
@@ -209,20 +212,163 @@ function loadWaterLayer(url, name, callback) {
|
|
|
id: name || 'water'
|
|
id: name || 'water'
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- const waterMaterial = new Cesium.Material({
|
|
|
|
|
|
|
+ // 创建支持水流方向的材质
|
|
|
|
|
+ const flowWaterMaterial = new Cesium.Material({
|
|
|
fabric: {
|
|
fabric: {
|
|
|
- type: 'Water',
|
|
|
|
|
|
|
+ type: 'FlowWater',
|
|
|
uniforms: {
|
|
uniforms: {
|
|
|
- baseWaterColor: new Cesium.Color(0.0, 0.12, 0.18, 0.9),
|
|
|
|
|
|
|
+ baseWaterColor: new Cesium.Color(0.05, 0.16, 0.16, 0.95),
|
|
|
|
|
+ shallowColor: new Cesium.Color(0.2, 0.28, 0.26, 0.8),
|
|
|
|
|
+ deepColor: new Cesium.Color(0.05, 0.25, 0.25, 0.95),
|
|
|
normalMap: Cesium.buildModuleUrl('Assets/Textures/waterNormals.jpg'),
|
|
normalMap: Cesium.buildModuleUrl('Assets/Textures/waterNormals.jpg'),
|
|
|
- frequency: 10000.0,
|
|
|
|
|
- animationSpeed: 0.003,
|
|
|
|
|
- amplitude: 2,
|
|
|
|
|
- specularIntensity: 5,
|
|
|
|
|
- direction: 45.0 // 水流方向(角度,0-360 度)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ skyTexture: 'src/texture/sky.jpg',
|
|
|
|
|
+ time: 0,
|
|
|
|
|
+ flowSpeed: 0.6,
|
|
|
|
|
+ flowDirection: new Cesium.Cartesian2(-1.0, 0.0),
|
|
|
|
|
+ frequency: 80.0,
|
|
|
|
|
+ specularIntensity: 0.5,
|
|
|
|
|
+ waveStrength: 0.8,
|
|
|
|
|
+ fresnelPower: 1
|
|
|
|
|
+ },
|
|
|
|
|
+ source: `
|
|
|
|
|
+ uniform vec4 baseWaterColor;
|
|
|
|
|
+ uniform vec4 shallowColor;
|
|
|
|
|
+ uniform vec4 deepColor;
|
|
|
|
|
+ uniform sampler2D normalMap;
|
|
|
|
|
+ uniform sampler2D skyTexture;
|
|
|
|
|
+ uniform float time;
|
|
|
|
|
+ uniform float flowSpeed;
|
|
|
|
|
+ uniform vec2 flowDirection;
|
|
|
|
|
+ uniform float frequency;
|
|
|
|
|
+ uniform float specularIntensity;
|
|
|
|
|
+ uniform float waveStrength;
|
|
|
|
|
+ uniform float fresnelPower;
|
|
|
|
|
+
|
|
|
|
|
+ // 简化噪声函数
|
|
|
|
|
+ float hash(vec2 p) {
|
|
|
|
|
+ return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ float noise(vec2 p) {
|
|
|
|
|
+ vec2 i = floor(p);
|
|
|
|
|
+ vec2 f = fract(p);
|
|
|
|
|
+ f = f * f * (3.0 - 2.0 * f);
|
|
|
|
|
+ float a = hash(i);
|
|
|
|
|
+ float b = hash(i + vec2(1.0, 0.0));
|
|
|
|
|
+ float c = hash(i + vec2(0.0, 1.0));
|
|
|
|
|
+ float d = hash(i + vec2(1.0, 1.0));
|
|
|
|
|
+ return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ czm_material czm_getMaterial(czm_materialInput materialInput)
|
|
|
|
|
+ {
|
|
|
|
|
+ czm_material material = czm_getDefaultMaterial(materialInput);
|
|
|
|
|
+
|
|
|
|
|
+ vec2 st = materialInput.st;
|
|
|
|
|
+
|
|
|
|
|
+ // 流动方向
|
|
|
|
|
+ vec2 flowDir = normalize(flowDirection);
|
|
|
|
|
+
|
|
|
|
|
+ // 多层流动UV - 不同速度和方向模拟真实水流
|
|
|
|
|
+ float t = time * flowSpeed;
|
|
|
|
|
+ vec2 flow1 = flowDir * t * 0.1;
|
|
|
|
|
+ vec2 flow2 = flowDir * t * 0.08 + vec2(t * 0.02, 0.0);
|
|
|
|
|
+ vec2 flow3 = -flowDir * t * 0.05;
|
|
|
|
|
+
|
|
|
|
|
+ // 多尺度波纹
|
|
|
|
|
+ vec2 st1 = fract(st * frequency + flow1);
|
|
|
|
|
+ vec2 st2 = fract(st * frequency * 0.6 + flow2 + 0.5);
|
|
|
|
|
+ vec2 st3 = fract(st * frequency * 1.5 + flow3 + 0.25);
|
|
|
|
|
+
|
|
|
|
|
+ // 采样法线贴图 - 增强波纹强度
|
|
|
|
|
+ vec3 n1 = texture2D(normalMap, st1).rgb * 2.0 - 1.0;
|
|
|
|
|
+ vec3 n2 = texture2D(normalMap, st2).rgb * 2.0 - 1.0;
|
|
|
|
|
+ vec3 n3 = texture2D(normalMap, st3).rgb * 2.0 - 1.0;
|
|
|
|
|
+
|
|
|
|
|
+ // 合成法线 - 多层叠加增加细节
|
|
|
|
|
+ vec3 finalNormal = normalize(n1 * 1.0 + n2 * 0.6 + n3 * 0.4);
|
|
|
|
|
+ finalNormal = normalize(vec3(finalNormal.xy * waveStrength, 1.0));
|
|
|
|
|
+
|
|
|
|
|
+ // 光照计算
|
|
|
|
|
+ vec3 sunDir = normalize(vec3(0.4, 0.8, 0.4));
|
|
|
|
|
+ vec3 viewDir = vec3(0.0, 0.0, 1.0);
|
|
|
|
|
+
|
|
|
|
|
+ float NdotL = max(dot(finalNormal, sunDir), 0.0);
|
|
|
|
|
+ float NdotV = max(dot(finalNormal, viewDir), 0.0);
|
|
|
|
|
+ vec3 halfVec = normalize(viewDir + sunDir);
|
|
|
|
|
+ float NdotH = max(dot(finalNormal, halfVec), 0.0);
|
|
|
|
|
+
|
|
|
|
|
+ // 真实菲涅尔效果 - 使用 Cesium 内置变换
|
|
|
|
|
+ // 将世界空间法线转换到眼睛空间
|
|
|
|
|
+ vec3 worldNormal = vec3(0.0, 0.0, 1.0); // 水面朝上
|
|
|
|
|
+ vec3 eyeNormal = czm_viewRotation * worldNormal;
|
|
|
|
|
+ // 计算与视线的角度(眼睛空间中 Z 轴是视线方向)
|
|
|
|
|
+ float NdotEye = abs(dot(eyeNormal, vec3(0.0, 0.0, 1.0)));
|
|
|
|
|
+ // 菲涅尔:角度越大(越倾斜),反射越强
|
|
|
|
|
+ float fresnel = pow(1.0 - NdotEye, fresnelPower);
|
|
|
|
|
+
|
|
|
|
|
+ // 高光 - 降低指数让高光范围更大,更易看到
|
|
|
|
|
+ float specular = pow(NdotH, 16.0) * specularIntensity;
|
|
|
|
|
+
|
|
|
|
|
+ // 次级高光 - 模拟散射光
|
|
|
|
|
+ float specular2 = pow(NdotH, 8.0) * specularIntensity * 0.5;
|
|
|
|
|
+
|
|
|
|
|
+ // 深度渐变 - 基于噪声模拟水深变化
|
|
|
|
|
+ float depthNoise = noise(st * 5.0 + t * 0.1) * 0.3;
|
|
|
|
|
+ float depthFactor = 0.5 + depthNoise;
|
|
|
|
|
+
|
|
|
|
|
+ // 混合浅水和深水颜色
|
|
|
|
|
+ vec3 waterCol = mix(deepColor.rgb, shallowColor.rgb, depthFactor);
|
|
|
|
|
+
|
|
|
|
|
+ // 光照
|
|
|
|
|
+ vec3 ambient = vec3(0.4, 0.45, 0.5);
|
|
|
|
|
+ vec3 sunLight = vec3(1.5, 1.4, 1.2);
|
|
|
|
|
+ vec3 lighting = ambient + NdotL * sunLight * 0.8;
|
|
|
|
|
+
|
|
|
|
|
+ // 基础颜色
|
|
|
|
|
+ vec3 finalColor = waterCol * lighting;
|
|
|
|
|
+
|
|
|
|
|
+ // 添加高光 - specularIntensity 直接控制强度
|
|
|
|
|
+ finalColor += specular * sunLight;
|
|
|
|
|
+ finalColor += specular2 * vec3(1.0, 1.0, 1.0);
|
|
|
|
|
+
|
|
|
|
|
+ // 菲涅尔反射 - 使用自定义天空贴图
|
|
|
|
|
+ // 计算反射方向
|
|
|
|
|
+ vec3 reflectDir = reflect(-viewDir, finalNormal);
|
|
|
|
|
+ // 转换到世界空间
|
|
|
|
|
+ vec3 reflectDirWorld = czm_inverseViewRotation * reflectDir;
|
|
|
|
|
+ // 半球投影:将反射方向映射到 UV 坐标
|
|
|
|
|
+ float skyU = 0.5 + atan(reflectDirWorld.x, reflectDirWorld.z) / (2.0 * 3.14159);
|
|
|
|
|
+ float skyV = 0.5 - asin(reflectDirWorld.y) / 3.14159;
|
|
|
|
|
+ vec2 skyUV = vec2(skyU, skyV);
|
|
|
|
|
+ // 采样天空贴图
|
|
|
|
|
+ vec3 skyReflection = texture2D(skyTexture, skyUV).rgb;
|
|
|
|
|
+ // 菲涅尔混合
|
|
|
|
|
+ finalColor = mix(finalColor, skyReflection, fresnel * 0.6);
|
|
|
|
|
+
|
|
|
|
|
+ // 轻微的焦散效果模拟
|
|
|
|
|
+ float caustic = noise(st * 30.0 + t * 0.5) * 0.1;
|
|
|
|
|
+ finalColor += caustic * vec3(0.3, 0.4, 0.5);
|
|
|
|
|
+
|
|
|
|
|
+ material.diffuse = finalColor;
|
|
|
|
|
+ material.alpha = baseWaterColor.a;
|
|
|
|
|
+ material.emission = vec3(0.0);
|
|
|
|
|
+
|
|
|
|
|
+ return material;
|
|
|
|
|
+ }
|
|
|
|
|
+ `
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ // 创建时间更新器,驱动水流动画
|
|
|
|
|
+ const updateFlow = function() {
|
|
|
|
|
+ flowWaterMaterial.uniforms.time += 0.016 * flowWaterMaterial.uniforms.flowSpeed;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 添加到场景渲染循环
|
|
|
|
|
+ const removeUpdateCallback = viewer.scene.preRender.addEventListener(updateFlow);
|
|
|
|
|
+
|
|
|
|
|
+ const waterMaterial = flowWaterMaterial;
|
|
|
|
|
|
|
|
const waterPrimitive = viewer.scene.primitives.add(new Cesium.Primitive({
|
|
const waterPrimitive = viewer.scene.primitives.add(new Cesium.Primitive({
|
|
|
geometryInstances: geometryInstance,
|
|
geometryInstances: geometryInstance,
|
|
@@ -231,6 +377,9 @@ function loadWaterLayer(url, name, callback) {
|
|
|
})
|
|
})
|
|
|
}));
|
|
}));
|
|
|
|
|
|
|
|
|
|
+ // 存储动画回调,用于后续清理
|
|
|
|
|
+ flowCallbacks.set(waterPrimitive, removeUpdateCallback);
|
|
|
|
|
+
|
|
|
waterPrimitives.push(waterPrimitive);
|
|
waterPrimitives.push(waterPrimitive);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -264,14 +413,26 @@ function removeWaterLayer(name) {
|
|
|
const primitive = primitives.get(i);
|
|
const primitive = primitives.get(i);
|
|
|
if (primitive && primitive.geometryInstances) {
|
|
if (primitive && primitive.geometryInstances) {
|
|
|
const instances = primitive.geometryInstances;
|
|
const instances = primitive.geometryInstances;
|
|
|
|
|
+ let shouldRemove = false;
|
|
|
|
|
+
|
|
|
if (Array.isArray(instances)) {
|
|
if (Array.isArray(instances)) {
|
|
|
for (const instance of instances) {
|
|
for (const instance of instances) {
|
|
|
if (instance.id === name || (instance.id && instance.id.includes(name))) {
|
|
if (instance.id === name || (instance.id && instance.id.includes(name))) {
|
|
|
- primitives.remove(primitive);
|
|
|
|
|
|
|
+ shouldRemove = true;
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- } else if (instances && instances.id === name || (instances.id && instances.id.includes(name))) {
|
|
|
|
|
|
|
+ } else if (instances && (instances.id === name || (instances.id && instances.id.includes(name)))) {
|
|
|
|
|
+ shouldRemove = true;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (shouldRemove) {
|
|
|
|
|
+ // 清理水流动画回调
|
|
|
|
|
+ if (flowCallbacks.has(primitive)) {
|
|
|
|
|
+ const removeCallback = flowCallbacks.get(primitive);
|
|
|
|
|
+ removeCallback();
|
|
|
|
|
+ flowCallbacks.delete(primitive);
|
|
|
|
|
+ }
|
|
|
primitives.remove(primitive);
|
|
primitives.remove(primitive);
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|