deeptwins-engine-3d 0.1.56 → 0.1.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -436,7 +436,7 @@ export var DEFAULT_BASE_LAYER = {
436
436
  name: '高德电子',
437
437
  type: 'raster',
438
438
  layers: [{
439
- url: 'http://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
439
+ url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
440
440
  minimumLevel: 1,
441
441
  maximumLevel: 18,
442
442
  credit: 'gd_vec'
@@ -344,6 +344,7 @@ var Drawer = /*#__PURE__*/function () {
344
344
  key: "endEdit",
345
345
  value: function endEdit() {
346
346
  if (!this._canOperate()) return;
347
+ this.getMap().canvas.style.cursor = 'default';
347
348
  this._changeStatus(STATUS.END_EDIT);
348
349
  this._updateTooltip();
349
350
  if (!this._instance) return;
@@ -276,6 +276,8 @@ var Point = /*#__PURE__*/function (_BaseShape) {
276
276
  value: function mouseDragChange(position) {
277
277
  if (this.dragging) {
278
278
  var _this$_drawContext$op, _this$_drawContext$op2;
279
+ // 根据edges重新生成新的点位
280
+ this.positions = [position.lng, position.lat, position.alt];
279
281
  // 调用点位改变事件
280
282
  ((_this$_drawContext$op = this._drawContext.options) === null || _this$_drawContext$op === void 0 ? void 0 : _this$_drawContext$op.onPointsChange) && ((_this$_drawContext$op2 = this._drawContext.options) === null || _this$_drawContext$op2 === void 0 ? void 0 : _this$_drawContext$op2.onPointsChange({
281
283
  layer: this,
@@ -15,7 +15,7 @@ export default class BaseSource {
15
15
  remove(): void;
16
16
  setData(data: any): this | undefined;
17
17
  _toDestroy(): void;
18
- static analysisSourceType(data: any): "wkt" | "sourceId" | "sourceInstance" | "geoJson" | "clampedPolygonGrid" | "other";
18
+ static analysisSourceType(data: any): "clampedPolygonGrid" | "wkt" | "sourceId" | "sourceInstance" | "geoJson" | "other";
19
19
  static wktToGeoJon(wkt: string): any;
20
20
  static geoJsonToGeoCartesian3Array(geoJson: any): any[];
21
21
  static handleFeaturePoint(feature: any): any;
@@ -73,5 +73,6 @@ export declare class DeepTwinsEngine3D {
73
73
  static BuildClampedPolygonGridUnified: any;
74
74
  static ViewShedAnalyserLayer: any;
75
75
  static VolumeClouds: any;
76
+ static ToolTip: any;
76
77
  }
77
78
  export default DeepTwinsEngine3D;
package/dist/esm/index.js CHANGED
@@ -47,6 +47,7 @@ import Compass from "./tool/Compass";
47
47
  import TimerInterval from "./tool/TimerInterval";
48
48
  import { ClampToGround, ShapeSection } from "./tool/common";
49
49
  import { loadCss } from "./tool/utils";
50
+ import ToolTip from "./toolTip";
50
51
  import VideoProject from "./videoFusion/VideoProject";
51
52
  import VideoTexture from "./videoFusion/VideoTexture";
52
53
  import Frustum from "./visualization/Frustum";
@@ -61,11 +62,11 @@ DEEP_TWINS_BASE_URL && (window.CESIUM_BASE_URL = DEEP_TWINS_BASE_URL);
61
62
  // 全局加载css
62
63
  loadCss(Cesium.buildModuleUrl('Widgets/widgets.css'));
63
64
  // 打印版本信息
64
- console.log('DeepTwinsEngine3D Version:', "0.1.56");
65
+ console.log('DeepTwinsEngine3D Version:', "0.1.58");
65
66
  export var DeepTwinsEngine3D = /*#__PURE__*/_createClass(function DeepTwinsEngine3D() {
66
67
  _classCallCheck(this, DeepTwinsEngine3D);
67
68
  });
68
- _defineProperty(DeepTwinsEngine3D, "Version", "0.1.56");
69
+ _defineProperty(DeepTwinsEngine3D, "Version", "0.1.58");
69
70
  _defineProperty(DeepTwinsEngine3D, "Map", Map);
70
71
  _defineProperty(DeepTwinsEngine3D, "GroundSkyBox", GroundSkyBox);
71
72
  _defineProperty(DeepTwinsEngine3D, "RasterLayer", RasterLayer);
@@ -138,4 +139,5 @@ _defineProperty(DeepTwinsEngine3D, "FlightRiskEvaluation", FlightRiskEvaluation)
138
139
  _defineProperty(DeepTwinsEngine3D, "BuildClampedPolygonGridUnified", BuildClampedPolygonGridUnified);
139
140
  _defineProperty(DeepTwinsEngine3D, "ViewShedAnalyserLayer", ViewShedAnalyserLayer);
140
141
  _defineProperty(DeepTwinsEngine3D, "VolumeClouds", VolumeClouds);
142
+ _defineProperty(DeepTwinsEngine3D, "ToolTip", ToolTip);
141
143
  export default DeepTwinsEngine3D;
@@ -0,0 +1,117 @@
1
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
4
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
5
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
6
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
7
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
8
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
9
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
10
+ var AtmosphericDispersion = /*#__PURE__*/function () {
11
+ function AtmosphericDispersion(viewer) {
12
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13
+ _classCallCheck(this, AtmosphericDispersion);
14
+ this.viewer = viewer.viewer;
15
+ if (!this.viewer || _typeof(this.viewer) !== 'object') {
16
+ throw new Error('Invalid Cesium Viewer instance provided');
17
+ }
18
+ if (!this.viewer.scene || !this.viewer.scene.postProcessStages) {
19
+ throw new Error('Viewer instance must have valid scene and postProcessStages');
20
+ }
21
+ this.params = {
22
+ u_threshold: options.u_threshold !== undefined ? options.u_threshold : 1.1,
23
+ u_blendFactor: options.u_blendFactor !== undefined ? options.u_blendFactor : 0.5,
24
+ u_lightIntensity: options.u_lightIntensity !== undefined ? options.u_lightIntensity : 1.6
25
+ };
26
+ this.originalOptions = _objectSpread({}, options);
27
+ this.atmosphericDispersion = null;
28
+ this.initialize();
29
+ }
30
+ _createClass(AtmosphericDispersion, [{
31
+ key: "initialize",
32
+ value: function initialize() {
33
+ var _this = this;
34
+ var atmosphereFs = this._createFragmentShader();
35
+ this.atmosphericDispersion = new Cesium.PostProcessStage({
36
+ fragmentShader: atmosphereFs,
37
+ uniforms: {
38
+ u_threshold: function u_threshold() {
39
+ return _this.params.u_threshold;
40
+ },
41
+ u_blendFactor: function u_blendFactor() {
42
+ return _this.params.u_blendFactor;
43
+ },
44
+ u_lightIntensity: function u_lightIntensity() {
45
+ return _this.params.u_lightIntensity;
46
+ }
47
+ }
48
+ });
49
+ this.viewer.scene.postProcessStages.add(this.atmosphericDispersion);
50
+ }
51
+ }, {
52
+ key: "_createFragmentShader",
53
+ value: function _createFragmentShader() {
54
+ return "\n precision highp float;\n uniform sampler2D colorTexture;\n uniform sampler2D depthTexture;\n uniform float u_threshold;\n uniform float u_blendFactor;\n uniform float u_lightIntensity;\n in vec2 v_textureCoordinates;\n const float PI = 3.14159265359;\n const float TWO_PI = PI * 2.0;\n const float FOUR_PI = PI * 4.0;\n\n const vec3 betaR = vec3(5.5e-6, 13.0e-6, 22.4e-6);\n\n const vec3 betaM = vec3(21e-6);\n\n const float hR = 10e3;\n const float hM = 3.8e3;\n const int num_samples = 16;\n const int num_samples_light = 4;\n #ifdef GL_ES\n #define _in(T) const in T\n #define _inout(T) inout T\n #define _out(T) out T\n #define _begin(type) type (\n #define _end )\n #define mul(a, b) (a) * (b)\n #endif\n\n struct ray_t {\n vec3 origin;\n vec3 direction;\n };\n struct sphere_t {\n vec3 origin;\n float radius;\n int material;\n };\n struct plane_t {\n vec3 direction;\n float distance;\n int material;\n };\n plane_t plane;\n mat3 rotate_around_x(_in(float) angle_degrees) {\n float angle = radians(angle_degrees);\n float _sin = sin(angle);\n float _cos = cos(angle);\n return mat3(1, 0, 0, 0, _cos, -_sin, 0, _sin, _cos);\n }\n\n bool isect_sphere(_in(ray_t) ray, _in(sphere_t) sphere, _inout(float) t0, _inout(float) t1) {\n vec3 rc = sphere.origin - ray.origin;\n float radius2 = sphere.radius * sphere.radius;\n float tca = dot(rc, ray.direction);\n float d2 = dot(rc, rc) - tca * tca;\n if (d2 > radius2) return false;\n float thc = sqrt(radius2 - d2);\n t0 = tca - thc;\n t1 = tca + thc;\n return true;\n }\n float rayleigh_phase_func(float mu) {\n return\n 3. * (1. + mu*mu)\n /\n (16. * PI);\n }\n const float g = 0.76;\n float henyey_greenstein_phase_func(float mu) {\n return\n (1. - g*g)\n /\n ((4. * PI) * pow(1. + g*g - 2.*g*mu, 1.5));\n }\n const float k = 1.55*g - 0.55 * (g*g*g);\n float schlick_phase_func(float mu) {\n return\n (1. - k*k)\n /\n (4. * PI * (1. + k*mu) * (1. + k*mu));\n }\n const sphere_t atmosphere = _begin(sphere_t)\n vec3(0, 0, 0), 6420e3, 0\n _end;\n\n bool get_sun_light(\n _in(ray_t) ray, _inout(float) optical_depthR, _inout(float) optical_depthM\n ) {\n float t0, t1;\n isect_sphere(ray, atmosphere, t0, t1);\n float march_pos = 0.;\n float march_step = t1 / float(num_samples_light);\n for (int i = 0; i < num_samples_light; i++) {\n vec3 s = ray.origin +\n ray.direction * (march_pos + 0.5 * march_step);\n float height = length(s) - 6360e3;\n if (height < 0.)\n return false;\n optical_depthR += exp(-height / hR) * march_step;\n optical_depthM += exp(-height / hM) * march_step;\n march_pos += march_step;\n }\n return true;\n }\n vec4 get_incident_light(_in(ray_t) ray) {\n vec3 dir = ray.direction;\n vec3 start = ray.origin;\n float a = dot( dir, dir);\n float b = 2.0 * dot(dir, start);\n float radius2 = atmosphere.radius * atmosphere.radius;\n float c = dot(start, start) - radius2;\n float d = (b * b) - 4.0 * a * c;\n if (d < 0.0) return vec4(0.0);\n float squaredD = sqrt(d);\n vec2 ray_length = vec2(\n max((-b - squaredD) / (2.0 * a), 0.0), min((-b + squaredD) / (2.0 * a), plane.distance)\n );\n if (ray_length.x > ray_length.y) return vec4(0.0);\n float march_step = (ray_length.y - ray_length.x) / float(num_samples);\n float mu = dot(ray.direction, normalize(czm_sunPositionWC));\n float phaseR = rayleigh_phase_func(mu);\n float phaseM =\n #if 1\n henyey_greenstein_phase_func(mu);\n #else\n schlick_phase_func(mu);\n #endif\n\n float optical_depthR = 0.;\n float optical_depthM = 0.;\n vec3 sumR = vec3(0);\n vec3 sumM = vec3(0);\n float march_pos = 0.;\n for (int i = 0; i < num_samples; i++) {\n vec3 s = ray.origin +\n ray.direction * (march_pos + 0.5 * march_step);\n float height = length(s) - 6360e3;\n\n float hr = exp(-height / hR) * march_step;\n float hm = exp(-height / hM) * march_step;\n optical_depthR += hr;\n optical_depthM += hm;\n\n ray_t light_ray = _begin(ray_t)\n s, normalize(czm_sunPositionWC)\n _end;\n float optical_depth_lightR = 0.;\n float optical_depth_lightM = 0.;\n bool overground = get_sun_light(\n light_ray,\n optical_depth_lightR,\n optical_depth_lightM);\n\n if (overground) {\n\n vec3 tau =\n betaR * (optical_depthR + optical_depth_lightR) +\n betaM * 1.1 * (optical_depthM + optical_depth_lightM);\n vec3 attenuation = exp(-tau);\n sumR += hr * attenuation;\n sumM += hm * attenuation;\n }\n\n march_pos += march_step;\n }\n float attenuation = length(exp(-((betaM * optical_depthM)\n + (betaR * optical_depthR)) * 4.));\n\n return vec4(\n 23. *\n (sumR * phaseR * betaR +\n sumM * phaseM * betaM), 1.0-attenuation);\n }\n\n void main() {\n vec4 rawColor = texture(colorTexture, v_textureCoordinates);\n float depth = czm_unpackDepth(texture(depthTexture, v_textureCoordinates));\n\n vec4 positionEC = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);\n vec4 positionWC = czm_inverseView * positionEC;\n positionWC.xyz = positionWC.xyz / positionWC.w;\n\n vec3 lVector = positionWC.xyz - czm_viewerPositionWC;\n ray_t ray;\n ray.origin = czm_viewerPositionWC;\n ray.direction = normalize(lVector);\n plane.distance = length(lVector);\n\n vec4 atmosphereColor = get_incident_light(ray);\n atmosphereColor.rgb *= u_lightIntensity;\n float brightness = dot(atmosphereColor.rgb, vec3(0.299, 0.587, 0.114));\n if (brightness < u_threshold) {\n atmosphereColor.rgb *= brightness / u_threshold;\n }\n rawColor = mix(rawColor, atmosphereColor + rawColor * (1.0 - atmosphereColor.a), u_blendFactor);\n rawColor = vec4(1.0 - exp(-2.2 * rawColor));\n\n out_FragColor = rawColor;\n }\n ";
55
+ }
56
+ }, {
57
+ key: "updateParameters",
58
+ value: function updateParameters(params) {
59
+ if (params && _typeof(params) === 'object') {
60
+ if (typeof params.u_threshold === 'number') {
61
+ this.params.u_threshold = params.u_threshold;
62
+ }
63
+ if (typeof params.u_blendFactor === 'number') {
64
+ this.params.u_blendFactor = params.u_blendFactor;
65
+ }
66
+ if (typeof params.u_lightIntensity === 'number') {
67
+ this.params.u_lightIntensity = params.u_lightIntensity;
68
+ }
69
+ }
70
+ return this;
71
+ }
72
+ }, {
73
+ key: "resetParameters",
74
+ value: function resetParameters() {
75
+ this.params = {
76
+ u_threshold: this.originalOptions.u_threshold !== undefined ? this.originalOptions.u_threshold : 1.1,
77
+ u_blendFactor: this.originalOptions.u_blendFactor !== undefined ? this.originalOptions.u_blendFactor : 0.5,
78
+ u_lightIntensity: this.originalOptions.u_lightIntensity !== undefined ? this.originalOptions.u_lightIntensity : 1.6
79
+ };
80
+ return this;
81
+ }
82
+ }, {
83
+ key: "show",
84
+ value: function show() {
85
+ if (this.atmosphericDispersion) {
86
+ this.atmosphericDispersion.enabled = true;
87
+ }
88
+ return this;
89
+ }
90
+ }, {
91
+ key: "hide",
92
+ value: function hide() {
93
+ if (this.atmosphericDispersion) {
94
+ this.atmosphericDispersion.enabled = false;
95
+ }
96
+ return this;
97
+ }
98
+ }, {
99
+ key: "isEnabled",
100
+ value: function isEnabled() {
101
+ return this.atmosphericDispersion ? this.atmosphericDispersion.enabled : false;
102
+ }
103
+ }, {
104
+ key: "destroy",
105
+ value: function destroy() {
106
+ if (this.atmosphericDispersion) {
107
+ this.viewer.scene.postProcessStages.remove(this.atmosphericDispersion);
108
+ this.atmosphericDispersion = null;
109
+ this.viewer = null;
110
+ this.params = null;
111
+ this.originalOptions = null;
112
+ }
113
+ }
114
+ }]);
115
+ return AtmosphericDispersion;
116
+ }();
117
+ export default AtmosphericDispersion;
@@ -0,0 +1,381 @@
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
4
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
5
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
6
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
7
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
8
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
9
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
10
+ /**
11
+ * 全球体积云类 - GlobalVolumetricCloud
12
+ *
13
+ * 一个用于在Cesium中创建和管理高质量体积云效果的封装类。
14
+ * 基于PostProcessStage后处理技术实现的物理基于的体积云渲染系统,
15
+ * 支持实时云形态变化、光照散射、体积阴影等高级特性。
16
+ *
17
+ * 功能特点:
18
+ * - 基于物理的体积云渲染
19
+ * - 实时风场模拟
20
+ * - 可调节的云类型和特征
21
+ * - 动态光照和阴影
22
+ * - 性能优化的渲染管线
23
+ *
24
+ * 使用示例:
25
+ * ```javascript
26
+ * // 创建Cesium Viewer
27
+ * const viewer = new Cesium.Viewer('cesiumContainer');
28
+ *
29
+ * // 初始化体积云系统
30
+ * const clouds = new GlobalVolumetricCloud(viewer: viewer, {
31
+ * cloudType: 0.5, // 混合积云和层云
32
+ * cloudCover: 0.5, // 中等云量
33
+ * cloudHeight: 5000.0, // 云层高度
34
+ * densityMultiplier: 1.2, // 密度调整
35
+ * windSpeedRatio: 0.5 // 风速
36
+ * });
37
+ *
38
+ * // 更新云参数
39
+ * clouds.updateParameters({
40
+ * cloudType: 0.8, // 更多积云特征
41
+ * cloudSharpness: 1.5 // 更清晰的云边界
42
+ * });
43
+ *
44
+ * // 控制云显示
45
+ * clouds.hide(); // 隐藏云
46
+ * clouds.show(); // 显示云
47
+ *
48
+ * // 销毁云系统
49
+ * clouds.destroy(); // 清理资源
50
+ * ```
51
+ */
52
+ var GlobalVolumetricCloud = /*#__PURE__*/function () {
53
+ /**
54
+ * 构造函数
55
+ *
56
+ * @param {Cesium.Viewer} viewer - Cesium查看器实例
57
+ * @param {Object} options - 体积云配置参数对象
58
+ * @param {number} [options.densityMultiplier=1.5] - 密度倍增器,影响整体云密度,推荐值范围: 0.5 ~ 3.0
59
+ * @param {number} [options.detailStrength=0.69] - 细节强度,控制云内部结构复杂度,推荐值范围: 0.1 ~ 2.0
60
+ * @param {number} [options.cloudSharpness=1.032] - 云边界锐度,控制云边缘清晰度,推荐值范围: 0.5 ~ 4.0
61
+ * @param {number} [options.rainClouds=0.45] - 雨云特征强度,影响云的厚重感,推荐值范围: 0.0 ~ 1.0
62
+ * @param {Cesium.Cartesian3} [options.currentWindVectorWC] - 风向向量,默认值为new Cesium.Cartesian3(100, 0, 0)
63
+ * @param {number} [options.cloudCover=0.039] - 云覆盖比例,控制天空中云的覆盖面积
64
+ * @param {number} [options.cloudHeight=8000.0] - 云层底部高度(米)
65
+ * @param {number} [options.cloudThickness=1000.0] - 云层厚度(米)
66
+ * @param {number} [options.absoluteMaxDistance=50000000] - 最大可视距离
67
+ * @param {number} [options.absorption=0.004] - 吸收系数,控制光在云中的衰减
68
+ * @param {number} [options.windSpeedRatio=0.3] - 风速比率,控制云移动速度
69
+ * @param {number} [options.scatteringCoefficient=0.01] - 散射系数,影响光的散射强度
70
+ * @param {number} [options.illuminationCoefficient=0.01] - 照明系数
71
+ * @param {number} [options.cloudSeed=Math.random()*100] - 随机种子,影响云的形态分布
72
+ * @param {number} [options.cloudType=0] - 云类型,0=层云, 1=积云,中间值为混合类型
73
+ * @param {number} [options.cloudTurbulence=0.5] - 湍流强度,控制云的不规则程度
74
+ * @param {number} [options.cloudClumping=0.75] - 团块程度,控制云的聚集特性
75
+ * @param {number} [options.cloudAnvil=0] - 砧状云特征,控制特殊云形态
76
+ * @param {string} [options.skyAmbientColor='#B4D9F3'] - 天空环境颜色
77
+ * @param {string} [options.groundAmbientColor='#d6d6d8'] - 地面环境颜色
78
+ */
79
+ function GlobalVolumetricCloud(viewer) {
80
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
81
+ _classCallCheck(this, GlobalVolumetricCloud);
82
+ this.viewer = viewer.viewer;
83
+
84
+ // 初始化体积云参数 - 确保包含所有原函数中的19个参数
85
+ this.uniforms = {
86
+ densityMultiplier: options.densityMultiplier || 1.5,
87
+ // 密度倍增器,推荐值范围: 0.5 ~ 3.0
88
+ detailStrength: options.detailStrength || 0.69,
89
+ // 细节强度,推荐值范围: 0.1 ~ 2.0
90
+ cloudSharpness: options.cloudSharpness || 1.032,
91
+ // 云边界锐度,推荐值范围: 0.5 ~ 4.0
92
+ rainClouds: options.rainClouds || 0.45,
93
+ // 雨云特征,推荐值范围: 0.0 ~ 1.0
94
+ currentWindVectorWC: options.currentWindVectorWC || new Cesium.Cartesian3(100, 0, 0),
95
+ cloudCover: options.cloudCover || 0.039,
96
+ // 云量
97
+ cloudHeight: options.cloudHeight || 8000.0,
98
+ // 以底部为基准
99
+ cloudThickness: options.cloudThickness || 1000.0,
100
+ // 云层厚度
101
+ absoluteMaxDistance: options.absoluteMaxDistance || 50000000,
102
+ // 最大可视距
103
+ absorption: options.absorption || 0.004,
104
+ // 吸收系数
105
+ windSpeedRatio: options.windSpeedRatio || 0.3,
106
+ //风速
107
+ scatteringCoefficient: options.scatteringCoefficient || 0.01,
108
+ // 散射系数
109
+ illuminationCoefficient: options.illuminationCoefficient || 0.01,
110
+ // 照明系数
111
+ cloudSeed: options.cloudSeed || Math.random() * 100.0,
112
+ // 随机种子
113
+ cloudType: options.cloudType || 0,
114
+ // 0=层云, 1=积云
115
+ cloudTurbulence: options.cloudTurbulence || 0.5,
116
+ // 湍流强度
117
+ cloudClumping: options.cloudClumping || 0.75,
118
+ // 团块程度
119
+ cloudAnvil: options.cloudAnvil || 0,
120
+ // 砧状云特征
121
+ skyAmbientColor: options.skyAmbientColor || '#B4D9F3',
122
+ // 原 vec3(0.705, 0.850, 0.952)
123
+ groundAmbientColor: options.groundAmbientColor || '#d6d6d8' // 原 vec3(0.741, 0.898, 0.823)
124
+ };
125
+
126
+ // 颜色转换对象 - 保持与原函数一致的颜色处理
127
+ this.skyAmbientColor3 = {
128
+ r: 0.705,
129
+ g: 0.85,
130
+ b: 0.952
131
+ };
132
+ this.groundAmbientColor3 = {
133
+ r: 0.839,
134
+ g: 0.839,
135
+ b: 0.847
136
+ };
137
+
138
+ // 后处理阶段对象
139
+ this.postProcessStage = null;
140
+
141
+ // 保存原始选项用于重置
142
+ this.originalOptions = _objectSpread({}, options);
143
+
144
+ // 初始化云
145
+ this.initialize();
146
+ }
147
+
148
+ /**
149
+ * 初始化体积云系统
150
+ *
151
+ * 创建并配置体积云后处理阶段,设置所有着色器和uniforms参数。
152
+ * 这是内部方法,通常由构造函数自动调用。
153
+ *
154
+ * @private
155
+ */
156
+ _createClass(GlobalVolumetricCloud, [{
157
+ key: "initialize",
158
+ value: function initialize() {
159
+ var _this = this;
160
+ var shader = this._createShader();
161
+ this.postProcessStage = new Cesium.PostProcessStage({
162
+ uniforms: {
163
+ realPlanetRadius: function realPlanetRadius() {
164
+ return _this._getRealPlanetRadius();
165
+ },
166
+ windVector: this.uniforms.currentWindVectorWC,
167
+ cloudCover: this.uniforms.cloudCover,
168
+ cloudHeight: this.uniforms.cloudHeight,
169
+ cloudThickness: this.uniforms.cloudThickness,
170
+ absoluteMaxDistance: this.uniforms.absoluteMaxDistance,
171
+ absorption: this.uniforms.absorption,
172
+ windSpeedRatio: this.uniforms.windSpeedRatio,
173
+ scatteringCoefficient: this.uniforms.scatteringCoefficient,
174
+ illuminationCoefficient: this.uniforms.illuminationCoefficient,
175
+ cloudSeed: this.uniforms.cloudSeed,
176
+ cloudType: this.uniforms.cloudType,
177
+ cloudTurbulence: this.uniforms.cloudTurbulence,
178
+ cloudClumping: this.uniforms.cloudClumping,
179
+ cloudAnvil: this.uniforms.cloudAnvil,
180
+ densityMultiplier: this.uniforms.densityMultiplier,
181
+ detailStrength: this.uniforms.detailStrength,
182
+ cloudSharpness: this.uniforms.cloudSharpness,
183
+ rainClouds: this.uniforms.rainClouds,
184
+ skyAmbientColor: function skyAmbientColor() {
185
+ return new Cesium.Cartesian3(_this.skyAmbientColor3.r, _this.skyAmbientColor3.g, _this.skyAmbientColor3.b);
186
+ },
187
+ groundAmbientColor: function groundAmbientColor() {
188
+ return new Cesium.Cartesian3(_this.groundAmbientColor3.r, _this.groundAmbientColor3.g, _this.groundAmbientColor3.b);
189
+ }
190
+ },
191
+ forcePowerOfTwo: true,
192
+ fragmentShader: shader
193
+ });
194
+ this.viewer.scene.postProcessStages.add(this.postProcessStage);
195
+ }
196
+
197
+ /**
198
+ * 创建体积云的着色器代码
199
+ *
200
+ * 生成包含体积云渲染算法的完整GLSL着色器代码。
201
+ * 包括云密度计算、光照模型、阴影计算、相位函数等关键算法。
202
+ *
203
+ * @private
204
+ * @returns {string} 包含完整体积云渲染算法的GLSL着色器代码
205
+ */
206
+ }, {
207
+ key: "_createShader",
208
+ value: function _createShader() {
209
+ return "\n precision highp float;\n uniform float realPlanetRadius;\n uniform vec3 windVector;\n uniform float cloudCover;\n uniform float cloudHeight;\n uniform float cloudThickness;\n uniform float absoluteMaxDistance;\n uniform float absorption;\n uniform float windSpeedRatio;\n uniform float scatteringCoefficient;\n uniform float illuminationCoefficient;\n\n const float PI = 3.14159265359;\n const float TWO_PI = 6.28318530718;\n const float FOUR_PI = 12.5663706144;\n float cloudTop;\n\n #define CLOUDS_MAX_LOD 1\n #define CLOUDS_MARCH_STEP 120.0\n #define CLOUDS_DENS_MARCH_STEP 60.0\n #define MAXIMUM_CLOUDS_STEPS 256\n\n // \u4F53\u79EF\u4ECB\u8D28\u6838\u5FC3\u53C2\u6570\n uniform float cloudSeed;\n uniform float cloudType; // 0-1: \u5C42\u4E91\u5230\u79EF\u4E91\n uniform float cloudTurbulence; // \u6E4D\u6D41\u5F3A\u5EA6\n uniform float cloudClumping; // \u56E2\u5757\u7A0B\u5EA6\n uniform float cloudAnvil; // \u7827\u72B6\u4E91\u7279\u5F81\n uniform vec3 skyAmbientColor;\n uniform vec3 groundAmbientColor;\n\n // ===== \u65B0\u589E\u7684\u4F53\u79EF\u4ECB\u8D28\u53C2\u6570 =====\n uniform float densityMultiplier; // \u5BC6\u5EA6\u500D\u589E\u5668\n uniform float detailStrength; // \u7EC6\u8282\u5F3A\u5EA6\n uniform float cloudSharpness; // \u4E91\u8FB9\u754C\u9510\u5EA6\n uniform float rainClouds; // \u96E8\u4E91\u7279\u5F81\n\n // \u5C04\u7EBF\u4E0E\u7403\u4F53\u76F8\u4EA4\n vec2 raySphereIntersect(vec3 r0, vec3 rd, float sr) {\n float a = dot(rd, rd);\n float b = 2.0 * dot(rd, r0);\n float c = dot(r0, r0) - (sr * sr);\n float d = (b * b) - 4.0 * a * c;\n\n if (d < 0.0) return vec2(-1.0, -1.0);\n float squaredD = sqrt(d);\n\n return vec2((-b - squaredD) / (2.0 * a), (-b + squaredD) / (2.0 * a));\n }\n\n float hash12(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * .1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n }\n\n float saturate(float value) {\n return clamp(value, 0.0, 1.0);\n }\n\n float isotropic() {\n return 0.07957747154594767;\n }\n\n // HG\u76F8\u4F4D\u51FD\u6570\n float HenyeyGreenstein(float cosTheta, float g) {\n float g2 = g * g;\n float denominator = 4.0 * PI * pow(1.0 + g2 - 2.0 * g * cosTheta, 1.5);\n return (1.0 - g2) / max(denominator, 0.0001);\n }\n\n // \u6539\u8FDB\u7684\u76F8\u4F4D\u51FD\u6570 - \u66F4\u597D\u7684\u80CC\u5149\u6563\u5C04\n float improvedPhase(float cosTheta) {\n // \u4E3B\u524D\u5411\u6563\u5C04\u74E3 - \u9002\u5EA6\u6291\u5236\n float forwardLobe = HenyeyGreenstein(cosTheta, 0.4) * 0.4;\n\n // \u589E\u5F3A\u7684\u80CC\u5149\u6563\u5C04\u74E3 - \u63D0\u5347\u80CC\u5149\u4EAE\u5EA6\n float backwardLobe = HenyeyGreenstein(cosTheta, -0.3) * 0.4;\n\n // \u5404\u5411\u540C\u6027\u57FA\u7840\u6563\u5C04\n float isotropicPart = isotropic() * 0.2;\n\n return forwardLobe + backwardLobe + isotropicPart;\n }\n // ===== \u8865\u5168\u7684\u4F53\u79EF\u4ECB\u8D28\u5BC6\u5EA6\u51FD\u6570 =====\n // \u67CF\u6797\u566A\u58F0\u51FD\u6570\n vec4 permute(vec4 x) { return mod(((x*34.0)+1.0)*x, 289.0); }\n vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; }\n\n float perlinNoise(vec3 P) {\n vec3 i0 = floor(P);\n vec3 f0 = fract(P);\n vec3 f = f0 * f0 * (3.0 - 2.0 * f0);\n vec4 ix = vec4(i0.x, i0.x + 1.0, i0.x, i0.x + 1.0);\n vec4 iy = vec4(i0.y, i0.y, i0.y + 1.0, i0.y + 1.0);\n vec4 iz0 = vec4(i0.z);\n vec4 iz1 = iz0 + 1.0;\n\n vec4 ixy = permute(permute(ix) + iy);\n vec4 ixy0 = permute(ixy + iz0);\n vec4 ixy1 = permute(ixy + iz1);\n\n vec4 gx0 = ixy0 / 7.0;\n vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;\n gx0 = fract(gx0) - 0.5;\n vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);\n vec4 sz0 = step(gz0, vec4(0.0));\n gx0 -= sz0 * (step(0.0, gx0) - 0.5);\n gy0 -= sz0 * (step(0.0, gy0) - 0.5);\n\n vec4 gx1 = ixy1 / 7.0;\n vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;\n gx1 = fract(gx1) - 0.5;\n vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);\n vec4 sz1 = step(gz1, vec4(0.0));\n gx1 -= sz1 * (step(0.0, gx1) - 0.5);\n gy1 -= sz1 * (step(0.0, gy1) - 0.5);\n\n vec3 g000 = vec3(gx0.x, gy0.x, gz0.x);\n vec3 g100 = vec3(gx0.y, gy0.y, gz0.y);\n vec3 g010 = vec3(gx0.z, gy0.z, gz0.z);\n vec3 g110 = vec3(gx0.w, gy0.w, gz0.w);\n vec3 g001 = vec3(gx1.x, gy1.x, gz1.x);\n vec3 g101 = vec3(gx1.y, gy1.y, gz1.y);\n vec3 g011 = vec3(gx1.z, gy1.z, gz1.z);\n vec3 g111 = vec3(gx1.w, gy1.w, gz1.w);\n\n vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));\n g000 *= norm0.x; g010 *= norm0.y;\n g100 *= norm0.z; g110 *= norm0.w;\n vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));\n g001 *= norm1.x; g011 *= norm1.y;\n g101 *= norm1.z; g111 *= norm1.w;\n\n float n000 = dot(g000, f0);\n float n100 = dot(g100, f0 - vec3(1.0, 0.0, 0.0));\n float n010 = dot(g010, f0 - vec3(0.0, 1.0, 0.0));\n float n110 = dot(g110, f0 - vec3(1.0, 1.0, 0.0));\n float n001 = dot(g001, f0 - vec3(0.0, 0.0, 1.0));\n float n101 = dot(g101, f0 - vec3(1.0, 0.0, 1.0));\n float n011 = dot(g011, f0 - vec3(0.0, 1.0, 1.0));\n float n111 = dot(g111, f0 - vec3(1.0, 1.0, 1.0));\n\n vec4 n_z0 = mix(vec4(n000, n100, n010, n110),\n vec4(n001, n101, n011, n111), f.z);\n vec2 n_z1 = mix(n_z0.xy, n_z0.zw, f.y);\n float n = mix(n_z1.x, n_z1.y, f.x);\n\n return 2.2 * n;\n }\n // FBM\u51FD\u6570\n float fBm(vec3 p, int octaves, float persistence, float lacunarity) {\n float value = 0.0;\n float amplitude = 1.0;\n float frequency = 1.0;\n float maxValue = 0.0;\n\n for (int i = 0; i < octaves; i++) {\n value += amplitude * perlinNoise(p * frequency);\n maxValue += amplitude;\n amplitude *= persistence;\n frequency *= lacunarity;\n }\n\n return value / maxValue;\n }\n // \u57DF\u626D\u66F2\u566A\u58F0\n float domainWarpedNoise(vec3 p, float intensity) {\n vec3 q = vec3(\n perlinNoise(p),\n perlinNoise(p + vec3(123.4, 234.5, 345.6)),\n perlinNoise(p + vec3(234.5, 345.6, 456.7))\n );\n vec3 r = vec3(\n perlinNoise(p + 1.0 * q + vec3(123.4, 234.5, 345.6)),\n perlinNoise(p + 1.0 * q + vec3(234.5, 345.6, 456.7)),\n perlinNoise(p + 1.0 * q + vec3(345.6, 456.7, 567.8))\n );\n return perlinNoise(p + intensity * r);\n }\n\n // Worley\u566A\u58F0\n float worleyNoise(vec3 p, float scale) {\n vec3 i = floor(p * scale);\n vec3 f = fract(p * scale);\n\n float minDist1 = 1.0;\n float minDist2 = 1.0;\n\n for (int x = -1; x <= 1; x++) {\n for (int y = -1; y <= 1; y++) {\n for (int z = -1; z <= 1; z++) {\n vec3 neighbor = vec3(float(x), float(y), float(z));\n vec3 random = fract(sin(dot(i + neighbor, vec3(127.1, 311.7, 74.7))) *\n vec3(43758.5453, 22578.1459, 19632.7411));\n vec3 point = neighbor + random - f;\n float dist = length(point);\n\n if (dist < minDist1) {\n minDist2 = minDist1;\n minDist1 = dist;\n } else if (dist < minDist2) {\n minDist2 = dist;\n }\n }\n }\n }\n\n return minDist2 - minDist1;\n }\n // \u4E91\u5C42\u9AD8\u5EA6\u5206\u5E03\u51FD\u6570\n float cloudHeightProfile(float heightRatio, float cloudType) {\n // \u5C42\u4E91 - \u5747\u5300\u5206\u5E03\n if (cloudType < 0.3) {\n return 1.0;\n }\n // \u79EF\u4E91 - \u4E2D\u95F4\u539A\uFF0C\u4E0A\u4E0B\u8584\n else if (cloudType < 0.7) {\n float profile = 1.0 - 4.0 * pow(heightRatio - 0.5, 2.0);\n return max(profile, 0.1);\n }\n // \u7827\u72B6\u4E91 - \u4E0A\u90E8\u6269\u5C55\n else {\n float anvilProfile = 1.0 - pow(heightRatio, 2.0);\n return max(anvilProfile, 0.2);\n }\n }\n\n // \u96E8\u4E91\u5BC6\u5EA6\u7279\u5F81\n float rainCloudDensity(float baseDensity, float heightRatio, float rainFactor) {\n if (rainFactor < 0.1) return baseDensity;\n\n // \u96E8\u4E91\u5E95\u90E8\u66F4\u5BC6\u96C6\n float rainProfile = 1.0 + rainFactor * (1.0 - heightRatio) * 2.0;\n return baseDensity * rainProfile;\n }\n // ===== \u8865\u5168\u7684\u4F53\u79EF\u4ECB\u8D28\u5149\u7167\u6A21\u578B =====\n\n const float preBaseScale = 0.0003;\n const float preFineScale = 0.0003 * 16.0;\n float baseScale = 0.0003;\n // ===== \u8865\u5168\u7684\u4E91\u6735\u5BC6\u5EA6\u51FD\u6570 =====\n float cloudDensity(vec3 p, vec3 windVector, inout float heightRatio) {\n float finalCoverage = cloudCover;\n if (finalCoverage <= 0.001) {\n return 0.0;\n }\n\n float height = length(p) - realPlanetRadius;\n heightRatio = clamp((height - cloudHeight) / cloudThickness, 0.0, 1.0);\n\n // \u57FA\u7840\u9AD8\u5EA6\u56E0\u5B50\n float hLower = cloudHeight;\n float hUpper = cloudHeight + cloudThickness;\n float heightFactor = smoothstep(hLower, hLower + 0.2 * cloudThickness, height) *\n (1.0 - smoothstep(hUpper - 0.2 * cloudThickness, hUpper, height));\n\n vec3 seedOffset = vec3(cloudSeed * 100.0, 0.0, cloudSeed * 50.0);\n vec3 animatedPos = p + windVector * windSpeedRatio + seedOffset;\n\n // \u57FA\u7840\u5F62\u72B6\u566A\u58F0\n float baseShape = fBm(animatedPos * preBaseScale, 4, 0.5, 2.0);\n\n // \u57DF\u626D\u66F2\u589E\u52A0\u7EC6\u8282\n float warpIntensity = mix(0.1, 0.8, cloudTurbulence);\n float baseShape0 = fBm(animatedPos * preBaseScale, 3, 0.5, 2.0);\n float warped = domainWarpedNoise(animatedPos * (preBaseScale * 0.5), warpIntensity * 0.7);\n baseShape = mix(baseShape0, warped, 0.25);\n\n // Worley\u566A\u58F0\u521B\u5EFA\u56E2\u5757\u7ED3\u6784\n float worleyScale = mix(0.0002, 0.0008, cloudClumping);\n float worleyPattern = worleyNoise(animatedPos, worleyScale);\n worleyPattern = saturate(worleyPattern * 1.2 + 0.7);\n\n // \u6839\u636E\u4E91\u7C7B\u578B\u6DF7\u5408\n float cumulusFactor = cloudType;\n float stratusFactor = 1.0 - cloudType;\n\n float cumulusShape = baseShape * worleyPattern;\n cumulusShape = mix(cumulusShape, baseShape, 0.3);\n\n float stratusShape = baseShape * 0.8;\n\n float cloudPattern = mix(stratusShape, cumulusShape, cumulusFactor);\n\n // \u7827\u72B6\u4E91\u7279\u5F81\n float anvilFactor = cloudAnvil * pow(heightRatio, 2.0);\n if (anvilFactor > 0.01) {\n float anvilNoise = fBm(animatedPos * baseScale * 0.3, 3, 0.6, 2.0);\n cloudPattern = mix(cloudPattern, anvilNoise, anvilFactor * 0.5);\n }\n\n // \u7EC6\u8282\u4FB5\u8680\n float fineScale = baseScale * 16.0;\n float fineDetail = fBm(animatedPos * fineScale, 2, 0.7, 2.5);\n float erosionIntensity = mix(0.1, 0.4, cumulusFactor) * detailStrength;\n float erosion = fineDetail * erosionIntensity;\n\n // \u57FA\u7840\u5BC6\u5EA6\u8BA1\u7B97\n float baseDensity = cloudPattern - erosion;\n float coverageThreshold = mix(0.1, -0.3, cloudCover * (0.5 + cumulusFactor * 0.5));\n float dens = saturate(baseDensity - coverageThreshold);\n\n // \u9510\u5316\u8FB9\u754C\n float sharpness = mix(1.2, 2.0, cumulusFactor) * cloudSharpness;\n dens = pow(dens, sharpness);\n\n // \u5E94\u7528\u9AD8\u5EA6\u5206\u5E03\n float verticalProfile = cloudHeightProfile(heightRatio, cloudType);\n dens *= heightFactor * cloudCover * verticalProfile;\n\n // \u5E94\u7528\u96E8\u4E91\u7279\u5F81\n dens = rainCloudDensity(dens, heightRatio, rainClouds);\n\n // \u6700\u7EC8\u5BC6\u5EA6\u8C03\u6574\n dens *= densityMultiplier;\n dens = max(dens, 0.0);\n\n return dens;\n }\n float cloudDensityFast(vec3 p)\n {\n float h = length(p) - realPlanetRadius;\n float heightRatio = clamp((h - cloudHeight) / cloudThickness, 0.0, 1.0);\n\n // \u53EA\u505A\u6700\u57FA\u7840\u7684 shape\uFF0C\u4E0D\u505A Worley\u3001\u4E0D\u505A domain warp\uFF0C\u4E0D\u505A erosion\n float n = perlinNoise(p * 0.00035);\n n = n * n * 1.2; // \u7B80\u5355\u5F3A\u5316\n\n // \u9AD8\u5EA6\u5206\u5E03\n float profile = cloudHeightProfile(heightRatio, cloudType);\n\n return saturate(n * profile * cloudCover * densityMultiplier * 0.8);\n }\n // \u4F53\u79EF\u9634\u5F71\u51FD\u6570 - \u6A21\u62DF\u5149\u7EBF\u5728\u4E91\u5185\u7684\u8870\u51CF\n float volumetricShadow(vec3 p, vec3 lightDir, float stepSize, int steps) {\n float shadow = 1.0;\n vec3 currentPos = p;\n\n for (int i = 0; i < steps; i++) {\n currentPos += lightDir * stepSize;\n float heightRatioDummy;\n float shadowDensity = cloudDensityFast(currentPos);\n\n // \u6307\u6570\u8870\u51CF\u6A21\u62DF\u5149\u7EBF\u5728\u4E91\u5185\u7684\u5438\u6536\n shadow *= exp(-shadowDensity * stepSize * absorption * 2.0);\n\n if (shadow < 0.01) break;\n }\n\n return shadow;\n }\n\n // \u6539\u8FDB\u7684\u73AF\u5883\u5149\u7167\u51FD\u6570\n vec3 calculateAmbientLight(float heightRatio, float dens, float dotLightRay) {\n // \u57FA\u7840\u73AF\u5883\u5149 - \u6839\u636E\u9AD8\u5EA6\u6DF7\u5408\u5730\u9762\u548C\u5929\u7A7A\u989C\u8272\n vec3 baseAmbient = mix(groundAmbientColor, skyAmbientColor, heightRatio);\n\n // \u5BC6\u5EA6\u76F8\u5173\u73AF\u5883\u5149 - \u8584\u4E91\u533A\u57DF\u66F4\u4EAE\n float densityFactor = 1.0 - exp(-dens * 2.0);\n baseAmbient *= (1.0 + densityFactor * 0.5);\n\n // \u80CC\u5149\u589E\u5F3A - \u5F53\u89C6\u7EBF\u4E0E\u5149\u6E90\u65B9\u5411\u63A5\u8FD1\u53CD\u5411\u65F6\n float backLightFactor = smoothstep(-0.8, -0.2, dotLightRay);\n vec3 backLightBoost = skyAmbientColor * backLightFactor * 0.8;\n\n return baseAmbient + backLightBoost;\n }\n\n // \u6539\u8FDB\u7684\u591A\u6563\u5C04\u6A21\u62DF\n float calculateMultiScatter(float dens, float heightRatio, float dotLightRay) {\n // \u57FA\u7840\u591A\u6563\u5C04\n float baseMultiScatter = pow(1.0 - exp(-dens * 1.5), 1.5) * 0.4;\n\n // \u80CC\u5149\u533A\u57DF\u989D\u5916\u591A\u6563\u5C04\n float backScatterBoost = smoothstep(-0.6, 0.0, dotLightRay) * 0.3;\n\n // \u9AD8\u7A7A\u533A\u57DF\u6563\u5C04\u589E\u5F3A\n float heightScatter = heightRatio * 0.2;\n\n return baseMultiScatter + backScatterBoost + heightScatter;\n }\n\n float distanceQualityR = 0.00005;\n float minDistance = 10.0;\n\n // ===== \u8865\u5168\u7684\u4F53\u79EF\u6E32\u67D3\u6838\u5FC3\u51FD\u6570 =====\n vec4 calculate_clouds(vec3 start, vec3 dir, float maxDistance, vec3 light_dir, vec3 wind) {\n vec4 cloud = vec4(0.0, 0.0, 0.0, 1.0);\n float cloudTopRadius = realPlanetRadius + cloudHeight + cloudThickness;\n vec2 toTop = raySphereIntersect(start, dir, cloudTopRadius);\n float cloudBaseRadius = realPlanetRadius + cloudHeight;\n vec2 toCloudBase = raySphereIntersect(start, dir, cloudBaseRadius);\n float startHeight = length(start) - realPlanetRadius;\n\n float tmin = 0.0;\n float tmax = maxDistance;\n cloudTop = cloudHeight + cloudThickness;\n\n // \u8BA1\u7B97\u5C04\u7EBF\u4E0E\u4E91\u5C42\u7684\u4EA4\u70B9\n if (startHeight > cloudTop) {\n if (toTop.x < 0.0) return vec4(0.0);\n tmin = toTop.x;\n if (toCloudBase.x > 0.0) {\n tmax = min(toCloudBase.x, maxDistance);\n } else {\n tmax = min(toTop.y, maxDistance);\n }\n } else if (startHeight < cloudHeight) {\n tmin = toCloudBase.y;\n tmax = min(toTop.y, maxDistance);\n } else {\n if (toCloudBase.x > 0.0) {\n tmax = min(toCloudBase.x, maxDistance);\n } else {\n tmax = min(toTop.y, maxDistance);\n }\n }\n\n tmin = max(tmin, minDistance);\n tmax = min(tmax, absoluteMaxDistance);\n\n if (tmax < tmin) return vec4(0.0);\n\n float rayLength = tmax - tmin;\n float thicknessFactor = cloudThickness / 500.0;\n float baseStepSize = mix(CLOUDS_MARCH_STEP, CLOUDS_MARCH_STEP * 1.5, saturate(thicknessFactor - 1.0));\n\n float maxEffectiveSteps = min(float(MAXIMUM_CLOUDS_STEPS), 200.0 + cloudThickness * 0.2);\n float adaptiveSteps = min(maxEffectiveSteps, rayLength / baseStepSize);\n\n float longMarchStep = rayLength / adaptiveSteps;\n longMarchStep = max(longMarchStep, baseStepSize);\n\n float shortMarchStep = CLOUDS_DENS_MARCH_STEP;\n float numberApproachSteps = (longMarchStep / shortMarchStep) * 1.5;\n\n float distance = tmin;\n\n float jitter = hash12(gl_FragCoord.xy) * 1.2;\n distance += jitter * longMarchStep;\n\n bool inCloud = false;\n float stepsBeforeExitingCloud = 0.0;\n int actualSteps = 0;\n\n // \u9884\u8BA1\u7B97\u5149\u7167\u53C2\u6570\n vec3 sunColor = normalize(czm_lightColor);\n // ===== \u8865\u5168\u7684\u4F53\u79EF\u4ECB\u8D28\u5149\u7167\u8BA1\u7B97 =====\n float dotLightRay = dot(dir, light_dir);\n for (int i = 0; i < MAXIMUM_CLOUDS_STEPS; i++) {\n if (distance > tmax) break;\n if (actualSteps > int(adaptiveSteps)) break;\n\n vec3 position = start + dir * distance;\n float heightRatio;\n float marchStep = inCloud ? shortMarchStep : longMarchStep;\n float dens = cloudDensity(position, wind, heightRatio);\n\n // \u7A7A\u7A7A\u95F4\u8DF3\u8FC7\u4F18\u5316\n if (dens < 0.005) {\n if (inCloud) {\n if (stepsBeforeExitingCloud > 0.0) {\n stepsBeforeExitingCloud -= 1.0;\n } else {\n inCloud = false;\n marchStep = longMarchStep * 2.0;\n }\n }\n distance += marchStep;\n actualSteps++;\n continue;\n }\n\n // \u8FDB\u5165\u4E91\u5C42\u68C0\u6D4B\n if (!inCloud) {\n inCloud = true;\n stepsBeforeExitingCloud = numberApproachSteps;\n distance = max(tmin, distance - longMarchStep);\n continue;\n }\n\n // \u4F53\u79EF\u9634\u5F71\n float shadow = volumetricShadow(position, light_dir, shortMarchStep * 2.0, 8);\n\n // \u4F7F\u7528\u6539\u8FDB\u7684\u76F8\u4F4D\u51FD\u6570\n float sunPhase = improvedPhase(dotLightRay);\n float ambientPhase = isotropic();\n\n // \u9633\u5149\u5F3A\u5EA6\n float sunIntensity = smoothstep(0.0, 0.4, 1.0 - dens) * 0.6 * shadow;\n\n // \u73AF\u5883\u5149\u7167\n vec3 ambientLight = calculateAmbientLight(heightRatio, dens, dotLightRay);\n float ambientIntensity = 2.5;\n\n // \u591A\u6563\u5C04\n float multiScatter = calculateMultiScatter(dens, heightRatio, dotLightRay);\n\n // \u76F4\u63A5\u5149\u7167\n vec3 directLight = sunPhase * sunColor * sunIntensity * 0.5;\n\n // \u73AF\u5883\u5149\u7167\u6563\u5C04\n vec3 ambientScattering = ambientPhase * ambientLight * ambientIntensity * (1.0 + multiScatter);\n\n // \u4FA7\u5149\u548C\u80CC\u5149\u589E\u5F3A\n float sideBackLight = smoothstep(-0.5, 0.3, dotLightRay) * 0.4;\n ambientScattering += ambientLight * sideBackLight * multiScatter;\n\n // \u7EC4\u5408\u5149\u7167\n vec3 stepScattering = cloud.a * scatteringCoefficient * marchStep *\n (directLight + ambientScattering);\n\n // \u80FD\u91CF\u9650\u5236\n float maxStepBrightness = 0.25;\n stepScattering = min(stepScattering, vec3(maxStepBrightness));\n\n cloud.rgb += stepScattering;\n\n // \u900F\u660E\u5EA6\u8BA1\u7B97 - \u4F53\u79EF\u4ECB\u8D28\u7684\u6D88\u5149\n float extinctionCoeff = absorption * (0.8 + 0.2 * dens) * (0.6 + dens*0.4);\n cloud.a *= exp(-extinctionCoeff * marchStep);\n\n // \u63D0\u524D\u7EC8\u6B62\n if (cloud.a < 0.01) {\n cloud.a = 0.0;\n break;\n }\n\n distance += marchStep;\n actualSteps++;\n }\n\n // \u8272\u8C03\u6620\u5C04\n cloud.rgb = cloud.rgb / (cloud.rgb + vec3(0.8));\n\n // \u4EAE\u5EA6\u9650\u5236\n float maxChannel = max(cloud.rgb.r, max(cloud.rgb.g, cloud.rgb.b));\n if (maxChannel > 0.7) {\n float compression = smoothstep(0.7, 1.0, maxChannel) * 0.3;\n cloud.rgb = mix(cloud.rgb, cloud.rgb * (1.0 - compression), compression);\n }\n\n return vec4(cloud.rgb, 1.0 - cloud.a);\n }\n\n uniform sampler2D colorTexture;\n in vec2 v_textureCoordinates;\n\n void main() {\n vec4 color = texture(colorTexture, v_textureCoordinates);\n vec4 rawDepthColor = texture(czm_globeDepthTexture, v_textureCoordinates);\n\n float depth = czm_unpackDepth(rawDepthColor);\n if (depth == 0.0) {\n depth = 1.0;\n }\n\n vec4 positionEC = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);\n vec4 worldCoordinate = czm_inverseView * positionEC;\n vec3 vWorldPosition = worldCoordinate.xyz / worldCoordinate.w;\n vec3 posToEye = vWorldPosition - czm_viewerPositionWC;\n vec3 direction = normalize(posToEye);\n vec3 lightDirection = normalize(czm_sunPositionWC);\n float distance = length(posToEye);\n\n if (depth == 1.0) {\n distance = absoluteMaxDistance;\n }\n\n vec3 wind = windVector * czm_frameNumber * windSpeedRatio;\n vec4 clouds = calculate_clouds(\n czm_viewerPositionWC,\n direction,\n distance,\n lightDirection,\n wind\n );\n\n color = mix(color, clouds, clouds.a);\n out_FragColor = color;\n }\n ";
210
+ }
211
+
212
+ /**
213
+ * 获取真实星球半径 - 与原函数逻辑一致
214
+ * @private
215
+ * @returns {number} 星球半径
216
+ */
217
+ }, {
218
+ key: "_getRealPlanetRadius",
219
+ value: function _getRealPlanetRadius() {
220
+ if (this.viewer && this.viewer.scene && this.viewer.scene.globe) {
221
+ return this.viewer.scene.globe.ellipsoid.maximumRadius;
222
+ }
223
+ return 6378137.0; // 默认地球赤道半径
224
+ }
225
+
226
+ /**
227
+ * 更新体积云参数 - 确保所有原函数中的参数都能正确更新
228
+ * @param {Object} params - 要更新的参数对象
229
+ */
230
+ }, {
231
+ key: "updateParameters",
232
+ value: function updateParameters(params) {
233
+ if (!params || !this.postProcessStage) return;
234
+
235
+ // 更新uniforms参数
236
+ for (var key in params) {
237
+ if (this.uniforms.hasOwnProperty(key)) {
238
+ this.uniforms[key] = params[key];
239
+
240
+ // 特殊处理颜色参数
241
+ if (key === 'skyAmbientColor' && typeof params[key] === 'string') {
242
+ this.skyAmbientColor3 = this._hexToRgb(params[key]);
243
+ } else if (key === 'groundAmbientColor' && typeof params[key] === 'string') {
244
+ this.groundAmbientColor3 = this._hexToRgb(params[key]);
245
+ }
246
+ // 更新后处理阶段的uniforms
247
+ if (this.postProcessStage.uniforms[key] !== undefined) {
248
+ this.postProcessStage.uniforms[key] = params[key];
249
+ }
250
+ }
251
+ }
252
+ }
253
+
254
+ /**
255
+ * 将十六进制颜色转换为RGB对象
256
+ * @private
257
+ * @param {string} hex - 十六进制颜色字符串
258
+ * @returns {Object} RGB对象
259
+ */
260
+ }, {
261
+ key: "_hexToRgb",
262
+ value: function _hexToRgb(hex) {
263
+ // 移除#号
264
+ hex = hex.replace('#', '');
265
+
266
+ // 解析RGB值
267
+ var r = parseInt(hex.substring(0, 2), 16) / 255;
268
+ var g = parseInt(hex.substring(2, 4), 16) / 255;
269
+ var b = parseInt(hex.substring(4, 6), 16) / 255;
270
+ return {
271
+ r: r,
272
+ g: g,
273
+ b: b
274
+ };
275
+ }
276
+
277
+ /**
278
+ * 重置体积云参数到初始状态
279
+ */
280
+ }, {
281
+ key: "resetParameters",
282
+ value: function resetParameters() {
283
+ this.uniforms = {
284
+ densityMultiplier: this.originalOptions.densityMultiplier || 1.5,
285
+ detailStrength: this.originalOptions.detailStrength || 0.69,
286
+ cloudSharpness: this.originalOptions.cloudSharpness || 1.032,
287
+ rainClouds: this.originalOptions.rainClouds || 0.45,
288
+ currentWindVectorWC: this.originalOptions.currentWindVectorWC || new Cesium.Cartesian3(100, 0, 0),
289
+ cloudCover: this.originalOptions.cloudCover || 0.039,
290
+ cloudHeight: this.originalOptions.cloudHeight || 8000.0,
291
+ cloudThickness: this.originalOptions.cloudThickness || 1000.0,
292
+ absoluteMaxDistance: this.originalOptions.absoluteMaxDistance || 50000000,
293
+ absorption: this.originalOptions.absorption || 0.004,
294
+ windSpeedRatio: this.originalOptions.windSpeedRatio || 0.3,
295
+ scatteringCoefficient: this.originalOptions.scatteringCoefficient || 0.01,
296
+ illuminationCoefficient: this.originalOptions.illuminationCoefficient || 0.01,
297
+ cloudSeed: this.originalOptions.cloudSeed || Math.random() * 100.0,
298
+ cloudType: this.originalOptions.cloudType || 0,
299
+ cloudTurbulence: this.originalOptions.cloudTurbulence || 0.5,
300
+ cloudClumping: this.originalOptions.cloudClumping || 0.75,
301
+ cloudAnvil: this.originalOptions.cloudAnvil || 0,
302
+ skyAmbientColor: this.originalOptions.skyAmbientColor || '#B4D9F3',
303
+ groundAmbientColor: this.originalOptions.groundAmbientColor || '#d6d6d8'
304
+ };
305
+
306
+ // 重置颜色对象
307
+ this.skyAmbientColor3 = {
308
+ r: 0.705,
309
+ g: 0.85,
310
+ b: 0.952
311
+ };
312
+ this.groundAmbientColor3 = {
313
+ r: 0.839,
314
+ g: 0.839,
315
+ b: 0.847
316
+ };
317
+
318
+ // 更新后处理阶段的参数
319
+ this.updateParameters(this.uniforms);
320
+ }
321
+
322
+ /**
323
+ * 显示体积云
324
+ *
325
+ * 启用后处理阶段,使体积云在场景中可见。
326
+ * 这是一个轻量级操作,只是启用现有的后处理效果。
327
+ */
328
+ }, {
329
+ key: "show",
330
+ value: function show() {
331
+ if (this.postProcessStage) {
332
+ this.postProcessStage.enabled = true;
333
+ }
334
+ }
335
+
336
+ /**
337
+ * 隐藏体积云
338
+ *
339
+ * 禁用后处理阶段,从场景中移除体积云效果。
340
+ * 这是一个轻量级操作,不会销毁资源,可以随时通过show()重新启用。
341
+ */
342
+ }, {
343
+ key: "hide",
344
+ value: function hide() {
345
+ if (this.postProcessStage) {
346
+ this.postProcessStage.enabled = false;
347
+ }
348
+ }
349
+
350
+ /**
351
+ * 移除体积云并清理资源
352
+ *
353
+ * 从场景中完全移除体积云后处理阶段并释放相关资源。
354
+ * 调用此方法后,必须重新创建新的实例才能再次使用体积云功能。
355
+ */
356
+ }, {
357
+ key: "destroy",
358
+ value: function destroy() {
359
+ if (this.postProcessStage && this.viewer.scene.postProcessStages.contains(this.postProcessStage)) {
360
+ this.viewer.scene.postProcessStages.remove(this.postProcessStage);
361
+ this.postProcessStage = null;
362
+ }
363
+ }
364
+
365
+ /**
366
+ * 获取当前云参数
367
+ *
368
+ * 返回当前体积云系统的所有配置参数的副本。
369
+ * 可以用于检查当前设置或保存配置以便稍后恢复。
370
+ *
371
+ * @returns {Object} 当前云参数对象的副本,包含所有19个可配置参数
372
+ */
373
+ }, {
374
+ key: "getParameters",
375
+ value: function getParameters() {
376
+ return _objectSpread({}, this.uniforms);
377
+ }
378
+ }]);
379
+ return GlobalVolumetricCloud;
380
+ }();
381
+ export default GlobalVolumetricCloud;