three-base 2.0.75 → 2.0.77

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 (82) hide show
  1. package/ApngTexture.d.ts +13 -13
  2. package/ApngTexture.js +61 -61
  3. package/ApngTextureManage.d.ts +18 -18
  4. package/ApngTextureManage.js +87 -87
  5. package/EventMesh.d.ts +18 -18
  6. package/EventMesh.js +59 -59
  7. package/ExtrudeMesh.d.ts +111 -111
  8. package/ExtrudeMesh.js +363 -363
  9. package/FPScontrol.d.ts +45 -45
  10. package/FPScontrol.js +171 -171
  11. package/Fire/Fire.d.ts +28 -28
  12. package/Fire/Fire.js +79 -79
  13. package/Fire/FireGromtry.d.ts +9 -9
  14. package/Fire/FireGromtry.js +40 -40
  15. package/Fire/FireMaterial.d.ts +28 -28
  16. package/Fire/FireMaterial.js +32 -32
  17. package/InstancedMeshBase.d.ts +32 -32
  18. package/InstancedMeshBase.js +229 -236
  19. package/InstancedMeshBase.ts +12 -22
  20. package/KeyFrameAnimation.d.ts +25 -25
  21. package/KeyFrameAnimation.js +78 -78
  22. package/MeshBase.d.ts +34 -34
  23. package/MeshBase.js +187 -187
  24. package/Mirror.d.ts +25 -25
  25. package/Mirror.js +139 -139
  26. package/PlaneModal1/PlaneModal1.d.ts +31 -31
  27. package/PlaneModal1/PlaneModal1.js +105 -105
  28. package/PlaneModal1/index.d.ts +1 -1
  29. package/PlaneModal1/index.js +1 -1
  30. package/PlaneText.d.ts +44 -44
  31. package/PlaneText.js +30 -30
  32. package/PlaneWithHtml.d.ts +11 -11
  33. package/PlaneWithHtml.js +68 -68
  34. package/PointLight/PointLight.d.ts +13 -13
  35. package/PointLight/PointLight.js +64 -64
  36. package/PointLight/index.d.ts +1 -1
  37. package/PointLight/index.js +1 -1
  38. package/Sky/Sky.d.ts +13 -13
  39. package/Sky/Sky.js +28 -28
  40. package/Sky/index.d.ts +1 -1
  41. package/Sky/index.js +1 -1
  42. package/SpriteModal1/SpriteModal1.d.ts +30 -30
  43. package/SpriteModal1/SpriteModal1.js +110 -110
  44. package/SpriteModal1/index.d.ts +1 -1
  45. package/SpriteModal1/index.js +1 -1
  46. package/SpriteText.d.ts +45 -45
  47. package/SpriteText.js +163 -163
  48. package/SpriteText2.d.ts +47 -47
  49. package/SpriteText2.js +198 -198
  50. package/SpriteWithHtml.d.ts +7 -7
  51. package/SpriteWithHtml.js +49 -49
  52. package/Text3D/Text3D.d.ts +20 -20
  53. package/Text3D/Text3D.js +62 -62
  54. package/Text3D/fontController.d.ts +59 -59
  55. package/Text3D/fontController.js +200 -200
  56. package/Text3D/index.d.ts +1 -1
  57. package/Text3D/index.js +1 -1
  58. package/ThreeBase.d.ts +92 -92
  59. package/ThreeBase.js +619 -619
  60. package/ThreeMouse.d.ts +39 -39
  61. package/ThreeMouse.js +359 -359
  62. package/Transition.d.ts +40 -40
  63. package/Transition.js +207 -207
  64. package/index.d.ts +1 -1
  65. package/index.js +2 -2
  66. package/keyContorls.d.ts +25 -25
  67. package/keyContorls.js +98 -98
  68. package/package.json +1 -1
  69. package/pass/FullScreenQuad.d.ts +7 -7
  70. package/pass/FullScreenQuad.js +25 -25
  71. package/pass/glow_pass.d.ts +2 -2
  72. package/pass/glow_pass.js +236 -236
  73. package/pass/stroke_pass.d.ts +2 -2
  74. package/pass/stroke_pass.js +202 -202
  75. package/shaders/FXAAShaderMaterial.d.ts +3 -3
  76. package/shaders/FXAAShaderMaterial.js +21 -21
  77. package/shaders.d.ts +17 -17
  78. package/shaders.js +99 -99
  79. package/threeLoader.d.ts +26 -26
  80. package/threeLoader.js +189 -189
  81. package/threeUtils.d.ts +47 -47
  82. package/threeUtils.js +266 -266
