@realsee/five 6.8.0-alpha.8 → 6.8.0-alpha.9

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.
Files changed (60) hide show
  1. package/ai_guides/README.md +3 -2
  2. package/ai_guides/features/flowing-light-2d-pass.md +278 -58
  3. package/ai_guides/features/flowing-light-3d-pass.md +223 -43
  4. package/docs/documents/README.html +2 -2
  5. package/docs/documents/features_flowing-light-2d-pass.html +275 -43
  6. package/docs/documents/features_flowing-light-3d-pass.html +268 -46
  7. package/docs/index.html +2 -2
  8. package/five/index.js +417 -383
  9. package/five/index.mjs +4822 -4821
  10. package/five/model/loaders/glTF-helpers/extensions/CESIUM_RTC.d.ts +1 -1
  11. package/five/model/loaders/glTF-helpers/extensions/EXT_meshopt_compression.d.ts +1 -1
  12. package/five/model/loaders/glTF-helpers/extensions/EXT_texture_webp.d.ts +1 -1
  13. package/five/model/loaders/glTF-helpers/extensions/KHR_binary_glTF.d.ts +1 -1
  14. package/five/model/loaders/glTF-helpers/extensions/KHR_draco_mesh_compression.d.ts +1 -1
  15. package/five/model/loaders/glTF-helpers/extensions/KHR_gaussian_splatting_compression_spz.d.ts +1 -1
  16. package/five/model/loaders/glTF-helpers/extensions/KHR_materials_clearcoat.d.ts +4 -0
  17. package/five/model/loaders/glTF-helpers/extensions/KHR_materials_emissive_strength.d.ts +4 -0
  18. package/five/model/loaders/glTF-helpers/extensions/KHR_materials_sheen.d.ts +4 -0
  19. package/five/model/loaders/glTF-helpers/extensions/KHR_materials_transmission.d.ts +4 -0
  20. package/five/model/loaders/glTF-helpers/extensions/KHR_materials_unlit.d.ts +4 -0
  21. package/five/model/loaders/glTF-helpers/extensions/KHR_node_visibility.d.ts +4 -0
  22. package/five/model/loaders/glTF-helpers/extensions/KHR_texture_basisu.d.ts +1 -1
  23. package/five/model/loaders/glTF-helpers/extensions/KHR_texture_transform.d.ts +1 -1
  24. package/five/model/loaders/glTF-helpers/extensions/REALSEE_materials_lightmap.d.ts +1 -1
  25. package/five/model/loaders/glTF-helpers/extensions/base.d.ts +2 -1
  26. package/five/model/loaders/glTF-helpers/index.d.ts +0 -2
  27. package/five/model/loaders/glTF-helpers/parser.d.ts +14 -13
  28. package/five/renderer/postprocessing/passes/flowing-light-2d-pass.d.ts +16 -2
  29. package/five/renderer/postprocessing/passes/flowing-light-3d-pass.d.ts +16 -2
  30. package/five/renderer/postprocessing/passes/gaussian-blur-pass.d.ts +1 -1
  31. package/gltf-loader/index.js +3 -3
  32. package/gltf-loader/index.mjs +3 -3
  33. package/line/index.js +3 -3
  34. package/line/index.mjs +3 -3
  35. package/llms.txt +3 -2
  36. package/package.json +1 -1
  37. package/plugins/index.js +2 -2
  38. package/plugins/index.mjs +2 -2
  39. package/react/index.js +2 -2
  40. package/react/index.mjs +2 -2
  41. package/shader-lib/index.js +2 -2
  42. package/shader-lib/index.mjs +2 -2
  43. package/sticker/index.js +3 -3
  44. package/sticker/index.mjs +3 -3
  45. package/umd/five-gltf-loader.js +3 -3
  46. package/umd/five-line.js +3 -3
  47. package/umd/five-plugins.js +2 -2
  48. package/umd/five-react.js +2 -2
  49. package/umd/five-shader-lib.js +2 -2
  50. package/umd/five-sticker.js +3 -3
  51. package/umd/five-vfx.js +2 -2
  52. package/umd/five-vue.js +2 -2
  53. package/umd/five.js +417 -383
  54. package/vfx/index.js +2 -2
  55. package/vfx/index.mjs +2 -2
  56. package/vue/index.js +2 -2
  57. package/vue/index.mjs +2 -2
  58. package/work-downloader/index.js +2 -2
  59. package/work-downloader/index.mjs +2 -2
  60. package/five/model/loaders/glTF-helpers/extensions/PBM_mesh.d.ts +0 -11
