得药当归、得药当归!

71 阅读3分钟

threeJs常用函数封装

three小知识

模型渲染顺序  Mesh属性 renderOrder = 0
如果在中途修改纹理色彩空间,需要设置`texture.needsUpdate=true`才可以生效。

压缩模型

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';

  
/* 压缩 */
function loaderDraco() {
    const loader = new GLTFLoader();
    const draco = new DRACOLoader();
    // DRACOLoader依赖examples\jsm\libs\下的 draco 里面多个解压文件
    draco.setDecoderPath('./draco/');//根据pubic里面解压文件结构设置
    loader.setDRACOLoader(draco);
    return loader;
}
export default loaderDraco;

加载模型

import * as THREE from 'three';
import loaderDraco from '../hook/DracoLoader';

  
function modelFn(url: any) {
    let modelArr = new THREE.Group();
    // let mod:any;
    loaderDraco().load(url, (gltf: any) => {
        modelArr.add(gltf.scene);
        // mod = gltf.scene
    })
    return modelArr
}
export default modelFn;

范围随机数

const getRandomIntFn = (min: number, max: number) => {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

export default getRandomIntFn;

getRandomIntFn(0, 20)

纹理贴图

import { THREE } from "../hook/web3d";
const texLoader = new THREE.TextureLoader();

  
function textureFn(url: any, material: any = 0) {
    let Mesh: any;
    let texture = texLoader.load(url);
    texture.colorSpace = THREE.SRGBColorSpace;

    if (material == 0) {
        const geometry = new THREE.BoxGeometry(1, 1, 1);
        const materials = new THREE.MeshBasicMaterial({
            map: texture,
            transparent: true,
        })
        Mesh = new THREE.Mesh(geometry, materials);
    }
    if (material == 'Sprite') {
        const spriteMaterial = new THREE.SpriteMaterial({
            map: texture,
            transparent: true,
        })
        Mesh = new THREE.Sprite(spriteMaterial);
    }
    return Mesh
}
export default textureFn;

gsap动画

import { useStore } from '../../pinia/index'

const store = useStore()

import { camera, gsap, controls } from '../hooks/web3d';

  


/* 相机动画函数 */

let GsapFn = (posArr:any,delays:number=0,durations:number=2)=>{
    let posArrz = posArr.z + 30;
    gsap.to(camera.position, {
        x: posArr.x +30,
        y: posArr.y +30,
        z: posArrz,
        delay: delays,
        duration: durations,  
        onStart: () => {
            controls.reset();
            store.contrbol = false;
        },
        
        onUpdate: () => {
            camera.lookAt(posArr);
            controls.target.set(posArr.x,posArr.y,posArr.z);
            controls.update();
        },
    });
}


const dbgsapFn = ()=>{
    gsap.to(camera.position, {
        x: 0,
        y: 150,
        z: 300,
        delay:1,
        duration: 1.5,
        onStart: () => {
            controls.reset()
        },
        onUpdate: () => {
            camera.lookAt(0,0,0);
        },
    })
}

export { dbgsapFn }
export default GsapFn;

动画节流

import { ref } from 'vue';
import { stats, controls, renderer, scene, camera } from "./hook/web3d";


let renderbol = ref(false);
let render = () => {
  if (renderbol.value) {
    stats.update();
    controls.update()
    console.log('动画防抖节流');
  }
  renderer.render(scene, camera);
  requestAnimationFrame(render);
}
render()

  
let timer: any = null;
const throttle = (delay = 10000) => {
  renderbol.value = true
  if (timer == null) {
    timer = setTimeout(() => {
      clearTimeout(timer)
      timer = null;
      renderbol.value = false
    }, delay);
  }
}
throttle(10000)

controls.addEventListener('change', () => {
  renderbol.value = true;
  throttle(5000)
})

web3d

import * as THREE from 'three';
import gsap from "gsap";
import { PathGeometry, PathPointList } from 'three.path';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';


import model from '../model/model';
  
  
/* 墨 */
let width = window.innerWidth;
let height = window.innerHeight;


/* 墨 */
/* 一. 创建3D场景对象Scene */
const scene = new THREE.Scene();

/* 检测器 */
const stats: any = new Stats();


/* 二.相机 */

/* 1.透视投影相机视锥体 */
let camera = new THREE.PerspectiveCamera(60, width / height, 0.5, 1650);
/* 2.相机位置 */
camera.position.set(0, 100, 300)
/* 3.观察目标 */
// camera.lookAt(model.position)



/* 三.WebGL渲染器 */
/* 1.创建渲染器 */
let renderer = new THREE.WebGLRenderer({
    // alpha: true,
    antialias: true,
    logarithmicDepthBuffer: true
})

/* 2.定义尺寸 颜色 抗锯齿 设备像素 条纹影响渲染效果优化*/
renderer.setSize(width, height);
renderer.setClearColor('#e9ebff', 1);
renderer.setPixelRatio(window.devicePixelRatio);

/* 3.渲染 场景及相机 */
renderer.render(scene, camera)


/* 墨 */
/* 平行光 环境*/
const directionalLight = new THREE.DirectionalLight('#fff', 3);
directionalLight.position.set(300, 150, 150);


const ambient = new THREE.AmbientLight('#fff', 3);

/* 相机控件 */
const controls = new OrbitControls(camera, renderer.domElement)


// 上下旋转范围
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI / 2.1;
controls.minDistance = 20;
controls.maxDistance = 800;
controls.enableDamping = true;
controls.dampingFactor = 0.03;

  
 
/* 1. 添加模型 */ // directionalLight  sphereMesh dibanMod
let sceneArr = [ambient, model]

scene.add(...sceneArr)



/* 墨 窗口响应 */
window.onresize = () => {
    let width = window.innerWidth;
    let height = window.innerHeight;

    renderer.setSize(width, height);
    camera.aspect = width / height;
    camera.updateProjectionMatrix();
    renderer.render(scene, camera);
}

/* import { ref } from 'vue';
let renderbol = ref(false);
let render = () => {
    if (renderbol.value) {
        stats.update();
        controls.update()
        renderer.render(scene, camera);
    }
    requestAnimationFrame(render);
}
render()

  
let timer: any = null;
const throttle = (delay = 10000) => {
    renderbol.value = true

    if (timer == null) {
        timer = setTimeout(() => {
            clearTimeout(timer)
            timer = null;
            renderbol.value = false
        }, delay);
    }
}
throttle(10000) */


export {
    scene, camera, renderer, controls, width, height,
    THREE, stats, gsap, PathGeometry, PathPointList
}

关键帧动画物体状态最后一步监听

const mixer = new THREE.AnimationMixer(gltf.scene);
const action=mixer.clipAction(gltf.animations[item.animationNumber]);
action.play();
action.loop = THREE.LoopOnce;
action.clampWhenFinished = true;
if (action.paused) {
    cancelAnimationFrame(planAnimarenderbol)
}
mixer.addEventListener( 'finished', function(e) {
    cancelAnimationFrame(planAnimarenderbol)
});

异步加载模型

  • 压缩-
/* 压缩 */


// 创建加载器实例(避免重复创建)

let gltfLoader:any = null

let dracoLoader:any = null

  


// 初始化加载器

const initLoaders = (dracoDecoderPath = "./draco/") => {

  if (!gltfLoader) {

    dracoLoader = new DRACOLoader()

    dracoLoader.setDecoderPath(dracoDecoderPath)

   

    gltfLoader = new GLTFLoader()

    gltfLoader.setDRACOLoader(dracoLoader)

  }

}

initLoaders()

  


const loadModel = async (url:any, onProgress:any) => {

  if (!gltfLoader) {

    throw new Error('mesh  loader not initialized')

  }

  


  return new Promise((resolve, reject) => {

    gltfLoader.load(

      url,

      (gltf:any) => resolve(gltf),

      (xhr:any) => {

        if (typeof onProgress === 'function') {

          onProgress((xhr.loaded / xhr.total) * 100)

        }

      },

      (error:any) => reject(error)

    )

  })

}


export default loadModel;
  • 调用
const loadDemoModel = async () => {
  try {

    const model: any = await loadModel(
      '/mo-Group/shunxN.glb',
      (progress: any) => {
        console.log(`Loading: ${progress.toFixed(1)}%`)
      }
    )
    console.log('模型加载完成', model)

    // 这里添加将模型添加到场景的代码
    modelNeiGroup.add(model.scene)
    modelNeiGroup.visible = false;

  } catch (error) {
    console.error('模型加载失败:', error)

  }

}