package/ThreeBase.js CHANGED
@@ -1,619 +1,619 @@
1
- /*
2
- * @Author: xiaosihan
3
- * @Date: 2021-12-20 13:38:22
4
- * @Last Modified by: xiaosihan
5
- * @Last Modified time: 2024-08-10 04:23:41
6
- */
7
- import { round } from "lodash";
8
- import { AxesHelper, Box3, DepthFormat, DepthTexture, EventDispatcher, GridHelper, LinearFilter, MOUSE, Mesh, NoBlending, NormalBlending, OrthographicCamera, PerspectiveCamera, RGBAFormat, Scene, TOUCH, UnsignedShortType, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
9
- import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
10
- import { degToRad, generateUUID } from "three/src/math/MathUtils";
11
- import ThreeMouse from "./ThreeMouse";
12
- import Transition from "./Transition";
13
- import glow_pass from "./pass/glow_pass";
14
- import stroke_pass from "./pass/stroke_pass";
15
- import { clearShader, coptyShader, fullScreenMesh, gaussBlurShader, strokeShader } from "./shaders";
16
- import FXAAShaderMaterial from "./shaders/FXAAShaderMaterial";
17
- import threeLoader from "./threeLoader";
18
- import threeUtils from "./threeUtils";
19
- // three.js 的基础对象
20
- export default class ThreeBase extends EventDispatcher {
21
- constructor() {
22
- super();
23
- this.init();
24
- }
25
- // 基础深度纹理
26
- baseDepthTexture = (() => {
27
- const baseDepthTexture = new DepthTexture(4096, 4096);
28
- baseDepthTexture.format = DepthFormat;
29
- baseDepthTexture.type = UnsignedShortType;
30
- baseDepthTexture.anisotropy = 16;
31
- return baseDepthTexture;
32
- })();
33
- // 基础纹理渲染对象
34
- baseRenderTarget = (() => {
35
- const baseRenderTarget = new WebGLRenderTarget(128, 128, {
36
- minFilter: LinearFilter,
37
- magFilter: LinearFilter,
38
- format: RGBAFormat,
39
- stencilBuffer: false,
40
- depthTexture: this.baseDepthTexture,
41
- anisotropy: 16,
42
- });
43
- return baseRenderTarget;
44
- })();
45
- // 公用渲染目标1
46
- renderTarget = new WebGLRenderTarget(128, 128, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat });
47
- // 渲染目标2
48
- renderTarget2 = new WebGLRenderTarget(128, 128, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat });
49
- // 公用二维向量
50
- tempVector2 = new Vector2();
51
- // 公用的三维向量
52
- tempVector3 = new Vector3();
53
- //渲染器的尺寸
54
- size = new Vector2(0, 0);
55
- //上下文丢失
56
- rendererLost = false;
57
- // 渲染器对象
58
- renderer = (() => {
59
- const renderer = new WebGLRenderer({
60
- alpha: true, // 透明背景
61
- antialias: true, // 阴影抗锯齿
62
- precision: "highp", //precision:highp/mediump/lowp着色精度选择
63
- logarithmicDepthBuffer: true, // 对数深度缓冲区 避免当2个面挨的很近时闪烁
64
- preserveDrawingBuffer: true, // 是否保存绘图缓冲
65
- stencil: false, // 模版
66
- });
67
- renderer.domElement.id = generateUUID();
68
- //监听上下文的销毁事件
69
- renderer.domElement.addEventListener('webglcontextlost', (event) => {
70
- this.rendererLost = true;
71
- }, false);
72
- // 监听上下文的恢复事件
73
- renderer.domElement.addEventListener('webglcontextrestored', (event) => {
74
- this.rendererLost = false;
75
- }, false);
76
- renderer.localClippingEnabled = true;
77
- renderer.setPixelRatio(window.devicePixelRatio);
78
- renderer.autoClear = false;
79
- renderer.clear();
80
- threeLoader.setAnisotropy(renderer.capabilities.getMaxAnisotropy());
81
- return renderer;
82
- })();
83
- //透视相机
84
- perspectiveCamera = new PerspectiveCamera(45, 1, 0.01, 10000);
85
- //正交相机
86
- orthographicCamera = new OrthographicCamera(-1, 1, 1, -1, 0.01, 10000);
87
- //相机的类型 有透视相机和正交相机
88
- cameraType = "perspectiveCamera";
89
- setCameraType(cameraType) {
90
- if (this.cameraType !== cameraType) {
91
- this.cameraType = cameraType;
92
- if (this.cameraType === "perspectiveCamera") {
93
- this.threeMouse._camera = this.perspectiveCamera;
94
- this.controls.object = this.perspectiveCamera;
95
- }
96
- else if (this.cameraType === "orthographicCamera") {
97
- this.threeMouse._camera = this.orthographicCamera;
98
- this.controls.object = this.orthographicCamera;
99
- }
100
- }
101
- }
102
- //获取当前使用的的相机
103
- getCamera() {
104
- switch (this.cameraType) {
105
- case "orthographicCamera":
106
- return this.orthographicCamera;
107
- case "perspectiveCamera":
108
- default:
109
- return this.perspectiveCamera;
110
- }
111
- }
112
- // 场景对象
113
- scene = new Scene();
114
- // 这个方法等效 this.scene.add
115
- // 想少写2个字母就用这个方法
116
- add(...object) {
117
- this.scene.add(...object);
118
- return this;
119
- }
120
- ///鼠标事件对象
121
- threeMouse = new ThreeMouse(this.renderer, this.perspectiveCamera, this.scene);
122
- // 轨道控制器
123
- controls = (() => {
124
- const controls = new OrbitControls(this.perspectiveCamera, this.renderer.domElement);
125
- controls.addEventListener("change", () => {
126
- this.needRender = true;
127
- });
128
- controls.rotateSpeed = 0.2;
129
- controls.maxPolarAngle = Math.PI;
130
- controls.enableDamping = true;
131
- controls.dampingFactor = 0.1;
132
- controls.screenSpacePanning = true;
133
- return controls;
134
- })();
135
- // 设置 dom 容器
136
- async setContainer(div) {
137
- if (div) {
138
- div.style.overflow = "hidden";
139
- div.innerHTML = "";
140
- div.append(this.renderer.domElement);
141
- if (this.rendererLost) {
142
- this.renderer.forceContextRestore();
143
- this.needRender = true;
144
- setTimeout(() => {
145
- this.needRender = true;
146
- }, 100);
147
- setTimeout(() => {
148
- this.renderer.domElement.style.opacity = "1";
149
- }, 200);
150
- }
151
- this.setSize(div.clientWidth, div.clientHeight);
152
- // 开启时时渲染
153
- threeUtils.removeEventListener("requestAnimationFrame", this.renderBindThis);
154
- threeUtils.addEventListener("requestAnimationFrame", this.renderBindThis);
155
- }
156
- }
157
- // 修改尺寸
158
- setSize(width, height) {
159
- //修改透视相机的比例
160
- this.perspectiveCamera.aspect = width / height;
161
- this.perspectiveCamera.updateProjectionMatrix();
162
- //修改正交相机的比例
163
- // this.orthographicCamera.top = height / width;
164
- // this.orthographicCamera.bottom = -height / width;
165
- this.orthographicCamera.left = -width / height;
166
- this.orthographicCamera.right = width / height;
167
- this.renderer.setPixelRatio(window.devicePixelRatio);
168
- this.renderer.setSize(width, height);
169
- // 设置帧缓存区的尺寸
170
- this.baseRenderTarget.setSize(width * window.devicePixelRatio, height * window.devicePixelRatio);
171
- // 设置深度纹理的尺寸
172
- this.baseDepthTexture.image.width = width * window.devicePixelRatio;
173
- this.baseDepthTexture.image.height = height * window.devicePixelRatio;
174
- this.size.set(width, height);
175
- // 渲染场景
176
- // this.renderer.clear();
177
- // this.renderer.render(this.scene, this.camera);
178
- }
179
- // 视角距离过度
180
- controlsDistance = (() => {
181
- const controlsDistance = new Transition({ distance: 0 });
182
- controlsDistance.setDuration(1000);
183
- return controlsDistance;
184
- })();
185
- // 控制器的 朝向带缓动的计算的顶点对象
186
- controlsCenter = (() => {
187
- const controlsCenter = new Transition({ x: 0, y: 0, z: 0 });
188
- controlsCenter.setDuration(1000);
189
- controlsCenter.onChange(({ x, y, z }) => {
190
- this.controls.target.set(x, y, z);
191
- this.perspectiveCamera.lookAt(this.controls.target);
192
- this.orthographicCamera.lookAt(this.controls.target);
193
- this.needRender = true;
194
- });
195
- return controlsCenter;
196
- })();
197
- // 控制器的位置缓动的计算的顶点对象
198
- controlsPosition = (() => {
199
- const controlsPosition = new Transition({ x: 100, y: 100, z: 100 });
200
- controlsPosition.setDuration(1000);
201
- controlsPosition.onChange(({ x, y, z }) => {
202
- // const distance = this.controlsDistance.get("distance");
203
- this.perspectiveCamera.position.set(x + 0.01, y, z + 0.01);
204
- // .sub(this.controls.target)
205
- // .setLength(distance)
206
- // .add(this.controls.target);
207
- // // 创建初始方向向量
208
- // const directionVecA = new THREE.Vector3(1, 0, 0); // 需要旋转的初始方向向量
209
- // const referenceVecB = new THREE.Vector3(0, 1, 0); // 参考方向向量,用于确定旋转轴
210
- // // 归一化参考方向向量
211
- // referenceVecB.normalize();
212
- // // 计算旋转轴
213
- // const axis = new THREE.Vector3().crossVectors(directionVecA, referenceVecB).normalize();
214
- // // 计算旋转角度(弧度)
215
- // const angle = Math.PI / 36; // 旋转角度为5度,Math.PI / 36为弧度
216
- // // 使用applyAxisAngle方法进行旋转
217
- // directionVecA.applyAxisAngle(axis, angle);
218
- // // 输出旋转后的方向向量
219
- // console.log(directionVecA);
220
- this.perspectiveCamera.lookAt(this.controls.target);
221
- //计算正交相机的缓动
222
- this.orthographicCamera.position
223
- .set(x + 0.01, y, z + 0.01)
224
- .sub(this.controls.target)
225
- .setLength(5000)
226
- .add(this.controls.target);
227
- this.orthographicCamera.lookAt(this.controls.target);
228
- this.needRender = true;
229
- });
230
- return controlsPosition;
231
- })();
232
- // 网格辅助线
233
- gridHelper = (() => {
234
- const gridHelper = new GridHelper(1000, 100, 0x303030, 0x303030);
235
- if (threeUtils.isDev) {
236
- this.scene.add(gridHelper);
237
- }
238
- return gridHelper;
239
- })();
240
- //坐标轴线
241
- axesHelper = (() => {
242
- const axesHelper = new AxesHelper(10000);
243
- if (threeUtils.isDev) {
244
- this.scene.add(axesHelper);
245
- }
246
- return axesHelper;
247
- })();
248
- //TODO 设置帧频率 默认50帧
249
- frame = 50;
250
- init() {
251
- // 资源加载进度有更新时, 需要渲染
252
- threeLoader.addEventListener("loadding", () => {
253
- this.dispatchEvent({ type: "loadding" });
254
- this.needRender = true;
255
- });
256
- threeLoader.addEventListener("onLoad", () => {
257
- this.dispatchEvent({ type: "onLoad" });
258
- this.needRender = true;
259
- });
260
- }
261
- // 重置控制器的位置
262
- resetLookAt({ position = this.perspectiveCamera.position, center = this.controls.target, }) {
263
- // 停止控制器的惯性
264
- const originEnableDamping = this.controls.enableDamping;
265
- this.controls.saveState();
266
- this.controls.update();
267
- this.controls.reset();
268
- this.controls.enableDamping = originEnableDamping;
269
- this.controlsDistance.reset({ distance: new Vector3(position.x, position.y, position.z).distanceTo(new Vector3(center.x, center.y, center.z)) });
270
- position && this.controlsPosition.reset({ x: position.x, y: position.y, z: position.z });
271
- center && this.controlsCenter.reset({ x: center.x, y: center.y, z: center.z });
272
- this.needRender = true;
273
- }
274
- // 设置控制器的位置 以动画的方式移动控制器
275
- async setLookAt({ position = this.perspectiveCamera.position, center = this.controls.target, duration = 1000 }) {
276
- // 停止控制器的惯性
277
- const originEnableDamping = this.controls.enableDamping;
278
- this.controls.saveState();
279
- this.controls.update();
280
- this.controls.reset();
281
- this.controls.enableDamping = originEnableDamping;
282
- let currentPosition = this.perspectiveCamera.position.clone();
283
- let currentCenter = this.controls.target.clone();
284
- // 等待动画执行完成
285
- await Promise.all([
286
- new Promise(async (resolve, reject) => {
287
- this.controlsDistance.setDuration(duration).reset({ distance: this.perspectiveCamera.position.distanceTo(this.controls.target) });
288
- await this.controlsDistance.set({ distance: new Vector3(position.x, position.y, position.z).distanceTo(new Vector3(center.x, center.y, center.z)) });
289
- resolve();
290
- }),
291
- new Promise(async (resolve, reject) => {
292
- this.controlsPosition.setDuration(duration).reset({ x: currentPosition.x, y: currentPosition.y, z: currentPosition.z });
293
- await this.controlsPosition.set({ x: position.x, y: position.y, z: position.z });
294
- resolve();
295
- }),
296
- new Promise(async (resolve, reject) => {
297
- this.controlsCenter.setDuration(duration).reset({ x: currentCenter.x, y: currentCenter.y, z: currentCenter.z });
298
- await this.controlsCenter.set({ x: center.x, y: center.y, z: center.z });
299
- resolve();
300
- })
301
- ]);
302
- }
303
- // 获取控制的位置和朝向 保留3位小数
304
- getLookAt() {
305
- //判断需要使用那个相机
306
- const position = this.perspectiveCamera.position.toArray().map(v => round(v, 3));
307
- const center = this.controls.target.toArray().map(v => round(v, 3));
308
- return {
309
- center: new Vector3(...center),
310
- position: new Vector3(...position),
311
- centerPosition: `center: new Vector3(${center.join(",")}),position: new Vector3(${position.join(",")})`
312
- };
313
- }
314
- // 查看对象
315
- lookatObjects({ objects, duration = 1000, direction = new Vector3(0.3, 0.3, 1), offset = new Vector3(0, 0, 0), distance = 1 }) {
316
- const box3 = new Box3();
317
- objects.map(o => {
318
- o.traverse(oo => {
319
- box3.expandByObject(oo);
320
- });
321
- });
322
- const size = new Vector3();
323
- box3.getSize(size);
324
- const center = new Vector3();
325
- box3.getCenter(center);
326
- center.add(offset);
327
- const modelSize = size.length() * distance;
328
- if (this.cameraType === "perspectiveCamera") {
329
- const { fov } = this.getCamera();
330
- const cameraDistance = modelSize / (2 * Math.tan(fov * Math.PI / 360));
331
- const position = direction.setLength(cameraDistance).add(center);
332
- this.setLookAt({ center, position, duration });
333
- }
334
- }
335
- // 三维向量转化成屏幕上的2维向量
336
- v3tov2(v3) {
337
- //判断需要使用那个相机
338
- const camera = this.getCamera();
339
- v3.project(camera);
340
- const v2 = new Vector2((v3.x + 1) * this.size.x * 0.5, (-v3.y + 1) * this.size.y * 0.5);
341
- return v2;
342
- }
343
- // 控制器类型
344
- controlType = "OrbitControls";
345
- // 记录一下空间平移方式 在切换控制器的时候保留一下平移方式
346
- originScreenSpacePanning = false;
347
- //改变控制器的类型
348
- setControlType(controlType) {
349
- if (controlType === "OrbitControls") {
350
- this.controls.screenSpacePanning = this.originScreenSpacePanning;
351
- this.controls.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
352
- this.controls.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
353
- }
354
- else if (controlType === "MapControls") {
355
- this.originScreenSpacePanning = this.controls.screenSpacePanning;
356
- // pan orthogonal to world-space direction camera.up
357
- this.controls.screenSpacePanning = false;
358
- this.controls.mouseButtons.LEFT = MOUSE.PAN;
359
- this.controls.mouseButtons.RIGHT = MOUSE.ROTATE;
360
- this.controls.touches.ONE = TOUCH.PAN;
361
- this.controls.touches.TWO = TOUCH.DOLLY_ROTATE;
362
- }
363
- }
364
- // 是否自动渲染
365
- autoRender = true;
366
- //是否需要渲染, 当autoRender=false的时候就需要 手动触发渲染
367
- needRender = false;
368
- // 停止渲染
369
- stopRender() {
370
- threeUtils.removeEventListener("requestAnimationFrame", this.renderBindThis);
371
- if (!this.rendererLost) {
372
- this.renderer.forceContextLoss();
373
- this.setSize(100, 100);
374
- this.renderer.domElement.style.opacity = "0";
375
- }
376
- }
377
- // 判断是否有 发光和描边等对象 以便判断是否需要 渲染深度纹理
378
- hasPass() {
379
- let has_stroke_or_pass = false;
380
- this.scene.traverse(obj => {
381
- const { stroke_enable = false, stroke_width = 1, glow_enable = false, glow_width = 1 } = obj.userData;
382
- if ((stroke_enable && stroke_width > 0) || (glow_enable && glow_width > 0)) {
383
- has_stroke_or_pass = true;
384
- }
385
- });
386
- return has_stroke_or_pass;
387
- }
388
- // 渲染场景
389
- render() {
390
- // canvas 没有在页面上面的话 就停止渲染
391
- if (!threeUtils.inDocument(this.renderer.domElement)) {
392
- this.stopRender();
393
- return;
394
- }
395
- //透视相机和正交相机参数的时时转换
396
- if (this.cameraType === "perspectiveCamera") {
397
- const { center } = this.getLookAt();
398
- const { fov } = this.perspectiveCamera;
399
- const distaned = this.perspectiveCamera.position.clone().sub(center).length();
400
- const zoom = 1 / (Math.tan(degToRad(fov) / 2) * distaned);
401
- this.orthographicCamera.zoom = zoom;
402
- this.orthographicCamera.position.copy(this.perspectiveCamera.position.clone().sub(center)).setLength(2500).add(center);
403
- }
404
- else if (this.cameraType === "orthographicCamera") {
405
- const { center } = this.getLookAt();
406
- const { fov } = this.perspectiveCamera;
407
- const { zoom } = this.orthographicCamera;
408
- const distaned = (1 / zoom) / Math.tan(degToRad(fov) / 2);
409
- this.perspectiveCamera.position.copy(this.orthographicCamera.position.clone().sub(center)).setLength(distaned).add(center);
410
- }
411
- //判断需要使用那个相机
412
- const camera = this.getCamera();
413
- //判断尺寸发生改变时重置尺寸
414
- const parentNode = this.renderer.domElement.parentNode;
415
- if (parentNode.clientWidth !== this.size.x || parentNode.clientHeight !== this.size.y) {
416
- this.setSize(parentNode.clientWidth, parentNode.clientHeight);
417
- }
418
- // 分发鼠标事件
419
- this.threeMouse.dispatchEvent();
420
- // 在不自动渲染时, needRender=false 就跳过渲染
421
- if (!this.autoRender && !this.needRender) {
422
- return; // 标识未渲染
423
- }
424
- this.needRender = false;
425
- if (this.controls.enabled) {
426
- this.controls.update();
427
- }
428
- // 渲染场景
429
- this.dispatchEvent({ type: "beforeRender" });
430
- const hasPass = this.hasPass();
431
- if (hasPass) {
432
- // 把基础场景渲染到 帧缓冲区 在后期通道中会用到
433
- this.renderer.setRenderTarget(this.baseRenderTarget);
434
- this.renderer.clear();
435
- this.renderer.render(this.scene, camera);
436
- this.renderer.setRenderTarget(null);
437
- }
438
- // 清空主画布
439
- this.renderer.clear();
440
- this.renderer.render(this.scene, camera);
441
- if (hasPass) {
442
- // 查找需要绘制描边的对象,并且绘制描边
443
- this.scene.traverseVisible(obj => {
444
- stroke_pass(this.renderer, camera, obj, this.baseDepthTexture);
445
- glow_pass(this.renderer, camera, obj, this.baseDepthTexture);
446
- });
447
- }
448
- this.dispatchEvent({ type: "afterRender" });
449
- }
450
- renderBindThis = this.render.bind(this);
451
- //获取渲染图blob对象
452
- async getThumbBlob() {
453
- this.render();
454
- const blob = await new Promise((resolve, reject) => {
455
- this.renderer.domElement.toBlob(blob => {
456
- resolve(blob);
457
- }, "image/png", 1);
458
- });
459
- return blob;
460
- }
461
- //获取渲染图base64
462
- getThumbBase64() {
463
- this.render();
464
- const base64 = this.renderer.domElement.toDataURL('image/png');
465
- return base64;
466
- }
467
- //获取渲染图文件对象
468
- async getThumbFile() {
469
- const blob = await this.getThumbBlob();
470
- const file = new File([blob], 'thumb.png', { type: blob.type });
471
- return file;
472
- }
473
- // 下载渲染图
474
- async download(fileName = "download") {
475
- this.render();
476
- const blob = await this.getThumbBlob();
477
- const base64 = this.renderer.domElement.toDataURL('image/png');
478
- let saveLink = document.createElement("a");
479
- saveLink.download = `${fileName}.png`;
480
- saveLink.href = URL.createObjectURL(blob);
481
- document.body.append(saveLink);
482
- saveLink.click();
483
- }
484
- _preMesh = new Mesh();
485
- _preRT = new WebGLRenderTarget(128, 128, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat });
486
- //预加载纹理图片
487
- async preloadTexture(src) {
488
- const texture = threeLoader.getTexture(src);
489
- while (texture && !texture.image || !texture.image.complete) {
490
- await new Promise(resolve => setTimeout(resolve, 50));
491
- }
492
- // 添加到计划任务中去
493
- threeUtils.addWork(() => {
494
- const originRenderTarget = this.renderer.getRenderTarget();
495
- this.renderer.setRenderTarget(this._preRT);
496
- this._preMesh.material.map = texture;
497
- this.renderer.render(this._preMesh, this.perspectiveCamera);
498
- this.renderer.setRenderTarget(originRenderTarget);
499
- });
500
- }
501
- //预加载网格
502
- async preloadGeometry(geometry) {
503
- // 添加到计划任务中去
504
- threeUtils.addWork(() => {
505
- const originRenderTarget = this.renderer.getRenderTarget();
506
- this.renderer.setRenderTarget(this._preRT);
507
- this._preMesh.geometry = geometry;
508
- this.renderer.render(this._preMesh, this.perspectiveCamera);
509
- this.renderer.setRenderTarget(originRenderTarget);
510
- });
511
- }
512
- // 清理渲染目标
513
- clearRendererTargte(rendererTarget) {
514
- const originTarget = this.renderer.getRenderTarget();
515
- this.renderer.setRenderTarget(rendererTarget);
516
- fullScreenMesh.material = clearShader;
517
- fullScreenMesh.render(this.renderer);
518
- this.renderer.clearDepth();
519
- this.renderer.setRenderTarget(originTarget);
520
- }
521
- // 拷贝纹理到渲染目标
522
- copyTextureToRenderTarget(texture, rendererTarget, alpha = false, opactity = 1) {
523
- const originTarget = this.renderer.getRenderTarget();
524
- if (rendererTarget) {
525
- this.renderer.getSize(this.tempVector2);
526
- rendererTarget.setSize(this.tempVector2.x, this.tempVector2.y);
527
- }
528
- this.renderer.setRenderTarget(rendererTarget);
529
- fullScreenMesh.material = coptyShader;
530
- coptyShader.uniforms.map.value = texture;
531
- coptyShader.uniforms.opacity.value = opactity;
532
- coptyShader.blending = alpha ? NoBlending : NormalBlending;
533
- fullScreenMesh.render(this.renderer);
534
- this.renderer.setRenderTarget(originTarget);
535
- }
536
- // 把对象绘制到渲染目标中
537
- object3dToRendererTarget(obj, rendererTarget) {
538
- this.clearRendererTargte(rendererTarget);
539
- const originTarget = this.renderer.getRenderTarget();
540
- this.renderer.setRenderTarget(rendererTarget);
541
- this.renderer.getSize(this.tempVector2);
542
- rendererTarget.setSize(this.tempVector2.x, this.tempVector2.y);
543
- this.renderer.render(obj, this.getCamera());
544
- this.renderer.setRenderTarget(originTarget);
545
- }
546
- // 给渲染目标的内容加描边
547
- rendererTagetAddStroke(rendererTarget, strockWidth, color, opacity) {
548
- const originTarget = this.renderer.getRenderTarget();
549
- this.clearRendererTargte(this.renderTarget2);
550
- this.renderer.setRenderTarget(this.renderTarget2);
551
- this.renderer.getSize(this.tempVector2);
552
- this.renderTarget2.setSize(this.tempVector2.x, this.tempVector2.y);
553
- fullScreenMesh.material = strokeShader;
554
- strokeShader.uniforms.map.value = rendererTarget.texture;
555
- strokeShader.uniforms.resolution.value.set(this.tempVector2.x, this.tempVector2.y);
556
- strokeShader.uniforms.strokeWidth.value = strockWidth;
557
- strokeShader.uniforms.strokeColor.value.set(color);
558
- strokeShader.uniforms.opacity.value = opacity;
559
- fullScreenMesh.render(this.renderer);
560
- this.copyTextureToRenderTarget(this.renderTarget2.texture, rendererTarget, true);
561
- this.renderer.setRenderTarget(originTarget);
562
- }
563
- // 渲染对象的描边
564
- renderObject3dStroke(obj, strockWidth, color, opacity) {
565
- this.object3dToRendererTarget(obj, this.renderTarget);
566
- this.rendererTagetAddStroke(this.renderTarget, strockWidth, color, opacity);
567
- this.copyTextureToRenderTarget(this.renderTarget.texture, null);
568
- }
569
- //给渲染目标加上高斯模糊计算
570
- renderTargetgaussBlur(rendererTarget, radius, opacity) {
571
- const originTarget = this.renderer.getRenderTarget();
572
- this.clearRendererTargte(this.renderTarget2);
573
- this.renderer.setRenderTarget(this.renderTarget2);
574
- this.renderer.getSize(this.tempVector2);
575
- this.renderTarget2.setSize(this.tempVector2.x, this.tempVector2.y);
576
- fullScreenMesh.material = gaussBlurShader;
577
- gaussBlurShader.uniforms.map.value = rendererTarget.texture;
578
- gaussBlurShader.uniforms.radius.value = radius;
579
- gaussBlurShader.uniforms.opacity.value = opacity;
580
- gaussBlurShader.uniforms.resolution.value.set(this.tempVector2.x, this.tempVector2.y);
581
- fullScreenMesh.render(this.renderer);
582
- this.copyTextureToRenderTarget(this.renderTarget2.texture, rendererTarget, true);
583
- this.renderer.setRenderTarget(originTarget);
584
- }
585
- // 渲染目标上的纹理添加辉光效果
586
- renderTargetAddGlow(renderTarget, width, color) {
587
- this.rendererTagetAddStroke(renderTarget, 1 * width, color, 0.3);
588
- this.rendererTagetAddStroke(renderTarget, 2 * width, color, 0.2);
589
- this.rendererTagetAddStroke(renderTarget, 3 * width, color, 0.05);
590
- this.renderTargetgaussBlur(renderTarget, 1, 1);
591
- this.renderTargetgaussBlur(renderTarget, 1, 1);
592
- this.renderTargetgaussBlur(renderTarget, 1, 1);
593
- }
594
- // 需要做抗锯齿处理 渲染 渲染目标
595
- renderRendererTarget(renderTarget, alpha = false) {
596
- FXAAShaderMaterial.uniforms.tDiffuse.value = renderTarget.texture;
597
- FXAAShaderMaterial.uniforms.resolution.value.set(1 / this.size.x, 1 / this.size.y);
598
- fullScreenMesh.material = FXAAShaderMaterial;
599
- fullScreenMesh.render(this.renderer);
600
- // const FXAAShaderMaterial = new ShaderMaterial({
601
- // uniforms: {
602
- // tDiffuse: { value: null }, // 贴图纹理
603
- // resolution: { value: new Vector2(1 / 1024, 1 / 1024) }
604
- // },
605
- // coptyShader.uniforms.map.value = renderTarget.texture;
606
- // coptyShader.uniforms.opacity.value = 1.0;
607
- // fullScreenMesh.material = coptyShader;
608
- // coptyShader.blending = alpha ? NoBlending : NormalBlending;
609
- // fullScreenMesh.render(this.renderer);
610
- }
611
- // 渲染纹理
612
- renderTexture(texture, alpha = false) {
613
- coptyShader.uniforms.map.value = texture;
614
- coptyShader.uniforms.opacity.value = 1.0;
615
- fullScreenMesh.material = coptyShader;
616
- coptyShader.blending = alpha ? NoBlending : NormalBlending;
617
- fullScreenMesh.render(this.renderer);
618
- }
619
- }
1
+ /*
2
+ * @Author: xiaosihan
3
+ * @Date: 2021-12-20 13:38:22
4
+ * @Last Modified by: xiaosihan
5
+ * @Last Modified time: 2024-08-10 04:23:41
6
+ */
7
+ import { round } from "lodash";
8
+ import { AxesHelper, Box3, DepthFormat, DepthTexture, EventDispatcher, GridHelper, LinearFilter, MOUSE, Mesh, NoBlending, NormalBlending, OrthographicCamera, PerspectiveCamera, RGBAFormat, Scene, TOUCH, UnsignedShortType, Vector2, Vector3, WebGLRenderTarget, WebGLRenderer } from "three";
9
+ import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
10
+ import { degToRad, generateUUID } from "three/src/math/MathUtils";
11
+ import ThreeMouse from "./ThreeMouse";
12
+ import Transition from "./Transition";
13
+ import glow_pass from "./pass/glow_pass";
14
+ import stroke_pass from "./pass/stroke_pass";
15
+ import { clearShader, coptyShader, fullScreenMesh, gaussBlurShader, strokeShader } from "./shaders";
16
+ import FXAAShaderMaterial from "./shaders/FXAAShaderMaterial";
17
+ import threeLoader from "./threeLoader";
18
+ import threeUtils from "./threeUtils";
19
+ // three.js 的基础对象
20
+ export default class ThreeBase extends EventDispatcher {
21
+ constructor() {
22
+ super();
23
+ this.init();
24
+ }
25
+ // 基础深度纹理
26
+ baseDepthTexture = (() => {
27
+ const baseDepthTexture = new DepthTexture(4096, 4096);
28
+ baseDepthTexture.format = DepthFormat;
29
+ baseDepthTexture.type = UnsignedShortType;
30
+ baseDepthTexture.anisotropy = 16;
31
+ return baseDepthTexture;
32
+ })();
33
+ // 基础纹理渲染对象
34
+ baseRenderTarget = (() => {
35
+ const baseRenderTarget = new WebGLRenderTarget(128, 128, {
36
+ minFilter: LinearFilter,
37
+ magFilter: LinearFilter,
38
+ format: RGBAFormat,
39
+ stencilBuffer: false,
40
+ depthTexture: this.baseDepthTexture,
41
+ anisotropy: 16,
42
+ });
43
+ return baseRenderTarget;
44
+ })();
45
+ // 公用渲染目标1
46
+ renderTarget = new WebGLRenderTarget(128, 128, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat });
47
+ // 渲染目标2
48
+ renderTarget2 = new WebGLRenderTarget(128, 128, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat });
49
+ // 公用二维向量
50
+ tempVector2 = new Vector2();
51
+ // 公用的三维向量
52
+ tempVector3 = new Vector3();
53
+ //渲染器的尺寸
54
+ size = new Vector2(0, 0);
55
+ //上下文丢失
56
+ rendererLost = false;
57
+ // 渲染器对象
58
+ renderer = (() => {
59
+ const renderer = new WebGLRenderer({
60
+ alpha: true,
61
+ antialias: true,
62
+ precision: "highp",
63
+ logarithmicDepthBuffer: true,
64
+ preserveDrawingBuffer: true,
65
+ stencil: false, // 模版
66
+ });
67
+ renderer.domElement.id = generateUUID();
68
+ //监听上下文的销毁事件
69
+ renderer.domElement.addEventListener('webglcontextlost', (event) => {
70
+ this.rendererLost = true;
71
+ }, false);
72
+ // 监听上下文的恢复事件
73
+ renderer.domElement.addEventListener('webglcontextrestored', (event) => {
74
+ this.rendererLost = false;
75
+ }, false);
76
+ renderer.localClippingEnabled = true;
77
+ renderer.setPixelRatio(window.devicePixelRatio);
78
+ renderer.autoClear = false;
79
+ renderer.clear();
80
+ threeLoader.setAnisotropy(renderer.capabilities.getMaxAnisotropy());
81
+ return renderer;
82
+ })();
83
+ //透视相机
84
+ perspectiveCamera = new PerspectiveCamera(45, 1, 0.01, 10000);
85
+ //正交相机
86
+ orthographicCamera = new OrthographicCamera(-1, 1, 1, -1, 0.01, 10000);
87
+ //相机的类型 有透视相机和正交相机
88
+ cameraType = "perspectiveCamera";
89
+ setCameraType(cameraType) {
90
+ if (this.cameraType !== cameraType) {
91
+ this.cameraType = cameraType;
92
+ if (this.cameraType === "perspectiveCamera") {
93
+ this.threeMouse._camera = this.perspectiveCamera;
94
+ this.controls.object = this.perspectiveCamera;
95
+ }
96
+ else if (this.cameraType === "orthographicCamera") {
97
+ this.threeMouse._camera = this.orthographicCamera;
98
+ this.controls.object = this.orthographicCamera;
99
+ }
100
+ }
101
+ }
102
+ //获取当前使用的的相机
103
+ getCamera() {
104
+ switch (this.cameraType) {
105
+ case "orthographicCamera":
106
+ return this.orthographicCamera;
107
+ case "perspectiveCamera":
108
+ default:
109
+ return this.perspectiveCamera;
110
+ }
111
+ }
112
+ // 场景对象
113
+ scene = new Scene();
114
+ // 这个方法等效 this.scene.add
115
+ // 想少写2个字母就用这个方法
116
+ add(...object) {
117
+ this.scene.add(...object);
118
+ return this;
119
+ }
120
+ ///鼠标事件对象
121
+ threeMouse = new ThreeMouse(this.renderer, this.perspectiveCamera, this.scene);
122
+ // 轨道控制器
123
+ controls = (() => {
124
+ const controls = new OrbitControls(this.perspectiveCamera, this.renderer.domElement);
125
+ controls.addEventListener("change", () => {
126
+ this.needRender = true;
127
+ });
128
+ controls.rotateSpeed = 0.2;
129
+ controls.maxPolarAngle = Math.PI;
130
+ controls.enableDamping = true;
131
+ controls.dampingFactor = 0.1;
132
+ controls.screenSpacePanning = true;
133
+ return controls;
134
+ })();
135
+ // 设置 dom 容器
136
+ async setContainer(div) {
137
+ if (div) {
138
+ div.style.overflow = "hidden";
139
+ div.innerHTML = "";
140
+ div.append(this.renderer.domElement);
141
+ if (this.rendererLost) {
142
+ this.renderer.forceContextRestore();
143
+ this.needRender = true;
144
+ setTimeout(() => {
145
+ this.needRender = true;
146
+ }, 100);
147
+ setTimeout(() => {
148
+ this.renderer.domElement.style.opacity = "1";
149
+ }, 200);
150
+ }
151
+ this.setSize(div.clientWidth, div.clientHeight);
152
+ // 开启时时渲染
153
+ threeUtils.removeEventListener("requestAnimationFrame", this.renderBindThis);
154
+ threeUtils.addEventListener("requestAnimationFrame", this.renderBindThis);
155
+ }
156
+ }
157
+ // 修改尺寸
158
+ setSize(width, height) {
159
+ //修改透视相机的比例
160
+ this.perspectiveCamera.aspect = width / height;
161
+ this.perspectiveCamera.updateProjectionMatrix();
162
+ //修改正交相机的比例
163
+ // this.orthographicCamera.top = height / width;
164
+ // this.orthographicCamera.bottom = -height / width;
165
+ this.orthographicCamera.left = -width / height;
166
+ this.orthographicCamera.right = width / height;
167
+ this.renderer.setPixelRatio(window.devicePixelRatio);
168
+ this.renderer.setSize(width, height);
169
+ // 设置帧缓存区的尺寸
170
+ this.baseRenderTarget.setSize(width * window.devicePixelRatio, height * window.devicePixelRatio);
171
+ // 设置深度纹理的尺寸
172
+ this.baseDepthTexture.image.width = width * window.devicePixelRatio;
173
+ this.baseDepthTexture.image.height = height * window.devicePixelRatio;
174
+ this.size.set(width, height);
175
+ // 渲染场景
176
+ // this.renderer.clear();
177
+ // this.renderer.render(this.scene, this.camera);
178
+ }
179
+ // 视角距离过度
180
+ controlsDistance = (() => {
181
+ const controlsDistance = new Transition({ distance: 0 });
182
+ controlsDistance.setDuration(1000);
183
+ return controlsDistance;
184
+ })();
185
+ // 控制器的 朝向带缓动的计算的顶点对象
186
+ controlsCenter = (() => {
187
+ const controlsCenter = new Transition({ x: 0, y: 0, z: 0 });
188
+ controlsCenter.setDuration(1000);
189
+ controlsCenter.onChange(({ x, y, z }) => {
190
+ this.controls.target.set(x, y, z);
191
+ this.perspectiveCamera.lookAt(this.controls.target);
192
+ this.orthographicCamera.lookAt(this.controls.target);
193
+ this.needRender = true;
194
+ });
195
+ return controlsCenter;
196
+ })();
197
+ // 控制器的位置缓动的计算的顶点对象
198
+ controlsPosition = (() => {
199
+ const controlsPosition = new Transition({ x: 100, y: 100, z: 100 });
200
+ controlsPosition.setDuration(1000);
201
+ controlsPosition.onChange(({ x, y, z }) => {
202
+ // const distance = this.controlsDistance.get("distance");
203
+ this.perspectiveCamera.position.set(x + 0.01, y, z + 0.01);
204
+ // .sub(this.controls.target)
205
+ // .setLength(distance)
206
+ // .add(this.controls.target);
207
+ // // 创建初始方向向量
208
+ // const directionVecA = new THREE.Vector3(1, 0, 0); // 需要旋转的初始方向向量
209
+ // const referenceVecB = new THREE.Vector3(0, 1, 0); // 参考方向向量,用于确定旋转轴
210
+ // // 归一化参考方向向量
211
+ // referenceVecB.normalize();
212
+ // // 计算旋转轴
213
+ // const axis = new THREE.Vector3().crossVectors(directionVecA, referenceVecB).normalize();
214
+ // // 计算旋转角度(弧度)
215
+ // const angle = Math.PI / 36; // 旋转角度为5度,Math.PI / 36为弧度
216
+ // // 使用applyAxisAngle方法进行旋转
217
+ // directionVecA.applyAxisAngle(axis, angle);
218
+ // // 输出旋转后的方向向量
219
+ // console.log(directionVecA);
220
+ this.perspectiveCamera.lookAt(this.controls.target);
221
+ //计算正交相机的缓动
222
+ this.orthographicCamera.position
223
+ .set(x + 0.01, y, z + 0.01)
224
+ .sub(this.controls.target)
225
+ .setLength(5000)
226
+ .add(this.controls.target);
227
+ this.orthographicCamera.lookAt(this.controls.target);
228
+ this.needRender = true;
229
+ });
230
+ return controlsPosition;
231
+ })();
232
+ // 网格辅助线
233
+ gridHelper = (() => {
234
+ const gridHelper = new GridHelper(1000, 100, 0x303030, 0x303030);
235
+ if (threeUtils.isDev) {
236
+ this.scene.add(gridHelper);
237
+ }
238
+ return gridHelper;
239
+ })();
240
+ //坐标轴线
241
+ axesHelper = (() => {
242
+ const axesHelper = new AxesHelper(10000);
243
+ if (threeUtils.isDev) {
244
+ this.scene.add(axesHelper);
245
+ }
246
+ return axesHelper;
247
+ })();
248
+ //TODO 设置帧频率 默认50帧
249
+ frame = 50;
250
+ init() {
251
+ // 资源加载进度有更新时, 需要渲染
252
+ threeLoader.addEventListener("loadding", () => {
253
+ this.dispatchEvent({ type: "loadding" });
254
+ this.needRender = true;
255
+ });
256
+ threeLoader.addEventListener("onLoad", () => {
257
+ this.dispatchEvent({ type: "onLoad" });
258
+ this.needRender = true;
259
+ });
260
+ }
261
+ // 重置控制器的位置
262
+ resetLookAt({ position = this.perspectiveCamera.position, center = this.controls.target, }) {
263
+ // 停止控制器的惯性
264
+ const originEnableDamping = this.controls.enableDamping;
265
+ this.controls.saveState();
266
+ this.controls.update();
267
+ this.controls.reset();
268
+ this.controls.enableDamping = originEnableDamping;
269
+ this.controlsDistance.reset({ distance: new Vector3(position.x, position.y, position.z).distanceTo(new Vector3(center.x, center.y, center.z)) });
270
+ position && this.controlsPosition.reset({ x: position.x, y: position.y, z: position.z });
271
+ center && this.controlsCenter.reset({ x: center.x, y: center.y, z: center.z });
272
+ this.needRender = true;
273
+ }
274
+ // 设置控制器的位置 以动画的方式移动控制器
275
+ async setLookAt({ position = this.perspectiveCamera.position, center = this.controls.target, duration = 1000 }) {
276
+ // 停止控制器的惯性
277
+ const originEnableDamping = this.controls.enableDamping;
278
+ this.controls.saveState();
279
+ this.controls.update();
280
+ this.controls.reset();
281
+ this.controls.enableDamping = originEnableDamping;
282
+ let currentPosition = this.perspectiveCamera.position.clone();
283
+ let currentCenter = this.controls.target.clone();
284
+ // 等待动画执行完成
285
+ await Promise.all([
286
+ new Promise(async (resolve, reject) => {
287
+ this.controlsDistance.setDuration(duration).reset({ distance: this.perspectiveCamera.position.distanceTo(this.controls.target) });
288
+ await this.controlsDistance.set({ distance: new Vector3(position.x, position.y, position.z).distanceTo(new Vector3(center.x, center.y, center.z)) });
289
+ resolve();
290
+ }),
291
+ new Promise(async (resolve, reject) => {
292
+ this.controlsPosition.setDuration(duration).reset({ x: currentPosition.x, y: currentPosition.y, z: currentPosition.z });
293
+ await this.controlsPosition.set({ x: position.x, y: position.y, z: position.z });
294
+ resolve();
295
+ }),
296
+ new Promise(async (resolve, reject) => {
297
+ this.controlsCenter.setDuration(duration).reset({ x: currentCenter.x, y: currentCenter.y, z: currentCenter.z });
298
+ await this.controlsCenter.set({ x: center.x, y: center.y, z: center.z });
299
+ resolve();
300
+ })
301
+ ]);
302
+ }
303
+ // 获取控制的位置和朝向 保留3位小数
304
+ getLookAt() {
305
+ //判断需要使用那个相机
306
+ const position = this.perspectiveCamera.position.toArray().map(v => round(v, 3));
307
+ const center = this.controls.target.toArray().map(v => round(v, 3));
308
+ return {
309
+ center: new Vector3(...center),
310
+ position: new Vector3(...position),
311
+ centerPosition: `center: new Vector3(${center.join(",")}),position: new Vector3(${position.join(",")})`
312
+ };
313
+ }
314
+ // 查看对象
315
+ lookatObjects({ objects, duration = 1000, direction = new Vector3(0.3, 0.3, 1), offset = new Vector3(0, 0, 0), distance = 1 }) {
316
+ const box3 = new Box3();
317
+ objects.map(o => {
318
+ o.traverse(oo => {
319
+ box3.expandByObject(oo);
320
+ });
321
+ });
322
+ const size = new Vector3();
323
+ box3.getSize(size);
324
+ const center = new Vector3();
325
+ box3.getCenter(center);
326
+ center.add(offset);
327
+ const modelSize = size.length() * distance;
328
+ if (this.cameraType === "perspectiveCamera") {
329
+ const { fov } = this.getCamera();
330
+ const cameraDistance = modelSize / (2 * Math.tan(fov * Math.PI / 360));
331
+ const position = direction.setLength(cameraDistance).add(center);
332
+ this.setLookAt({ center, position, duration });
333
+ }
334
+ }
335
+ // 三维向量转化成屏幕上的2维向量
336
+ v3tov2(v3) {
337
+ //判断需要使用那个相机
338
+ const camera = this.getCamera();
339
+ v3.project(camera);
340
+ const v2 = new Vector2((v3.x + 1) * this.size.x * 0.5, (-v3.y + 1) * this.size.y * 0.5);
341
+ return v2;
342
+ }
343
+ // 控制器类型
344
+ controlType = "OrbitControls";
345
+ // 记录一下空间平移方式 在切换控制器的时候保留一下平移方式
346
+ originScreenSpacePanning = false;
347
+ //改变控制器的类型
348
+ setControlType(controlType) {
349
+ if (controlType === "OrbitControls") {
350
+ this.controls.screenSpacePanning = this.originScreenSpacePanning;
351
+ this.controls.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
352
+ this.controls.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
353
+ }
354
+ else if (controlType === "MapControls") {
355
+ this.originScreenSpacePanning = this.controls.screenSpacePanning;
356
+ // pan orthogonal to world-space direction camera.up
357
+ this.controls.screenSpacePanning = false;
358
+ this.controls.mouseButtons.LEFT = MOUSE.PAN;
359
+ this.controls.mouseButtons.RIGHT = MOUSE.ROTATE;
360
+ this.controls.touches.ONE = TOUCH.PAN;
361
+ this.controls.touches.TWO = TOUCH.DOLLY_ROTATE;
362
+ }
363
+ }
364
+ // 是否自动渲染
365
+ autoRender = true;
366
+ //是否需要渲染, 当autoRender=false的时候就需要 手动触发渲染
367
+ needRender = false;
368
+ // 停止渲染
369
+ stopRender() {
370
+ threeUtils.removeEventListener("requestAnimationFrame", this.renderBindThis);
371
+ if (!this.rendererLost) {
372
+ this.renderer.forceContextLoss();
373
+ this.setSize(100, 100);
374
+ this.renderer.domElement.style.opacity = "0";
375
+ }
376
+ }
377
+ // 判断是否有 发光和描边等对象 以便判断是否需要 渲染深度纹理
378
+ hasPass() {
379
+ let has_stroke_or_pass = false;
380
+ this.scene.traverse(obj => {
381
+ const { stroke_enable = false, stroke_width = 1, glow_enable = false, glow_width = 1 } = obj.userData;
382
+ if ((stroke_enable && stroke_width > 0) || (glow_enable && glow_width > 0)) {
383
+ has_stroke_or_pass = true;
384
+ }
385
+ });
386
+ return has_stroke_or_pass;
387
+ }
388
+ // 渲染场景
389
+ render() {
390
+ // canvas 没有在页面上面的话 就停止渲染
391
+ if (!threeUtils.inDocument(this.renderer.domElement)) {
392
+ this.stopRender();
393
+ return;
394
+ }
395
+ //透视相机和正交相机参数的时时转换
396
+ if (this.cameraType === "perspectiveCamera") {
397
+ const { center } = this.getLookAt();
398
+ const { fov } = this.perspectiveCamera;
399
+ const distaned = this.perspectiveCamera.position.clone().sub(center).length();
400
+ const zoom = 1 / (Math.tan(degToRad(fov) / 2) * distaned);
401
+ this.orthographicCamera.zoom = zoom;
402
+ this.orthographicCamera.position.copy(this.perspectiveCamera.position.clone().sub(center)).setLength(2500).add(center);
403
+ }
404
+ else if (this.cameraType === "orthographicCamera") {
405
+ const { center } = this.getLookAt();
406
+ const { fov } = this.perspectiveCamera;
407
+ const { zoom } = this.orthographicCamera;
408
+ const distaned = (1 / zoom) / Math.tan(degToRad(fov) / 2);
409
+ this.perspectiveCamera.position.copy(this.orthographicCamera.position.clone().sub(center)).setLength(distaned).add(center);
410
+ }
411
+ //判断需要使用那个相机
412
+ const camera = this.getCamera();
413
+ //判断尺寸发生改变时重置尺寸
414
+ const parentNode = this.renderer.domElement.parentNode;
415
+ if (parentNode.clientWidth !== this.size.x || parentNode.clientHeight !== this.size.y) {
416
+ this.setSize(parentNode.clientWidth, parentNode.clientHeight);
417
+ }
418
+ // 分发鼠标事件
419
+ this.threeMouse.dispatchEvent();
420
+ // 在不自动渲染时, needRender=false 就跳过渲染
421
+ if (!this.autoRender && !this.needRender) {
422
+ return; // 标识未渲染
423
+ }
424
+ this.needRender = false;
425
+ if (this.controls.enabled) {
426
+ this.controls.update();
427
+ }
428
+ // 渲染场景
429
+ this.dispatchEvent({ type: "beforeRender" });
430
+ const hasPass = this.hasPass();
431
+ if (hasPass) {
432
+ // 把基础场景渲染到 帧缓冲区 在后期通道中会用到
433
+ this.renderer.setRenderTarget(this.baseRenderTarget);
434
+ this.renderer.clear();
435
+ this.renderer.render(this.scene, camera);
436
+ this.renderer.setRenderTarget(null);
437
+ }
438
+ // 清空主画布
439
+ this.renderer.clear();
440
+ this.renderer.render(this.scene, camera);
441
+ if (hasPass) {
442
+ // 查找需要绘制描边的对象,并且绘制描边
443
+ this.scene.traverseVisible(obj => {
444
+ stroke_pass(this.renderer, camera, obj, this.baseDepthTexture);
445
+ glow_pass(this.renderer, camera, obj, this.baseDepthTexture);
446
+ });
447
+ }
448
+ this.dispatchEvent({ type: "afterRender" });
449
+ }
450
+ renderBindThis = this.render.bind(this);
451
+ //获取渲染图blob对象
452
+ async getThumbBlob() {
453
+ this.render();
454
+ const blob = await new Promise((resolve, reject) => {
455
+ this.renderer.domElement.toBlob(blob => {
456
+ resolve(blob);
457
+ }, "image/png", 1);
458
+ });
459
+ return blob;
460
+ }
461
+ //获取渲染图base64
462
+ getThumbBase64() {
463
+ this.render();
464
+ const base64 = this.renderer.domElement.toDataURL('image/png');
465
+ return base64;
466
+ }
467
+ //获取渲染图文件对象
468
+ async getThumbFile() {
469
+ const blob = await this.getThumbBlob();
470
+ const file = new File([blob], 'thumb.png', { type: blob.type });
471
+ return file;
472
+ }
473
+ // 下载渲染图
474
+ async download(fileName = "download") {
475
+ this.render();
476
+ const blob = await this.getThumbBlob();
477
+ const base64 = this.renderer.domElement.toDataURL('image/png');
478
+ let saveLink = document.createElement("a");
479
+ saveLink.download = `${fileName}.png`;
480
+ saveLink.href = URL.createObjectURL(blob);
481
+ document.body.append(saveLink);
482
+ saveLink.click();
483
+ }
484
+ _preMesh = new Mesh();
485
+ _preRT = new WebGLRenderTarget(128, 128, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat });
486
+ //预加载纹理图片
487
+ async preloadTexture(src) {
488
+ const texture = threeLoader.getTexture(src);
489
+ while (texture && !texture.image || !texture.image.complete) {
490
+ await new Promise(resolve => setTimeout(resolve, 50));
491
+ }
492
+ // 添加到计划任务中去
493
+ threeUtils.addWork(() => {
494
+ const originRenderTarget = this.renderer.getRenderTarget();
495
+ this.renderer.setRenderTarget(this._preRT);
496
+ this._preMesh.material.map = texture;
497
+ this.renderer.render(this._preMesh, this.perspectiveCamera);
498
+ this.renderer.setRenderTarget(originRenderTarget);
499
+ });
500
+ }
501
+ //预加载网格
502
+ async preloadGeometry(geometry) {
503
+ // 添加到计划任务中去
504
+ threeUtils.addWork(() => {
505
+ const originRenderTarget = this.renderer.getRenderTarget();
506
+ this.renderer.setRenderTarget(this._preRT);
507
+ this._preMesh.geometry = geometry;
508
+ this.renderer.render(this._preMesh, this.perspectiveCamera);
509
+ this.renderer.setRenderTarget(originRenderTarget);
510
+ });
511
+ }
512
+ // 清理渲染目标
513
+ clearRendererTargte(rendererTarget) {
514
+ const originTarget = this.renderer.getRenderTarget();
515
+ this.renderer.setRenderTarget(rendererTarget);
516
+ fullScreenMesh.material = clearShader;
517
+ fullScreenMesh.render(this.renderer);
518
+ this.renderer.clearDepth();
519
+ this.renderer.setRenderTarget(originTarget);
520
+ }
521
+ // 拷贝纹理到渲染目标
522
+ copyTextureToRenderTarget(texture, rendererTarget, alpha = false, opactity = 1) {
523
+ const originTarget = this.renderer.getRenderTarget();
524
+ if (rendererTarget) {
525
+ this.renderer.getSize(this.tempVector2);
526
+ rendererTarget.setSize(this.tempVector2.x, this.tempVector2.y);
527
+ }
528
+ this.renderer.setRenderTarget(rendererTarget);
529
+ fullScreenMesh.material = coptyShader;
530
+ coptyShader.uniforms.map.value = texture;
531
+ coptyShader.uniforms.opacity.value = opactity;
532
+ coptyShader.blending = alpha ? NoBlending : NormalBlending;
533
+ fullScreenMesh.render(this.renderer);
534
+ this.renderer.setRenderTarget(originTarget);
535
+ }
536
+ // 把对象绘制到渲染目标中
537
+ object3dToRendererTarget(obj, rendererTarget) {
538
+ this.clearRendererTargte(rendererTarget);
539
+ const originTarget = this.renderer.getRenderTarget();
540
+ this.renderer.setRenderTarget(rendererTarget);
541
+ this.renderer.getSize(this.tempVector2);
542
+ rendererTarget.setSize(this.tempVector2.x, this.tempVector2.y);
543
+ this.renderer.render(obj, this.getCamera());
544
+ this.renderer.setRenderTarget(originTarget);
545
+ }
546
+ // 给渲染目标的内容加描边
547
+ rendererTagetAddStroke(rendererTarget, strockWidth, color, opacity) {
548
+ const originTarget = this.renderer.getRenderTarget();
549
+ this.clearRendererTargte(this.renderTarget2);
550
+ this.renderer.setRenderTarget(this.renderTarget2);
551
+ this.renderer.getSize(this.tempVector2);
552
+ this.renderTarget2.setSize(this.tempVector2.x, this.tempVector2.y);
553
+ fullScreenMesh.material = strokeShader;
554
+ strokeShader.uniforms.map.value = rendererTarget.texture;
555
+ strokeShader.uniforms.resolution.value.set(this.tempVector2.x, this.tempVector2.y);
556
+ strokeShader.uniforms.strokeWidth.value = strockWidth;
557
+ strokeShader.uniforms.strokeColor.value.set(color);
558
+ strokeShader.uniforms.opacity.value = opacity;
559
+ fullScreenMesh.render(this.renderer);
560
+ this.copyTextureToRenderTarget(this.renderTarget2.texture, rendererTarget, true);
561
+ this.renderer.setRenderTarget(originTarget);
562
+ }
563
+ // 渲染对象的描边
564
+ renderObject3dStroke(obj, strockWidth, color, opacity) {
565
+ this.object3dToRendererTarget(obj, this.renderTarget);
566
+ this.rendererTagetAddStroke(this.renderTarget, strockWidth, color, opacity);
567
+ this.copyTextureToRenderTarget(this.renderTarget.texture, null);
568
+ }
569
+ //给渲染目标加上高斯模糊计算
570
+ renderTargetgaussBlur(rendererTarget, radius, opacity) {
571
+ const originTarget = this.renderer.getRenderTarget();
572
+ this.clearRendererTargte(this.renderTarget2);
573
+ this.renderer.setRenderTarget(this.renderTarget2);
574
+ this.renderer.getSize(this.tempVector2);
575
+ this.renderTarget2.setSize(this.tempVector2.x, this.tempVector2.y);
576
+ fullScreenMesh.material = gaussBlurShader;
577
+ gaussBlurShader.uniforms.map.value = rendererTarget.texture;
578
+ gaussBlurShader.uniforms.radius.value = radius;
579
+ gaussBlurShader.uniforms.opacity.value = opacity;
580
+ gaussBlurShader.uniforms.resolution.value.set(this.tempVector2.x, this.tempVector2.y);
581
+ fullScreenMesh.render(this.renderer);
582
+ this.copyTextureToRenderTarget(this.renderTarget2.texture, rendererTarget, true);
583
+ this.renderer.setRenderTarget(originTarget);
584
+ }
585
+ // 渲染目标上的纹理添加辉光效果
586
+ renderTargetAddGlow(renderTarget, width, color) {
587
+ this.rendererTagetAddStroke(renderTarget, 1 * width, color, 0.3);
588
+ this.rendererTagetAddStroke(renderTarget, 2 * width, color, 0.2);
589
+ this.rendererTagetAddStroke(renderTarget, 3 * width, color, 0.05);
590
+ this.renderTargetgaussBlur(renderTarget, 1, 1);
591
+ this.renderTargetgaussBlur(renderTarget, 1, 1);
592
+ this.renderTargetgaussBlur(renderTarget, 1, 1);
593
+ }
594
+ // 需要做抗锯齿处理 渲染 渲染目标
595
+ renderRendererTarget(renderTarget, alpha = false) {
596
+ FXAAShaderMaterial.uniforms.tDiffuse.value = renderTarget.texture;
597
+ FXAAShaderMaterial.uniforms.resolution.value.set(1 / this.size.x, 1 / this.size.y);
598
+ fullScreenMesh.material = FXAAShaderMaterial;
599
+ fullScreenMesh.render(this.renderer);
600
+ // const FXAAShaderMaterial = new ShaderMaterial({
601
+ // uniforms: {
602
+ // tDiffuse: { value: null }, // 贴图纹理
603
+ // resolution: { value: new Vector2(1 / 1024, 1 / 1024) }
604
+ // },
605
+ // coptyShader.uniforms.map.value = renderTarget.texture;
606
+ // coptyShader.uniforms.opacity.value = 1.0;
607
+ // fullScreenMesh.material = coptyShader;
608
+ // coptyShader.blending = alpha ? NoBlending : NormalBlending;
609
+ // fullScreenMesh.render(this.renderer);
610
+ }
611
+ // 渲染纹理
612
+ renderTexture(texture, alpha = false) {
613
+ coptyShader.uniforms.map.value = texture;
614
+ coptyShader.uniforms.opacity.value = 1.0;
615
+ fullScreenMesh.material = coptyShader;
616
+ coptyShader.blending = alpha ? NoBlending : NormalBlending;
617
+ fullScreenMesh.render(this.renderer);
618
+ }
619
+ }