| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- import * as THREE from 'three';
- import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
- // 导入轨道控制器
- import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
- import gsap from 'gsap';
- import ResourceTracker from '/@/utils/threejs/ResourceTracker';
- const resourceTracker = new ResourceTracker();
- const track = resourceTracker.track.bind(resourceTracker);
- class UseThree {
- container: HTMLCanvasElement;
- camera: THREE.PerspectiveCamera | null = null;
- scene: THREE.Scene | null = null;
- renderer: THREE.WebGLRenderer | null = null;
- orbitControls: OrbitControls | null = null;
- giftLoader: THREE.Object3D | null = null;
- animationMixer: THREE.AnimationMixer | null = null;
- animationAction: THREE.AnimationAction | null = null;
- clock: THREE.Clock = new THREE.Clock(); // 计时器
- timeoutId: NodeJS.Timeout | null = null;
- animationId: number = 0;
- constructor(selector) {
- this.animationId = 0;
- this.container = document.querySelector(selector);
- //初始化
- this.init();
- this.animate();
- window.addEventListener('resize', this.resizeRenderer.bind(this));
- // 添加滚动事件,鼠标滚动模型执行动画
- // window.addEventListener('wheel', this.wheelRenderer.bind(this));
- }
- init() {
- // 初始化场景
- this.initScene();
- // 初始化环境光
- this.initLight();
- // 初始化相机
- this.initCamera();
- //初始化渲染器
- this.initRenderer();
- // 初始化控制器
- this.initControles();
- }
- initScene() {
- this.scene = new THREE.Scene();
- const axesHelper = new THREE.AxesHelper(5);
- // this.scene.add(axesHelper);
- }
- initLight() {
- const light = new THREE.AmbientLight(0xfafafa, 1);
- light.position.set(0, 200, 200);
- (this.scene as THREE.Scene).add(light);
- }
- initCamera() {
- this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
- this.camera.position.set(0, 0.2, 0.3);
- }
- initRenderer() {
- this.renderer = new THREE.WebGLRenderer({ antialias: true });
- // 设置屏幕像素比
- this.renderer.setPixelRatio(window.devicePixelRatio);
- // 设置渲染的尺寸
- this.renderer.setSize(window.innerWidth, window.innerHeight);
- // 色调映射
- this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
- // 曝光程度
- this.renderer.toneMappingExposure = 3;
- this.container.appendChild(this.renderer.domElement);
- }
- initControles() {
- this.orbitControls = new OrbitControls(this.camera as THREE.Camera, this.renderer?.domElement);
- }
- test() {
- //
- const planeGeometry = new THREE.PlaneGeometry(1, 1);
- const meshStandardMaterial = new THREE.MeshStandardMaterial();
- const mesh = new THREE.Mesh(planeGeometry, meshStandardMaterial);
- mesh.position.set(0, -0.2, 0);
- mesh.rotation.z = Math.PI / 4;
- this.scene?.add(mesh);
- }
- setGLTFModel(modalName) {
- const a = new Date().getTime() / 1000;
- return new Promise(async (resolve, reject) => {
- try {
- const db = window['CustomDB'];
- const modalArr = await db.modal.where('modalName').equals(modalName).toArray();
- if (modalArr.length > 0) {
- const modalValue = modalArr[0].modalVal;
- new THREE.ObjectLoader().parse(modalValue, (e) => {
- const group = e.children[0];
- const cityMaterial = new THREE.MeshBasicMaterial({
- color: new THREE.Color(0x0c016f),
- });
- group?.traverse((item: THREE.Mesh) => {
- if (item.type === 'Mesh') {
- item.material = cityMaterial;
- this.setMaterial(item);
- }
- });
- this.scene?.add(e.children[0]);
- console.log(new Date().getTime() / 1000 - a + ' s');
- resolve(this.scene);
- });
- }
- } catch (error) {
- reject('加载模型出错');
- }
- });
- }
- setMaterial(obj) {
- obj.geometry.computeBoundingBox();
- const { max, min } = obj.geometry.boundingBox;
- const distance = max.y - min.y + 2;
- obj.material.onBeforeCompile = (shader) => {
- shader.uniforms.uDistance = {
- value: distance,
- };
- shader.uniforms.uTopColor = {
- value: new THREE.Color(0xaaaeff),
- };
- this.addGrad(shader);
- this.addRadiusSpread(shader);
- this.addLineSpread(shader);
- this.addToSpread(shader);
- };
- }
- addGrad(shader) {
- shader.vertexShader = shader.vertexShader.replace(
- '#include <common>',
- `
- #include <common>
- varying vec3 vPosition;
- `
- );
- shader.vertexShader = shader.vertexShader.replace(
- '#include <fog_vertex>',
- `
- #include <fog_vertex>
- vPosition = position;
- `
- );
- shader.fragmentShader = shader.fragmentShader.replace(
- '#include <common>',
- `
- #include <common>
- uniform vec3 uTopColor;
- uniform float uDistance;
- varying vec3 vPosition;
-
- `
- );
- shader.fragmentShader = shader.fragmentShader.replace(
- '#include <dithering_fragment>',
- `
- #include <dithering_fragment>
- vec4 distGradColor = gl_FragColor;
- // 设置混合的百分比
- float gradMix = (vPosition.y + (uDistance / 2.0)) / uDistance / 5.0;
- // 设置混合颜色
- vec3 colorMix = mix(distGradColor.xyz, uTopColor, gradMix);
- gl_FragColor = vec4(colorMix, 1.0);
- //#end#
- `
- );
- }
- addRadiusSpread(shader) {
- // 设置扩散中心店
- shader.uniforms.uRadiusSpreadCenter = {
- value: new THREE.Vector2(0, 0),
- };
- // 扩散时间
- shader.uniforms.uRadiusSpreadTime = {
- value: 0,
- };
- shader.uniforms.uRadiusSpreadWidth = {
- value: 20,
- };
- shader.fragmentShader = shader.fragmentShader.replace(
- '#include <common>',
- `
- #include <common>
- uniform vec2 uRadiusSpreadCenter;
- uniform float uRadiusSpreadTime;
- uniform float uRadiusSpreadWidth;
- `
- );
- shader.fragmentShader = shader.fragmentShader.replace(
- '//#end#',
- `
- float spreadRadius = distance(vPosition.xz + vec2(-720, -550), uRadiusSpreadCenter);
- // 扩散范围
- float spreadIndex = -(spreadRadius - uRadiusSpreadTime) * (spreadRadius - uRadiusSpreadTime ) + uRadiusSpreadWidth;
- if(spreadIndex > 0.0){
- gl_FragColor = mix(gl_FragColor, vec4(1.0, 1.0, 1.0, 1.0), spreadIndex / uRadiusSpreadWidth );
- }
- //#end#
- `
- );
- gsap.to(shader.uniforms.uRadiusSpreadTime, {
- value: 250,
- duration: 1,
- ease: 'none',
- repeat: -1,
- });
- }
- addLineSpread(shader) {
- // 设置扩散中心店
- shader.uniforms.uLineSpreadCenter = {
- value: new THREE.Vector2(0, 0),
- };
- // 扩散时间
- shader.uniforms.uLineSpreadTime = {
- value: 0,
- };
- shader.uniforms.uLineSpreadWidth = {
- value: 20,
- };
- shader.fragmentShader = shader.fragmentShader.replace(
- '#include <common>',
- `
- #include <common>
- uniform vec2 uLineSpreadCenter;
- uniform float uLineSpreadTime;
- uniform float uLineSpreadWidth;
- `
- );
- shader.fragmentShader = shader.fragmentShader.replace(
- '//#end#',
- `
- float spreadLine = vPosition.x - 400.0;
- // 扩散范围
- float spreadLineIndex = -(spreadLine - uLineSpreadTime * 5.0) * (spreadLine - uLineSpreadTime * 5.0) + uLineSpreadWidth;
- if(spreadLineIndex > 0.0){
- gl_FragColor = mix(gl_FragColor, vec4(1.0, 1.0, 1.0, 1.0), spreadLineIndex / uLineSpreadWidth );
- }
- //#end#
- `
- );
- gsap.to(shader.uniforms.uLineSpreadTime, {
- value: 150,
- duration: 2,
- ease: 'none',
- repeat: -1,
- });
- }
- addToSpread(shader) {
- // 设置扩散中心店
- shader.uniforms.uTopSpreadCenter = {
- value: new THREE.Vector2(0, 0),
- };
- // 扩散时间
- shader.uniforms.uTopSpreadTime = {
- value: 0,
- };
- shader.uniforms.uTopSpreadWidth = {
- value: 20,
- };
- shader.fragmentShader = shader.fragmentShader.replace(
- '#include <common>',
- `
- #include <common>
- uniform vec2 uTopSpreadCenter;
- uniform float uTopSpreadTime;
- uniform float uTopSpreadWidth;
- `
- );
- shader.fragmentShader = shader.fragmentShader.replace(
- '//#end#',
- `
- float spreadTopLine = vPosition.y * 2.0;
- // 扩散范围uTopSpreadWidth
- float spreadTopIndex = -(spreadTopLine - uTopSpreadTime) * (spreadTopLine - uTopSpreadTime) + uTopSpreadWidth;
- if(spreadTopIndex > 0.0){
- gl_FragColor = mix(gl_FragColor, vec4(0.0, 1.0, 1.0, 1.0), spreadTopIndex / uTopSpreadWidth / uTopSpreadTime * 2.0);
- }
- //#end#
- `
- );
- gsap.to(shader.uniforms.uTopSpreadTime, {
- value: 200,
- duration: 5,
- ease: 'none',
- repeat: -1,
- });
- }
- addLine(geometry) {
- const edges = new THREE.EdgesGeometry(geometry);
- const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xff0000 }));
- (this.scene as THREE.Scene).add(line);
- // console.log(line);
- }
- /* 场景环境背景 */
- setEnvMap(hdr) {
- new RGBELoader().setPath('/public/model/hdr/').load(hdr + '.hdr', (texture) => {
- texture.mapping = THREE.EquirectangularReflectionMapping;
- (this.scene as THREE.Scene).background = texture;
- (this.scene as THREE.Scene).environment = texture;
- });
- }
- render() {
- const delta = this.clock.getDelta();
- this.renderer?.render(this.scene as THREE.Object3D, this.camera as THREE.Camera);
- this.animationMixer?.update(delta);
- }
- animate() {
- // this.renderer?.setAnimationLoop(this.render.bind(this));
- if (this.animationId != -1) {
- this.render();
- this.animationId = requestAnimationFrame(this.animate.bind(this));
- }
- }
- resizeRenderer() {
- // 更新相机比例
- (this.camera as THREE.PerspectiveCamera).aspect = window.innerWidth / window.innerHeight;
- // 刷新相机矩阵
- this.camera?.updateProjectionMatrix();
- // 设置场景尺寸
- this.renderer?.setSize(window.innerWidth, window.innerHeight);
- }
- wheelRenderer(e: WheelEvent) {
- const timeScale = e.deltaY > 0 ? 1 : -1;
- this.animationAction?.setEffectiveTimeScale(timeScale);
- (this.animationAction as THREE.AnimationAction).paused = false;
- this.animationAction?.play();
- if (this.timeoutId) {
- clearTimeout(this.timeoutId);
- }
- this.timeoutId = setTimeout(() => {
- this.animationAction?.halt(0.5);
- }, 500);
- }
- clearScene() {
- this.scene?.children.forEach((obj: any) => {
- if (obj.type === 'Group') {
- obj.traverse(function (item: any) {
- if (item.type === 'Mesh') {
- item.geometry.dispose();
- item.material.dispose();
- !!item.clear && item.clear();
- }
- });
- } else if (obj.material) {
- obj.material.dispose();
- } else if (obj.geometry) {
- obj.geometry.dispose();
- }
- this.scene?.remove(obj);
- !!obj.clear ?? obj.clear();
- obj = null;
- });
- const gl = this.renderer?.domElement.getContext('webgl');
- gl && gl?.getExtension('WEBGL_lose_context')?.loseContext();
- this.renderer?.forceContextLoss();
- this.renderer?.dispose();
- this.camera = null;
- this.orbitControls = null;
- this.renderer.domElement = null;
- this.renderer = null;
- // css3dRender.domElement = null;
- // css3dRender = null;
- // model3D.innerHTML = '';
- // css3D.innerHTML = '';
- // model3D = null
- // css3D = null
- !!this.scene?.clear ?? this.scene?.clear();
- cancelAnimationFrame(this.animationId);
- this.animationId = -1;
- // this.stats = null
- // scene = null
- THREE.Cache.clear();
- console.log('3D环境已清理干净');
- }
- }
- export default UseThree;
|