ThreeJS

您所在的位置:网站首页 圣诞节卡通卡片 ThreeJS

ThreeJS

2023-08-23 21:03| 来源: 网络整理| 查看: 265

 

import * as THREE from "three";

import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";

import { Water } from "three/examples/jsm/objects/Water2";

import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader";

import gsap from "gsap";

export default {

  name: "HOME",

  components: {

    // vueQr,

    // glHome,

  },

  data() {

    return {};

  },

  mounted() {

    //使用控制器控制3D拖动旋转OrbitControls

    //控制3D物体移动

    //1.创建场景

    const scene = new THREE.Scene();

    //2.创建相机

    const camera = new THREE.PerspectiveCamera(

      75,

      window.innerWidth / window.innerHeight,

      0.1,

      20000

    );

    //设置相机位置

    camera.position.set(0, 0, 10);

    //将相机添加到场景

    scene.add(camera);

    //初始化渲染器

    const render = new THREE.WebGLRenderer({

      //设置抗锯齿,防失真

      antialis: true,

      //对数深度缓冲区,防止模型闪烁

      logarithmicdepthbuffer: true,

    });

    /*设置场景渲染编码threejs将贴图的编码都默认设置为THREE.LinearEncoding,

     *导致图片色彩失真(色彩不像正常那么鲜艳,会灰蒙蒙的),所以务必将场景中的所有贴图的编码都调整为THREE.sRGBEncoding

     */

    render.outputEncoding = THREE.sRGBEncoding;

    //设置渲染器的尺寸

    render.setSize(window.innerWidth, window.innerHeight);

    //清除默认设置颜色

    render.setClearColor("#000");

    //设置曝光类型(电影类型、文本类型、游戏类型),电影类型

    render.toneMapping = THREE.ACESFilmicToneMapping;

    //曝光强度

    render.toneMappingExposure = 0.5;

    //开启物理灯光

    render.physicallyCorrectLights = true;

    //创建轨道控制器,可以拖动,控制的是摄像头

    const controls = new OrbitControls(camera, render.domElement);

    //控制器焦点,需要随着相机的更新而更新

    controls.target.set(-8, 2, 0);

    //设置控制阻尼,让控制器有更真实的效果

    controls.enableDamping = true;

    //将webgl渲染的canvas内容添加到body上

    document.getElementById("three_div").appendChild(render.domElement);

    //渲染下一帧的时候就会调用回调函数

    let renderFun = () => {

      //更新阻尼数据

      controls.update();

      //需要重新绘制canvas画布

      render.render(scene, camera);

      //监听屏幕刷新(60HZ,120HZ),每次刷新触发一次requestAnimationFrame回调函数

      //但是requestAnimationFrame的回调函数注册生命只有一次,因此需要循环注册,才能达到一直调用的效果

      window.requestAnimationFrame(renderFun);

    };

    // window.requestAnimationFrame(renderFun);

    renderFun();

    //添加灯光

    const light = new THREE.DirectionalLight(0xffffff, 1);

    scene.add(light);

    //加载模型

    const sceneLoader = new GLTFLoader().setPath("three/glb/");

    //创建解码器

    const dencoderLoader = new DRACOLoader().setDecoderPath(

      "three/draco/gltf/"

    );

    sceneLoader.setDRACOLoader(dencoderLoader);

    sceneLoader.load("christmas .glb", (loader) => {

      console.log(loader);

      const model = loader.scene;

      model.traverse((child) => {

        //隐藏模型水面

        if (child.name == "Plane") {

          child.visible = false;

        }

        if (child.isMesh) {

          child.castShadow = true;

          child.receiveShadow = true;

        }

      });

      scene.add(model);

    });

    //加入自己的水面

    const waterPlane = new THREE.CircleBufferGeometry(400, 100);

    const textureLoader = new THREE.TextureLoader();

    const waterMesh = new Water(waterPlane, {

      textureWidth: 1024,

      textureHeight: 1024,

      color: 0xeeeeff,

      flowDirection: new THREE.Vector2(1, 1),

      scale: 100,

      flowMap: textureLoader.load("three/textures/water/Water_1_M_Flow.webp"),

      normalMap1: textureLoader.load(

        "three/textures/water/Water_1_M_Normal.webp"

      ),

      normalMap2: textureLoader.load(

        "three/textures/water/Water_2_M_Normal.webp"

      ),

    });

    waterMesh.rotation.x = -Math.PI / 2;

    waterMesh.position.y = -0.7;

    scene.add(waterMesh);

    //添加场景背景

    const rgbeLoader = new RGBELoader();

    rgbeLoader.loadAsync("three/christmas-sky.hdr").then((load) => {

      //将用于等距圆柱投影的环境贴图,也被叫做经纬线映射贴图。

      //等距圆柱投影贴图表示沿着其水平中线360°的视角,以及沿着其垂直轴向180°的视角。

      //贴图顶部和底部的边缘分别对应于它所映射的球体的北极和南极。

      load.mapping = THREE.EquirectangularReflectionMapping;

      scene.background = load;

      scene.environment = load;

    });

    //屋子创建灯光

    const hotelLight = new THREE.PointLight(0xffffff, 1);

    hotelLight.position.set(0, 2.4, 0);

    scene.add(hotelLight);

    render.shadowMap.enabled = true;

    //开启灯光动态投影

    hotelLight.castShadow = true;

    //创建灯光组

    const lightGroup = new THREE.Group();

    lightGroup.position.set(-8, 2.5, -1.5);

    for (let index = 0; index < 3; index++) {

      const geometry = new THREE.SphereBufferGeometry(0.2, 50, 5);

      const material = new THREE.MeshBasicMaterial({

        color: 0xffffff,

        emissive: 0xffffff,

        emissiveintensity: 10,

      });

      const mesh = new THREE.Mesh(geometry, material);

      const light = new THREE.PointLight(0xffff, 1);

      mesh.add(light);

      mesh.position.set(

        Math.cos(Math.random()) * 3,

        Math.cos(Math.random()) * 1,

        Math.sin(Math.random()) * 3

      );

      lightGroup.add(mesh);

    }

    console.log("组", lightGroup);

    scene.add(lightGroup);

    let options = {

      angle: 0,

    };

    gsap.to(options, {

      angle: Math.PI * 2,

      duration: 10,

      repeat: -1, //无线循环

      ease: "linear",

      onUpdate: () => {

        lightGroup.children.forEach((elem, index) => {

          elem.position.set(

            Math.cos(options.angle + (Math.PI / 2) * index) * 3,

            Math.cos(3 * options.angle) * 1,

            Math.sin(options.angle + (Math.PI / 2) * index) * 3

          );

        });

      },

    });

    // 使用补间动画移动相机

    let timeLine1 = gsap.timeline();

    let timeline2 = gsap.timeline();

    // 定义相机移动函数

    function translateCamera(position, target) {

      timeLine1.to(camera.position, {

        x: position.x,

        y: position.y,

        z: position.z,

        duration: 1,

        ease: "power2.inOut",

      });

      timeline2.to(controls.target, {

        x: target.x,

        y: target.y,

        z: target.z,

        duration: 1,

        ease: "power2.inOut",

      });

    }

    // 实例化创建漫天星星

    let starsInstance = new THREE.InstancedMesh(

      new THREE.SphereGeometry(0.1, 32, 32),

      new THREE.MeshStandardMaterial({

        color: 0xffffff,

        emissive: 0xffffff,

        emissiveIntensity: 10,

      }),

      100

    );

    // 星星随机到天上

    let starsArr = [];

    let endArr = [];

    for (let i = 0; i < 100; i++) {

      let x = Math.random() * 100 - 50;

      let y = Math.random() * 100 - 50;

      let z = Math.random() * 100 - 50;

      starsArr.push(new THREE.Vector3(x, y, z));

      let matrix = new THREE.Matrix4();

      matrix.setPosition(x, y, z);

      starsInstance.setMatrixAt(i, matrix);

    }

    scene.add(starsInstance);

    // 创建爱心路径

    let heartShape = new THREE.Shape();

    heartShape.moveTo(25, 25);

    heartShape.bezierCurveTo(25, 25, 20, 0, 0, 0);

    heartShape.bezierCurveTo(-30, 0, -30, 35, -30, 35);

    heartShape.bezierCurveTo(-30, 55, -10, 77, 25, 95);

    heartShape.bezierCurveTo(60, 77, 80, 55, 80, 35);

    heartShape.bezierCurveTo(80, 35, 80, 0, 50, 0);

    heartShape.bezierCurveTo(35, 0, 25, 25, 25, 25);

    // 根据爱心路径获取点

    let center = new THREE.Vector3(0, 2, 10);

    for (let i = 0; i < 100; i++) {

      let point = heartShape.getPoint(i / 100);

      endArr.push(

        new THREE.Vector3(

          point.x * 0.1 + center.x,

          point.y * 0.1 + center.y,

          center.z

        )

      );

    }

    // 创建爱心动画

    function makeHeart() {

      let params = {

        time: 0,

      };

      gsap.to(params, {

        time: 1,

        duration: 1,

        onUpdate: () => {

          for (let i = 0; i < 100; i++) {

            let x = starsArr[i].x + (endArr[i].x - starsArr[i].x) * params.time;

            let y = starsArr[i].y + (endArr[i].y - starsArr[i].y) * params.time;

            let z = starsArr[i].z + (endArr[i].z - starsArr[i].z) * params.time;

            let matrix = new THREE.Matrix4();

            matrix.setPosition(x, y, z);

            starsInstance.setMatrixAt(i, matrix);

          }

          starsInstance.instanceMatrix.needsUpdate = true;

        },

      });

    }

    function restoreHeart() {

      let params = {

        time: 0,

      };

      gsap.to(params, {

        time: 1,

        duration: 1,

        onUpdate: () => {

          for (let i = 0; i < 100; i++) {

            let x = endArr[i].x + (starsArr[i].x - endArr[i].x) * params.time;

            let y = endArr[i].y + (starsArr[i].y - endArr[i].y) * params.time;

            let z = endArr[i].z + (starsArr[i].z - endArr[i].z) * params.time;

            let matrix = new THREE.Matrix4();

            matrix.setPosition(x, y, z);

            starsInstance.setMatrixAt(i, matrix);

          }

          starsInstance.instanceMatrix.needsUpdate = true;

        },

      });

    }

    let scenes = [

      {

        text: "圣诞快乐",

        callback: () => {

          // 执行函数切换位置

          translateCamera(

            new THREE.Vector3(-3.23, 3, 4.06),

            new THREE.Vector3(-8, 2, 0)

          );

         

        },

      },

      {

        text: "感谢在这么大的世界里遇见了你",

        callback: () => {

          // 执行函数切

          translateCamera(

            new THREE.Vector3(7, 0, 23),

            new THREE.Vector3(0, 0, 0)

          );

        },

      },

      {

        text: "愿与你探寻世界的每一个角落",

        callback: () => {

          // 执行函数切

          translateCamera(

            new THREE.Vector3(10, 3, 0),

            new THREE.Vector3(5, 2, 0)

          );

        },

      },

      {

        text: "愿将天上的星星送给你",

        callback: () => {

          // 执行函数切

          translateCamera(

            new THREE.Vector3(7, 0, 23),

            new THREE.Vector3(0, 0, 0)

          );

          makeHeart();

        },

      },

      {

        text: "愿疫情结束,大家健康快乐!",

        callback: () => {

          // 执行函数切

          translateCamera(

            new THREE.Vector3(-20, 1.3, 6.6),

            new THREE.Vector3(5, 2, 0)

          );

        },

      },

    ];

    let index = 0;

    let isAnimate = false;

    //创建滑轮滚动切换摄像头位置

    window,

      addEventListener("wheel", (e) => {

        if (isAnimate) return;

        isAnimate = true;

        if (e.deltaY > 0) {

          index++;

          index %= scenes.length;

          if(index == 0){

            restoreHeart();

          }

          console.log(index);

        }

        scenes[index].callback();

        setTimeout(() => {

          isAnimate = false;

        }, 1000);

      });

    //画布全屏

    window.addEventListener("dblclick", () => {

      if (document.fullscreenElement) {

        document.exitFullscreen();

      } else {

        //document.documentElement.requestFullscreen();

        render.domElement.requestFullscreen();

      }

    });

    //监听画面变化,更新渲染画面,(自适应的大小)

    window.addEventListener("resize", () => {

      //更新摄像机的宽高比

      camera.aspect = window.innerWidth / window.innerHeight;

      //更新摄像机的投影矩阵

      camera.updateProjectionMatrix();

      //更新渲染器宽度和高度

      render.setSize(window.innerWidth, window.innerHeight);

      //设置渲染器的像素比

      render.setPixelRatio(window.devicePixelRatio);

    });

  },

  methods: {},

};

* {

  margin: 0;

  padding: 0;

}

.home-content {

  position: fixed;

  top: 0;

  right: 20px;

}

.select-item-color {

  width: 50px;

  height: 50px;

  border: 1px solid #ccc;

  margin: 10px;

  display: inline-block;

  cursor: pointer;

  border-radius: 10px;

}

.select {

  display: flex;

}



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3