当前位置:   article > 正文

【three.js沉浸式商城】使用three.js实现模型展示_three.js 商品 展示

three.js 商品 展示

沉浸式商城主打使用模型展示产品信息,并提供不同展示背景。
由此产生的需求是:
1. 3D模型在网页上展示
2. 切换hdr背景,并调节环境光

接下来介绍实现思路

具体实现思路:

计划使用three.js来实现3D展示功能。
Threejs是一款WebGL三维引擎,开发文档three.js

在整个项目中新建Base3d.js
在这里插入图片描述
然后新建Scene.vue文件,引用3d库,并设置挂载响应式对象

<template>
  <div class="scene" id="scene"></div>
</template>

<script setup>
import Base3d from "../utils/Base3d.js";
import { reactive, onMounted } from "vue";
const data = reactive({
  base3d: {},
});

onMounted(() => {
  data.base3d = new Base3d("#scene");
});
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

导入需要的库

// export default Base3d;
import * as THREE from "three";
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader";
// 导入控制器,轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 导入模型解析器
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

接着新建一个构造器存放需要的变量,传入selector选择器和一个onFinish函数。
选择器的作用是定位到three.js要渲染的页面,在本项目中传入的是#product

  constructor(selector, onFinish) {
    this.container = document.querySelector(selector);
    this.camera;
    this.scene;
    this.renderer;
    this.model;
    this.panzi;
    this.animateAction;
    this.clock = new THREE.Clock();
    this.onFinish = onFinish;
    this.init();
    this.animate();
    this.progressFn;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

创建好构造器后,进行基本设置初始化。首先是对场景进行初始化,然后初始化相机,因为相机位置的变化才能记录物体的移动

  init() {
    //   初始化场景
    this.initScene();
    // 初始化相机
    this.initCamera();

    // 初始化渲染器
    this.initRenderer();
    // 控制器
    // this.initControls();
    // 添加物体
    this.addMesh();

  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

场景初始化中新建一个场景

  initScene() {
    this.scene = new THREE.Scene();

  }
  • 1
  • 2
  • 3
  • 4

初始化透视摄像机

  initCamera() {
    this.camera = new THREE.PerspectiveCamera(
      45,
      window.innerWidth / window.innerHeight,
      0.25,
      200
    );
    this.camera.position.set(-1.8, 0.6, 2.7);
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

初始化渲染函数,渲染的尺寸大小设置为窗口大小
因为背景图片的hdr中带有不同的光照信息,我们需要在渲染器中设置色调映射
做好渲染设置后,使用appendChild添加所有渲染的元素

  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); //
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

初始化轨道控制器,实现在场景中全景旋转场景的效果

  initControls() {
    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
  }
  • 1
  • 2
  • 3

设置RGBELoader来加载场景hdr纹理,把纹理值赋给场景背景和环境,当点击选择切换场景时,会将场景名称传到setEnvMap中,对应加载hdr

  setEnvMap(hdr) {
    new RGBELoader().setPath("./files/hdr/").load(hdr + ".hdr", (texture) => {
      texture.mapping = THREE.EquirectangularReflectionMapping;
      this.scene.background = texture;
      this.scene.environment = texture;
    });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

到了这个阶段,我们已经有了一个设置好的渲染器,但是还没有进行渲染。
接下来在render()中进行渲染。由于网页页面每帧都在刷新,所以需要设置一个动画循环不断地绘制帧画面,保证渲染流畅。

  render() {
    this.renderer.render(this.scene, this.camera);
  }
  animate() {
    this.renderer.setAnimationLoop(this.render.bind(this));
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

场景初始化了,渲染器也设置了,场景选择也写好了,终于迎来了最基础也是最重要的一步——设置模型
因为模型一般比较大,加载模型需要时间,所以可以设置一个进度来观察加载程度,使用异步函数进行封装。
同时,为了更好地展示产品效果,选择采用播放动画的方式来替代自主操作,这样更能全方位展示产品最好的角度。

  setModel(modelName){
    return new Promise((resolve,reject) => {
      const loader = new GLTFLoader().setPath("files/gltf/");
      loader.load(modelName,(gltf)=>{
        this.model&&this.model.removeFromParent();
        
        this.model = gltf.scene.children[0]; //子元素的第一个就是模型
        if('bag2.glb'==modelName&&!this.panzi){
          // this.panzi = gltf.scene.children[5];
          
          // this.scene.add(this.panzi)
          this.scene.add(gltf.scene);
          // 修改摄像头为模型摄像头
          this.camera = gltf.cameras[0]
          // 调用动画
          this.mixer = new THREE.AnimationMixer(gltf.scene.children[1]); //动画混合器,播放动画
          this.animateAction = this.mixer.clipAction(gltf.animations[0]); //调度加载动画
          // 设置动画播放时长
          this.animateAction.setDuration(20).setLoop(THREE.LoopOnce); //设置播放20s,只播放一次
          // 设置播放完动画后停止
          this.animateAction.clampWhenFinished = true;



          // 设置灯光
          this.spotlight1 = gltf.scene.children[2].children[0];
          this.spotlight1.intensity = 1;
          this.spotlight2 = gltf.scene.children[3].children[0];
          this.spotlight2.intensity = 1;
          this.spotlight3 = gltf.scene.children[4].children[0];
          this.spotlight3.intensity = 1;

          
        }
        this.scene.add(this.model);
        resolve(this.modelName+"模型添加成功");

      },(e)=>{
        // 模型加载进度
        this.onProgress(e);
      });
      
    });


  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

如果一味播放动画,会使得网页交互效果单调。为了增加网页趣味性,通过监听鼠标滚轮事件来对播放的动画进行交互。滚轮的滚动速度决定了动画的播放速度,滚轮暂停,动画也暂停。向前滚轮,动画向前播放,向后滚轮,动画向后播放。

这里的滚动使用到了防抖。
防抖,防抖就是当触发一个事件不会立即执行,会等待 n 秒后再执行该事件,如果在等待 n 秒期间你再次出发,则会重新计时,也就是说防抖不管你触发多少次这个事件,永远只有一次在执行,并且执行的是最后一次
什么意思,针对该项目具体来说,就是我需要滑动一次滚轮的时候,动画播放一段,然后暂停。如果持续滑动滚轮,那动画就持续播放不会暂停。
用防抖的意思来说,就是触发一次滚轮事件的时候,等待n秒(这n秒动画播放),然后执行暂停事件。

  onMouseWheel(e) {
    // console.log(this.animateAction);
    // 设置播放速度,先监听滚轮方向
    let timeScale = e.deltaY > 0 ? 1 : -1; //获取滚轮方向
    this.animateAction.setEffectiveTimeScale(timeScale);
    this.animateAction.paused = false; //去除暂停状态
    // 播放动画
    this.animateAction.play();

    
    if(this.timeoutid){
      clearTimeout(this.timeoutid);
    }
    this.timeoutid = setTimeout(()=>{
      this.animateAction.halt(0.5);
    },300)
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

动画章节完结~~~

商城

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/95830
推荐阅读
相关标签
  

闽ICP备14008679号