@@ -74,8 +74,8 @@
74
74
  * [Pano Tile](./features/pano-tile.md): 全景瓦片渲染机制(高分辨率分片加载)。
75
75
  * [Model](./features/model.md): 内置模型渲染 (Mesh/Geometry)。
76
76
  * [Postprocessing](./features/postprocessing.md): 后处理效果 (Pass, EffectComposer)。
77
- * [Flowing Light 2D Pass](./features/flowing-light-2d-pass.md): 屏幕空间流光特效。
78
- * [Flowing Light 3D Pass](./features/flowing-light-3d-pass.md): 基于模型深度的流光特效。
77
+ * [Flowing Light 2D Pass](./features/flowing-light-2d-pass.md): 屏幕空间流光特效(InstancedMesh 优化)。
78
+ * [Flowing Light 3D Pass](./features/flowing-light-3d-pass.md): 3D 世界坐标流光特效(InstancedMesh 优化)。
79
79
  * [Gaussian Blur Pass](./features/gaussian-blur-pass.md): 高斯模糊特效。
80
80
  * [Material](./features/material.md): 材质参数配置 (透明度、点云大小、顶点标记)。
81
81
  * [Get Screen Pixels](./features/get-screen-pixels.md): 获取屏幕像素 (放大镜/截图)。
@@ -119,3 +119,4 @@
119
119
  ```yaml
120
120
  tags: [index, readme, guide, map]
121
121
  ```
122
+
@@ -1,16 +1,16 @@
1
1
  # Flowing Light 2D Pass (流光 2D 通道)
2
2
 
3
- - **Summary**: `FlowingLight2DPass` 提供屏幕空间的流光(光带)特效,沿着 2D 路径点绘制动态光流,适用于突出导航路径、指示线或动态路径演示。
3
+ - **Summary**: `FlowingLight2DPass` 提供屏幕空间的流光(光带)特效,使用 InstancedMesh 实例化渲染优化性能,沿着 2D 归一化坐标路径绘制动态光流,适用于突出导航路径、指示线或动态路径演示。
4
4
  - **Schema**: `FlowingLight2DPass` 类及 Line 数据结构。
5
- - **Concepts**: 后处理通道、屏幕空间光带、路径数据驱动、时间动画。
6
- - **Configuration**: 路径点集合、颜色、不透明度、动画时长。
7
- - **Examples**: 基础集成、多条路径、动态更新。
5
+ - **Concepts**: 后处理通道、屏幕空间光带、InstancedMesh 实例化渲染、归一化坐标系统、头尾追逐动画。
6
+ - **Configuration**: 归一化路径点、颜色、不透明度、线宽、动画时长、延迟。
7
+ - **Examples**: 基础集成、多条路径、动态更新、高 DPR 场景优化。
8
8
 
9
9
  ## Schema
10
10
 
11
11
  > **Definition**: [FlowingLight2DPass](../../five/renderer/postprocessing/passes/flowing-light-2d-pass.d.ts)
12
12
 
13
- `FlowingLight2DPass` 是 `FivePass` 的子类,核心接口如下:
13
+ `FlowingLight2DPass` 是 `FivePass` 的子类,核心接口如下:
14
14
 
