useThree copy.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. import * as THREE from 'three';
  2. import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
  3. // 导入轨道控制器
  4. import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
  5. import gsap from 'gsap';
  6. import ResourceTracker from '/@/utils/threejs/ResourceTracker';
  7. const resourceTracker = new ResourceTracker();
  8. const track = resourceTracker.track.bind(resourceTracker);
  9. class UseThree {
  10. container: HTMLCanvasElement;
  11. camera: THREE.PerspectiveCamera | null = null;
  12. scene: THREE.Scene | null = null;
  13. renderer: THREE.WebGLRenderer | null = null;
  14. orbitControls: OrbitControls | null = null;
  15. giftLoader: THREE.Object3D | null = null;
  16. animationMixer: THREE.AnimationMixer | null = null;
  17. animationAction: THREE.AnimationAction | null = null;
  18. clock: THREE.Clock = new THREE.Clock(); // 计时器
  19. timeoutId: NodeJS.Timeout | null = null;
  20. animationId: number = 0;
  21. constructor(selector) {
  22. this.animationId = 0;
  23. this.container = document.querySelector(selector);
  24. //初始化
  25. this.init();
  26. this.animate();
  27. window.addEventListener('resize', this.resizeRenderer.bind(this));
  28. // 添加滚动事件,鼠标滚动模型执行动画
  29. // window.addEventListener('wheel', this.wheelRenderer.bind(this));
  30. }
  31. init() {
  32. // 初始化场景
  33. this.initScene();
  34. // 初始化环境光
  35. this.initLight();
  36. // 初始化相机
  37. this.initCamera();
  38. //初始化渲染器
  39. this.initRenderer();
  40. // 初始化控制器
  41. this.initControles();
  42. }
  43. initScene() {
  44. this.scene = new THREE.Scene();
  45. const axesHelper = new THREE.AxesHelper(5);
  46. // this.scene.add(axesHelper);
  47. }
  48. initLight() {
  49. const light = new THREE.AmbientLight(0xfafafa, 1);
  50. light.position.set(0, 200, 200);
  51. (this.scene as THREE.Scene).add(light);
  52. }
  53. initCamera() {
  54. this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
  55. this.camera.position.set(0, 0.2, 0.3);
  56. }
  57. initRenderer() {
  58. this.renderer = new THREE.WebGLRenderer({ antialias: true });
  59. // 设置屏幕像素比
  60. this.renderer.setPixelRatio(window.devicePixelRatio);
  61. // 设置渲染的尺寸
  62. this.renderer.setSize(window.innerWidth, window.innerHeight);
  63. // 色调映射
  64. this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
  65. // 曝光程度
  66. this.renderer.toneMappingExposure = 3;
  67. this.container.appendChild(this.renderer.domElement);
  68. }
  69. initControles() {
  70. this.orbitControls = new OrbitControls(this.camera as THREE.Camera, this.renderer?.domElement);
  71. }
  72. test() {
  73. //
  74. const planeGeometry = new THREE.PlaneGeometry(1, 1);
  75. const meshStandardMaterial = new THREE.MeshStandardMaterial();
  76. const mesh = new THREE.Mesh(planeGeometry, meshStandardMaterial);
  77. mesh.position.set(0, -0.2, 0);
  78. mesh.rotation.z = Math.PI / 4;
  79. this.scene?.add(mesh);
  80. }
  81. setGLTFModel(modalName) {
  82. const a = new Date().getTime() / 1000;
  83. return new Promise(async (resolve, reject) => {
  84. try {
  85. const db = window['CustomDB'];
  86. const modalArr = await db.modal.where('modalName').equals(modalName).toArray();
  87. if (modalArr.length > 0) {
  88. const modalValue = modalArr[0].modalVal;
  89. new THREE.ObjectLoader().parse(modalValue, (e) => {
  90. const group = e.children[0];
  91. const cityMaterial = new THREE.MeshBasicMaterial({
  92. color: new THREE.Color(0x0c016f),
  93. });
  94. group?.traverse((item: THREE.Mesh) => {
  95. if (item.type === 'Mesh') {
  96. item.material = cityMaterial;
  97. this.setMaterial(item);
  98. }
  99. });
  100. this.scene?.add(e.children[0]);
  101. console.log(new Date().getTime() / 1000 - a + ' s');
  102. resolve(this.scene);
  103. });
  104. }
  105. } catch (error) {
  106. reject('加载模型出错');
  107. }
  108. });
  109. }
  110. setMaterial(obj) {
  111. obj.geometry.computeBoundingBox();
  112. const { max, min } = obj.geometry.boundingBox;
  113. const distance = max.y - min.y + 2;
  114. obj.material.onBeforeCompile = (shader) => {
  115. shader.uniforms.uDistance = {
  116. value: distance,
  117. };
  118. shader.uniforms.uTopColor = {
  119. value: new THREE.Color(0xaaaeff),
  120. };
  121. this.addGrad(shader);
  122. this.addRadiusSpread(shader);
  123. this.addLineSpread(shader);
  124. this.addToSpread(shader);
  125. };
  126. }
  127. addGrad(shader) {
  128. shader.vertexShader = shader.vertexShader.replace(
  129. '#include <common>',
  130. `
  131. #include <common>
  132. varying vec3 vPosition;
  133. `
  134. );
  135. shader.vertexShader = shader.vertexShader.replace(
  136. '#include <fog_vertex>',
  137. `
  138. #include <fog_vertex>
  139. vPosition = position;
  140. `
  141. );
  142. shader.fragmentShader = shader.fragmentShader.replace(
  143. '#include <common>',
  144. `
  145. #include <common>
  146. uniform vec3 uTopColor;
  147. uniform float uDistance;
  148. varying vec3 vPosition;
  149. `
  150. );
  151. shader.fragmentShader = shader.fragmentShader.replace(
  152. '#include <dithering_fragment>',
  153. `
  154. #include <dithering_fragment>
  155. vec4 distGradColor = gl_FragColor;
  156. // 设置混合的百分比
  157. float gradMix = (vPosition.y + (uDistance / 2.0)) / uDistance / 5.0;
  158. // 设置混合颜色
  159. vec3 colorMix = mix(distGradColor.xyz, uTopColor, gradMix);
  160. gl_FragColor = vec4(colorMix, 1.0);
  161. //#end#
  162. `
  163. );
  164. }
  165. addRadiusSpread(shader) {
  166. // 设置扩散中心店
  167. shader.uniforms.uRadiusSpreadCenter = {
  168. value: new THREE.Vector2(0, 0),
  169. };
  170. // 扩散时间
  171. shader.uniforms.uRadiusSpreadTime = {
  172. value: 0,
  173. };
  174. shader.uniforms.uRadiusSpreadWidth = {
  175. value: 20,
  176. };
  177. shader.fragmentShader = shader.fragmentShader.replace(
  178. '#include <common>',
  179. `
  180. #include <common>
  181. uniform vec2 uRadiusSpreadCenter;
  182. uniform float uRadiusSpreadTime;
  183. uniform float uRadiusSpreadWidth;
  184. `
  185. );
  186. shader.fragmentShader = shader.fragmentShader.replace(
  187. '//#end#',
  188. `
  189. float spreadRadius = distance(vPosition.xz + vec2(-720, -550), uRadiusSpreadCenter);
  190. // 扩散范围
  191. float spreadIndex = -(spreadRadius - uRadiusSpreadTime) * (spreadRadius - uRadiusSpreadTime ) + uRadiusSpreadWidth;
  192. if(spreadIndex > 0.0){
  193. gl_FragColor = mix(gl_FragColor, vec4(1.0, 1.0, 1.0, 1.0), spreadIndex / uRadiusSpreadWidth );
  194. }
  195. //#end#
  196. `
  197. );
  198. gsap.to(shader.uniforms.uRadiusSpreadTime, {
  199. value: 250,
  200. duration: 1,
  201. ease: 'none',
  202. repeat: -1,
  203. });
  204. }
  205. addLineSpread(shader) {
  206. // 设置扩散中心店
  207. shader.uniforms.uLineSpreadCenter = {
  208. value: new THREE.Vector2(0, 0),
  209. };
  210. // 扩散时间
  211. shader.uniforms.uLineSpreadTime = {
  212. value: 0,
  213. };
  214. shader.uniforms.uLineSpreadWidth = {
  215. value: 20,
  216. };
  217. shader.fragmentShader = shader.fragmentShader.replace(
  218. '#include <common>',
  219. `
  220. #include <common>
  221. uniform vec2 uLineSpreadCenter;
  222. uniform float uLineSpreadTime;
  223. uniform float uLineSpreadWidth;
  224. `
  225. );
  226. shader.fragmentShader = shader.fragmentShader.replace(
  227. '//#end#',
  228. `
  229. float spreadLine = vPosition.x - 400.0;
  230. // 扩散范围
  231. float spreadLineIndex = -(spreadLine - uLineSpreadTime * 5.0) * (spreadLine - uLineSpreadTime * 5.0) + uLineSpreadWidth;
  232. if(spreadLineIndex > 0.0){
  233. gl_FragColor = mix(gl_FragColor, vec4(1.0, 1.0, 1.0, 1.0), spreadLineIndex / uLineSpreadWidth );
  234. }
  235. //#end#
  236. `
  237. );
  238. gsap.to(shader.uniforms.uLineSpreadTime, {
  239. value: 150,
  240. duration: 2,
  241. ease: 'none',
  242. repeat: -1,
  243. });
  244. }
  245. addToSpread(shader) {
  246. // 设置扩散中心店
  247. shader.uniforms.uTopSpreadCenter = {
  248. value: new THREE.Vector2(0, 0),
  249. };
  250. // 扩散时间
  251. shader.uniforms.uTopSpreadTime = {
  252. value: 0,
  253. };
  254. shader.uniforms.uTopSpreadWidth = {
  255. value: 20,
  256. };
  257. shader.fragmentShader = shader.fragmentShader.replace(
  258. '#include <common>',
  259. `
  260. #include <common>
  261. uniform vec2 uTopSpreadCenter;
  262. uniform float uTopSpreadTime;
  263. uniform float uTopSpreadWidth;
  264. `
  265. );
  266. shader.fragmentShader = shader.fragmentShader.replace(
  267. '//#end#',
  268. `
  269. float spreadTopLine = vPosition.y * 2.0;
  270. // 扩散范围uTopSpreadWidth
  271. float spreadTopIndex = -(spreadTopLine - uTopSpreadTime) * (spreadTopLine - uTopSpreadTime) + uTopSpreadWidth;
  272. if(spreadTopIndex > 0.0){
  273. gl_FragColor = mix(gl_FragColor, vec4(0.0, 1.0, 1.0, 1.0), spreadTopIndex / uTopSpreadWidth / uTopSpreadTime * 2.0);
  274. }
  275. //#end#
  276. `
  277. );
  278. gsap.to(shader.uniforms.uTopSpreadTime, {
  279. value: 200,
  280. duration: 5,
  281. ease: 'none',
  282. repeat: -1,
  283. });
  284. }
  285. addLine(geometry) {
  286. const edges = new THREE.EdgesGeometry(geometry);
  287. const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xff0000 }));
  288. (this.scene as THREE.Scene).add(line);
  289. // console.log(line);
  290. }
  291. /* 场景环境背景 */
  292. setEnvMap(hdr) {
  293. new RGBELoader().setPath('/public/model/hdr/').load(hdr + '.hdr', (texture) => {
  294. texture.mapping = THREE.EquirectangularReflectionMapping;
  295. (this.scene as THREE.Scene).background = texture;
  296. (this.scene as THREE.Scene).environment = texture;
  297. });
  298. }
  299. render() {
  300. const delta = this.clock.getDelta();
  301. this.renderer?.render(this.scene as THREE.Object3D, this.camera as THREE.Camera);
  302. this.animationMixer?.update(delta);
  303. }
  304. animate() {
  305. // this.renderer?.setAnimationLoop(this.render.bind(this));
  306. if (this.animationId != -1) {
  307. this.render();
  308. this.animationId = requestAnimationFrame(this.animate.bind(this));
  309. }
  310. }
  311. resizeRenderer() {
  312. // 更新相机比例
  313. (this.camera as THREE.PerspectiveCamera).aspect = window.innerWidth / window.innerHeight;
  314. // 刷新相机矩阵
  315. this.camera?.updateProjectionMatrix();
  316. // 设置场景尺寸
  317. this.renderer?.setSize(window.innerWidth, window.innerHeight);
  318. }
  319. wheelRenderer(e: WheelEvent) {
  320. const timeScale = e.deltaY > 0 ? 1 : -1;
  321. this.animationAction?.setEffectiveTimeScale(timeScale);
  322. (this.animationAction as THREE.AnimationAction).paused = false;
  323. this.animationAction?.play();
  324. if (this.timeoutId) {
  325. clearTimeout(this.timeoutId);
  326. }
  327. this.timeoutId = setTimeout(() => {
  328. this.animationAction?.halt(0.5);
  329. }, 500);
  330. }
  331. clearScene() {
  332. this.scene?.children.forEach((obj: any) => {
  333. if (obj.type === 'Group') {
  334. obj.traverse(function (item: any) {
  335. if (item.type === 'Mesh') {
  336. item.geometry.dispose();
  337. item.material.dispose();
  338. !!item.clear && item.clear();
  339. }
  340. });
  341. } else if (obj.material) {
  342. obj.material.dispose();
  343. } else if (obj.geometry) {
  344. obj.geometry.dispose();
  345. }
  346. this.scene?.remove(obj);
  347. !!obj.clear ?? obj.clear();
  348. obj = null;
  349. });
  350. const gl = this.renderer?.domElement.getContext('webgl');
  351. gl && gl?.getExtension('WEBGL_lose_context')?.loseContext();
  352. this.renderer?.forceContextLoss();
  353. this.renderer?.dispose();
  354. this.camera = null;
  355. this.orbitControls = null;
  356. this.renderer.domElement = null;
  357. this.renderer = null;
  358. // css3dRender.domElement = null;
  359. // css3dRender = null;
  360. // model3D.innerHTML = '';
  361. // css3D.innerHTML = '';
  362. // model3D = null
  363. // css3D = null
  364. !!this.scene?.clear ?? this.scene?.clear();
  365. cancelAnimationFrame(this.animationId);
  366. this.animationId = -1;
  367. // this.stats = null
  368. // scene = null
  369. THREE.Cache.clear();
  370. console.log('3D环境已清理干净');
  371. }
  372. }
  373. export default UseThree;