15
15
  ```ts
16
16
  import * as THREE from 'three';
@@ -18,11 +18,15 @@ import { FivePass } from './pass';
18
18
  import { type Camera } from '../../../core/camera';
19
19
 
20
20
  type Line = {
21
- points: THREE.Vector2[]; // 屏幕空间路径点 [x, y]
22
- totalLength: number; // 路径总长度(像素)
21
+ id?: string; // 可选的唯一标识符,不传则自动生成 UUID
22
+ points: THREE.Vector2[]; // 归一化坐标路径点 [0, 1]
23
+ totalLength: number; // 路径总长度(归一化单位)
23
24
  color: THREE.Color; // 光带颜色
24
- opacity?: number; // 光带不透明度(0-1
25
- duration?: number; // 动画时长(毫秒)
25
+ opacity?: number; // 光带不透明度(0-1)
26
+ duration?: number; // 动画时长(毫秒)
27
+ delay?: number; // 动画延迟(毫秒)
28
+ lineWidth?: number; // 线宽(归一化单位)
29
+ tailLengthRatio?: number; // 尾巴长度比例(0-1),默认 0.2
26
30
  };
27
31
 
28
32
  export class FlowingLight2DPass extends FivePass {
@@ -31,6 +35,9 @@ export class FlowingLight2DPass extends FivePass {
31
35
  // 设置要渲染的路径列表
32
36
  public setLines(lines: Line[]): void;
33
37
 
38
+ // 修改单条线的参数(通过 id)
39
+ public setLine(params: { id: string } & Partial<Omit<Line, 'id'>>): void;
40
+
34
41
  public render(
35
42
  renderer: THREE.WebGLRenderer,
36
43
  writeBuffer: THREE.WebGLRenderTarget,
@@ -42,17 +49,36 @@ export class FlowingLight2DPass extends FivePass {
42
49
  public dispose(): void;
43
50
  }
44
51
  ```
45
-
46
52
  ## Concepts
47
53
 
48
- ### 屏幕空间路径 (Screen-space Lines)
49
- 路径点在屏幕坐标系中定义(像素单位),不受 3D 场景变化影响。光流沿路径顶点顺序流动,自动计算相邻顶点间距离。
54
+ ### InstancedMesh 实例化渲染 (Instanced Rendering)
55
+ 为了优化高 DPR(Device Pixel Ratio)场景下的性能,该 Pass 使用 InstancedMesh 代替全屏着色器。每个线段作为一个实例,只渲染线段覆盖的区域,避免了对每个像素的全屏检查,显著提升了渲染效率。
56
+
57
+ ### 归一化坐标系统 (Normalized Coordinates)
58
+ 路径点使用归一化坐标 `[0, 1]` 定义:
59
+ - `(0, 0)` 表示屏幕左上角
60
+ - `(1, 1)` 表示屏幕右下角
61
+ - `(0.5, 0.5)` 表示屏幕中心
62
+
63
+ 这种坐标系统与屏幕分辨率无关,自动适配不同尺寸和 DPR。
64
+
65
+ ### 头尾追逐动画 (Head-Tail Chase Animation)
66
+ 流光效果采用"头尾追逐"模式:
67
+ - **头部(Head)**: 光流的前端,沿路径循环移动
68
+ - **尾部(Tail)**: 光流的后端,跟随头部移动
69
+ - **渐变**: 从尾部(透明度 0)到头部(透明度 1)线性渐变
70
+ - **尾长**: 尾部长度由 `tailLengthRatio` 参数控制(默认 0.2,即路径总长的 20%)
50
71
 
51
- ### 时间驱动光头 (Time-driven Head)
52
- 光带"头部"根据 `duration` 和经过时间循环移动。`duration` 定义一个完整周期(从起点到终点再回到起点)所需的毫秒数。光带尾部长度固定为路径总长的 1/4。
72
+ 光头在路径上循环流动,通过像素空间投影计算每个像素的渐变位置,确保渐变效果准确。尾巴长度会在动画开始时逐渐增长,直到达到 `tailLengthRatio * totalLength`。
53
73
 
54
- ### 数据纹理驱动 (Data Texture)
55
- 路径数据编码为 Float32 纹理上传到 GPU,使 shader 能高效计算光效。支持同时渲染多条路径。
74
+ ### NDC 坐标转换
75
+ 内部实现中,归一化坐标 `[0, 1]` 会转换为 NDC(Normalized Device Coordinates) `[-1, 1]`,与 WebGL 标准坐标系统一致,确保投影计算准确。
76
+
77
+ ### DPR 处理与宽高比适配
78
+ 自动处理高 DPR 场景和不同屏幕宽高比:
79
+ - **DPR 补偿**: 通过 `uPixelRatio` uniform 自动补偿高 DPR 设备的光线宽度,确保在 Retina 等高分辨率屏幕上视觉一致
80
+ - **像素空间计算**: 所有距离计算在像素空间进行,而非 NDC 空间,确保横向和纵向光线宽度在任何宽高比下都保持一致
81
+ - **各向同性渲染**: 无论屏幕是横屏、竖屏还是方形,光线的扩散效果在各个方向上都保持相同
56
82
 
57
83
  ## Configuration
58
84
 
@@ -60,49 +86,108 @@ export class FlowingLight2DPass extends FivePass {
60
86
 
61
87
  | Parameter | Type | Required | Default | Description |
62
88
  | :--- | :--- | :--- | :--- | :--- |
63
- | `points` | `THREE.Vector2[]` | | - | 屏幕空间路径点数组。至少 2 个点。 |
64
- | `totalLength` | `number` | ✓ | - | 路径总长度(像素)。通常为所有相邻点间距离之和。 |
65
- | `color` | `THREE.Color` | ✓ | - | 光带颜色(RGB)。 |
66
- | `opacity` | `number` | | `1` | 光带不透明度(0-1)。控制与背景的混合强度。 |
67
- | `duration` | `number` | | `1000` | 动画周期(毫秒)。光头从路径起点完成一个周期的时长。 |
68
-
69
- > **注意**: `totalLength` 应精确计算。若传入错误值,光流速度会显著偏差。
89
+ | `id` | `string` | | 自动生成 UUID | 唯一标识符,用于 `setLine` 方法更新单条线。 |
90
+ | `points` | `THREE.Vector2[]` | ✓ | - | 归一化坐标路径点数组 [0, 1]。至少 2 个点。 |
91
+ | `totalLength` | `number` | ✓ | - | 路径总长度(归一化单位)。通常为所有相邻点间距离之和。 |
92
+ | `color` | `THREE.Color` | | - | 光带颜色(RGB)。 |
93
+ | `opacity` | `number` | | `1.0` | 光带不透明度(0-1)。控制与背景的混合强度。 |
94
+ | `duration` | `number` | | `1000` | 动画周期(毫秒)。光头从路径起点完成一个周期的时长。 |
95
+ | `delay` | `number` | | `0` | 动画延迟(毫秒)。延迟后才开始播放动画。 |
96
+ | `lineWidth` | `number` | | `0.01` | 线宽(归一化单位)。控制光带在屏幕上的宽度。 |
97
+ | `tailLengthRatio` | `number` | | `0.2` | 尾巴长度比例(0-1)。控制流光尾巴相对于路径总长的比例。 |
98
+
99
+ > **注意**: `totalLength` 和 `lineWidth` 使用归一化单位,与屏幕实际像素无关。
70
100
 
71
101
  ## Instance Methods
72
102
 
73
103
  ### `constructor(camera: Camera)`
74
- 初始化 Pass,需传入相机以获取屏幕分辨率。
104
+ 初始化 Pass,需传入相机以获取屏幕分辨率。
75
105
 
76
106
  ### `setLines(lines: Line[])`
77
107
  更新要渲染的路径列表。可随时调用以修改或新增路径。
78
108
 
79
- **示例**:
109
+ **自动生成 ID**: 如果 line 没有提供 `id`,会自动生成 UUID。
110
+
111
+ **示例**:
80
112
  ```ts
81
113
  const pathLine: Line = {
114
+ id: 'my-line-1', // 可选,不传则自动生成
82
115
  points: [
83
- new THREE.Vector2(100, 100),
84
- new THREE.Vector2(300, 150),
85
- new THREE.Vector2(500, 100),
116
+ new THREE.Vector2(0.3, 0.7),
117
+ new THREE.Vector2(0.7, 0.7),
118
+ new THREE.Vector2(0.7, 0.3),
86
119
  ],
87
- totalLength: 400, // 实际路径总长度
120
+ totalLength: 0.8, // 归一化单位
88
121
  color: new THREE.Color(0x00c2ff),
89
122
  opacity: 0.8,
90
123
  duration: 2000,
124
+ delay: 500,
125
+ lineWidth: 0.01,
91
126
  };
92
127
  pass.setLines([pathLine]);
93
128
  ```
94
129
 
130
+ ### `setLine(params: { id: string } & Partial<Omit<Line, 'id'>>)`
131
+ 通过 `id` 修改单条线的参数。支持部分更新,只更新传入的字段。
132
+
133
+ **性能优化**:
134
+ - 如果 `points` 数量不变,只更新 GPU 的 instance attributes,性能极高
135
+ - 如果 `points` 数量改变,会重建整个 mesh,性能开销较大
136
+
137
+ **示例**:
138
+ ```ts
139
+ // 只修改颜色 - 高性能
140
+ pass.setLine({
141
+ id: 'my-line-1',
142
+ color: new THREE.Color(0xff0000),
143
+ });
144
+
145
+ // 修改多个属性 - 高性能
146
+ pass.setLine({
147
+ id: 'my-line-1',
148
+ color: new THREE.Color(0x00ff00),
149
+ opacity: 0.5,
150
+ duration: 3000,
151
+ });
152
+
153
+ // 修改 points (相同数量) - 高性能
154
+ pass.setLine({
155
+ id: 'my-line-1',
156
+ points: [
157
+ new THREE.Vector2(0.2, 0.2),
158
+ new THREE.Vector2(0.8, 0.8),
159
+ new THREE.Vector2(0.8, 0.2),
160
+ ], // 仍是 3 个点
161
+ totalLength: 1.2,
162
+ });
163
+
164
+ // 修改 points (不同数量) - 会重建 mesh
165
+ pass.setLine({
166
+ id: 'my-line-1',
167
+ points: [
168
+ new THREE.Vector2(0.2, 0.2),
169
+ new THREE.Vector2(0.8, 0.8),
170
+ ], // 从 3 个点变为 2 个点
171
+ totalLength: 0.85,
172
+ });
173
+ ```
174
+
175
+ **注意事项**:
176
+ - 如果 `id` 不存在,会在控制台输出警告
177
+ - 动画时间(`uTime`)不会被重置,流光继续从当前位置播放
178
+ - 建议修改 `points` 时同时更新 `totalLength`
179
+
95
180
  ### `render(...)`
96
- 由 EffectComposer 自动调用,无需手动调用。
181
+ 由 EffectComposer 自动调用,无需手动调用。自动同步渲染目标尺寸。
97
182
 
98
183
  ### `dispose()`
99
- 释放着色材质和纹理资源,避免内存泄漏。销毁 Pass 时必须调用。
184
+ 释放 InstancedMesh、着色材质和渲染目标资源,避免内存泄漏。销毁 Pass 时必须调用。
100
185
 
101
186
  ## Examples
102
187
 
103
188
  ### 快速上手 (Quick Example)
104
189
 
105
- ```ts
190
+
106
191
  import { Five } from '@realsee/five';
107
192
  import { FlowingLight2DPass } from '../../lib/five/renderer/postprocessing';
108
193
  import * as THREE from 'three';
@@ -112,14 +197,13 @@ const five = new Five();
112
197
  // 创建 2D 流光通道
113
198
  const flowing2D = new FlowingLight2DPass(five.camera);
114
199
 
115
- // 定义一条屏幕空间的路径
200
+ // 定义一条屏幕空间的路径(归一化坐标)
116
201
  const path = [
117
- new THREE.Vector2(100, 100),
118
- new THREE.Vector2(300, 200),
119
- new THREE.Vector2(500, 100),
120
- new THREE.Vector2(700, 250),
202
+ new THREE.Vector2(0.2, 0.2),
203
+ new THREE.Vector2(0.5, 0.8),
204
+ new THREE.Vector2(0.8, 0.2),
121
205
  ];
122
- const totalLen = 600; // 近似路径长度
206
+ const totalLen = 1.2; // 归一化单位
123
207
 
124
208
  flowing2D.setLines([{
125
209
  points: path,
@@ -127,6 +211,7 @@ flowing2D.setLines([{
127
211
  color: new THREE.Color(0x00ff00),
128
212
  opacity: 0.9,
129
213
  duration: 2000,
214
+ lineWidth: 0.01,
130
215
  }]);
131
216
 
132
217
  five.addPass(flowing2D);
@@ -136,27 +221,36 @@ function animate() {
136
221
  five.render();
137
222
  }
138
223
  animate();
139
- ```
224
+
140
225
 
141
226
  ### 多条路径 & 动态更新
142
227
 
143
- ```ts
228
+
144
229
  const pass = new FlowingLight2DPass(five.camera);
145
230
 
146
231
  // 初始路径集合
147
232
  const paths = [
148
233
  {
149
- points: [...],
150
- totalLength: 500,
234
+ points: [
235
+ new THREE.Vector2(0.1, 0.1),
236
+ new THREE.Vector2(0.9, 0.1),
237
+ ],
238
+ totalLength: 0.8,
151
239
  color: new THREE.Color(0xff6600),
152
240
  duration: 1500,
241
+ lineWidth: 0.01,
153
242
  },
154
243
  {
155
- points: [...],
156
- totalLength: 400,
244
+ points: [
245
+ new THREE.Vector2(0.1, 0.5),
246
+ new THREE.Vector2(0.9, 0.5),
247
+ ],
248
+ totalLength: 0.8,
157
249
  color: new THREE.Color(0x0066ff),
158
250
  opacity: 0.7,
159
251
  duration: 2000,
252
+ delay: 500,
253
+ lineWidth: 0.015,
160
254
  },
161
255
  ];
162
256
 
@@ -165,19 +259,27 @@ five.addPass(pass);
165
259
 
166
260
  // 响应用户交互动态更新路径
167
261
  document.addEventListener('click', (e) => {
262
+ const rect = five.canvas.getBoundingClientRect();
263
+ const x = (e.clientX - rect.left) / rect.width;
264
+ const y = (e.clientY - rect.top) / rect.height; // 左上角为原点,无需翻转
265
+
168
266
  const newPath = {
169
- points: [/* 鼠标点击生成的路径点 */],
170
- totalLength: 300,
267
+ points: [
268
+ new THREE.Vector2(0.5, 0.5),
269
+ new THREE.Vector2(x, y),
270
+ ],
271
+ totalLength: Math.hypot(x - 0.5, y - 0.5),
171
272
  color: new THREE.Color(Math.random() * 0xffffff),
172
273
  duration: 1200,
274
+ lineWidth: 0.01,
173
275
  };
174
276
  pass.setLines([...paths, newPath]);
175
277
  });
176
- ```
278
+
177
279
 
178
280
  ### 计算路径总长度
179
281
 
180
- ```ts
282
+
181
283
  function calculatePathLength(points: THREE.Vector2[]): number {
182
284
  let length = 0;
183
285
  for (let i = 1; i < points.length; i++) {
@@ -186,25 +288,144 @@ function calculatePathLength(points: THREE.Vector2[]): number {
186
288
  return length;
187
289
  }
188
290
 
291
+ const points = [
292
+ new THREE.Vector2(0.3, 0.7),
293
+ new THREE.Vector2(0.7, 0.7),
294
+ new THREE.Vector2(0.7, 0.3),
295
+ new THREE.Vector2(0.3, 0.3),
296
+ new THREE.Vector2(0.3, 0.7),
297
+ ];
298
+
189
299
  const pathLine = {
190
- points: [...],
300
+ points: points,
191
301
  totalLength: calculatePathLength(points),
192
302
  color: new THREE.Color(0x00ffff),
193
303
  duration: 2000,
304
+ lineWidth: 0.01,
305
+ };
306
+
307
+
308
+ ### 矩形路径示例
309
+
310
+ ```ts
311
+ const pass = new FlowingLight2DPass(five.camera);
312
+
313
+ // 创建一个矩形路径(注意:左上角为原点)
314
+ const rectPath = {
315
+ points: [
316
+ new THREE.Vector2(0.3, 0.3), // 左上
317
+ new THREE.Vector2(0.7, 0.3), // 右上
318
+ new THREE.Vector2(0.7, 0.7), // 右下
319
+ new THREE.Vector2(0.3, 0.7), // 左下
320
+ new THREE.Vector2(0.3, 0.3), // 闭合路径
321
+ ],
322
+ totalLength: 1.6, // 矩形周长
323
+ color: new THREE.Color(1.0, 1.0, 1.0),
324
+ opacity: 1.0,
325
+ duration: 4000,
326
+ delay: 2000,
327
+ lineWidth: 0.01,
194
328
  };
329
+
330
+ pass.setLines([rectPath]);
195
331
  ```
196
332
 
333
+ ### 高 DPR 场景优化
334
+
335
+
336
+ // InstancedMesh 实现自动优化高 DPR 性能
337
+ // 在 DPR=2 的 Retina 屏幕上,性能显著优于全屏着色器方案
338
+
339
+ const pass = new FlowingLight2DPass(camera);
340
+
341
+ // 多条路径在高 DPR 下仍能保持流畅
342
+ const paths = Array.from({ length: 10 }, (_, i) => ({
343
+ points: [
344
+ new THREE.Vector2(0.1, 0.1 + i * 0.08),
345
+ new THREE.Vector2(0.9, 0.1 + i * 0.08),
346
+ ],
347
+ totalLength: 0.8,
348
+ color: new THREE.Color(Math.random() * 0xffffff),
349
+ duration: 2000 + i * 100,
350
+ delay: i * 200,
351
+ lineWidth: 0.01,
352
+ }));
353
+
354
+ pass.setLines(paths);
355
+
356
+
197
357
  ## Debugging
198
358
 
199
- - **光带不出现**:检查 `color` 和 `opacity`,确保非全透明;验证 `points` 至少 2 个。
200
- - **流速不对**:检查 `totalLength` 计算是否准确;若流速过快/过慢,调整 `duration`。
201
- - **性能问题**:降低路径点数或使用更简单的路径。
359
+ - **光带不出现**: 检查 `color` 和 `opacity`,确保非全透明;验证 `points` 至少 2 个;确认坐标在 [0, 1] 范围内。
360
+ - **流速不对**: 检查 `totalLength` 计算是否准确;若流速过快/过慢,调整 `duration`。
361
+ - **位置偏移**: 确认坐标系统使用归一化坐标 [0, 1],不是像素坐标;注意坐标系统以左上角为原点,Y 轴方向为 0(上) 到 1(下)。
362
+ - **性能问题**: InstancedMesh 实现已优化,但过多路径点仍会影响性能。建议单条路径不超过 20 个点。
363
+
364
+ ## Performance
365
+
366
+ ### 与全屏着色器对比
367
+ - **全屏着色器**: 每帧检查所有像素,DPR=2 时像素数增加 4 倍,性能下降明显
368
+ - **InstancedMesh**: 仅渲染线段覆盖区域,DPR=2 时性能影响较小
369
+ - **实测**: 在 DPR=2 场景下,InstancedMesh 方案 FPS 提升约 3-5 倍
370
+
371
+ ### setLine 性能优化
372
+ `setLine` 方法根据修改类型自动选择最优更新策略:
373
+
374
+ | 修改类型 | 性能 | 说明 |
375
+ |---------|------|------|
376
+ | 只修改颜色/透明度 | ⚡️ 极快 (~100x) | 只更新 instanceColor attribute |
377
+ | 只修改 duration/delay | ⚡️ 极快 (~100x) | 只更新 instanceMeta attribute |
378
+ | 只修改 lineWidth | ⚡️ 极快 (~100x) | 只更新 instanceData attribute |
379
+ | 修改 points (相同数量) | ⚡️ 快 (~50x) | 只更新 instanceStart/End attributes |
380
+ | 修改 points (不同数量) | ⚠️ 慢 | 重建整个 mesh |
381
+
382
+ **最佳实践**:
383
+ ```ts
384
+ // ✅ 高性能 - 频繁修改颜色
385
+ requestAnimationFrame(() => {
386
+ pass.setLine({
387
+ id: lineId,
388
+ color: new THREE.Color(Math.random(), Math.random(), Math.random())
389
+ });
390
+ });
391
+
392
+ // ✅ 高性能 - 动画路径(保持点数)
393
+ function animatePath(t: number) {
394
+ pass.setLine({
395
+ id: lineId,
396
+ points: [
397
+ new THREE.Vector2(0.1, 0.1),
398
+ new THREE.Vector2(0.5 + Math.sin(t) * 0.2, 0.5),
399
+ new THREE.Vector2(0.9, 0.9),
400
+ ] // 始终 3 个点
401
+ });
402
+ }
403
+
404
+ // ⚠️ 低性能 - 避免频繁改变点数
405
+ setInterval(() => {
406
+ const pointCount = Math.floor(Math.random() * 5) + 2;
407
+ pass.setLine({
408
+ id: lineId,
409
+ points: generateRandomPoints(pointCount) // 点数不固定
410
+ });
411
+ }, 100);
412
+ ```
413
+
414
+ ### 优化建议
415
+ 1. 减少路径点数量,使用更简化的路径
416
+ 2. 降低 `lineWidth`,减少渲染区域
417
+ 3. 控制同时渲染的路径数量
418
+ 4. 使用 `setLine` 而非 `setLines` 来更新单条线
419
+ 5. 保持 points 数量不变,只修改坐标
420
+ 6. 在低端设备上可通过 `pass.enabled = false` 禁用
202
421
 
203
422
  ## Common Pitfalls
204
423
 
205
- 1. **totalLength 计算错误**:光流速度直接依赖 `totalLength`。手动计算时务必精确。
206
- 2. **屏幕坐标系混淆**:`points` 使用屏幕坐标(原点通常在左下角),不是 Canvas 默认坐标系。
207
- 3. **未及时 dispose()**:移除 Pass 时必须调用 `dispose()`,否则 GPU 纹理资源泄漏。
424
+ 1. **坐标系统混淆**: 必须使用归一化坐标 [0, 1],不能使用像素坐标。坐标系统以左上角为原点,Y 轴方向为 0(上) 到 1(下)。
425
+ 2. **totalLength 计算错误**: 光流速度直接依赖 `totalLength`。手动计算时务必精确,使用归一化单位。
426
+ 3. **lineWidth 过大**: 归一化单位下,0.01 约为屏幕高度的 1%。过大的值会导致光带过宽。
427
+ 4. **未及时 dispose()**: 移除 Pass 时必须调用 `dispose()`,否则 GPU 资源泄漏。
428
+ 5. **DPR 不匹配**: 确保渲染目标尺寸与实际屏幕 DPR 匹配,Pass 会自动处理。
208
429
 
209
430
  ## Related
210
431
 
@@ -213,6 +434,5 @@ const pathLine = {
213
434
 
214
435
  ---
215
436
 
216
- ```yaml
217
- tags: [postprocessing, effect, flowing, light, pass, rendering, screenspace]
218
- ```
437
+
438
+ tags: [postprocessing, effect, flowing, light, pass, rendering, screenspace, instanced, performance]