three.js加载模型

        three.js是一款基于原生web GL封装通用Web 3D引擎,在小游戏、产品展示、物联网、数字孪生、智慧城市园区、机械、建筑、全景看房、GIS等各个领域基本上都有three.js的身影。

 一、three.js学习指南官网 

Three.js中文网提供Three.js、WebGL视频课程icon-default.png?t=N7T8http://www.webgl3d.cn/

 二、引入three.js

npm install three@0.157.0 -S

 三、素材资源准备

1)准备模型,在public下新建model文件夹,把模型放到model文件夹下

2)复制three.js 依赖包自带的draco文件夹复制到public下

 draco的位置如下:node_modules->three->examples->jsm->libs->draco

将该文件夹复制到public下

3)准备hdr纹理贴图,如果不需要纹理贴图的可以忽略

四、代码实现

<template>
  <div>
    <!-- 进度条 -->
    <div class="pro-box">
      <el-progress v-if="showProcess" :stroke-width="22" :percentage="loadProcess" />
    </div>
  </div>
</template>

<script setup>
  import {
    ref,
    getCurrentInstance,
    watch
  } from "vue";
  import {
    tag,
    labelRenderer
  } from "@/label.js";

  import * as THREE from "three";
  import {
    OrbitControls
  } from "three/examples/jsm/controls/OrbitControls";
  import {
    GLTFLoader
  } from "three/examples/jsm/loaders/GLTFLoader"; // 加载gltf模型
  import {
    DRACOLoader
  } from "three/examples/jsm/loaders/DRACOLoader"; // 解压gltf模型
  import {
    GUI
  } from "three/examples/jsm/libs/lil-gui.module.min.js";
  import {
    RGBELoader
  } from "three/examples/jsm/loaders/RGBELoader"; // 加载hdr环境光照

  // import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer"; // 后期处理
  // import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass"; // 后期处理
  // import gsap from "gsap";

  // ******************
  const {
    proxy
  } = getCurrentInstance();
  const loadProcess = ref(0);
  const showProcess = ref(true);
  const labelBox = ref(tag());
  const modelObj = ref(null);

  // 进度条
  watch(loadProcess, (value) => {
    if (value == 100) {
      setTimeout(() => {
        showProcess.value = false;
      }, 2000);
    }
  });
  // ******************




  // 初始化场景
  // const model = ref(null);
  const scene = new THREE.Scene();

  // 初始化相机
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  camera.position.set(8, 5, 4);
  camera.lookAt(0, 0, 0); // 相机观察目标
  camera.updateProjectionMatrix(); //更新投影矩阵
  // 初始化渲染器
  const renderer = new THREE.WebGLRenderer({
    // 设置抗锯齿
    antialias: true,
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  // 设置色调映射
  renderer.outputEncoding = THREE.sRGBEncoding;
  renderer.toneMapping = THREE.ACESFilmicToneMapping;
  renderer.toneMappingExposure = 1;
  renderer.shadowMap.enabled = true; //允许阴影

  // AxesHelper: 辅助观察的坐标系
  const axesHelper = new THREE.AxesHelper(10);
  scene.add(axesHelper);

  // 初始化相机轨道控制器
  const controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;

  // 加载环境纹理
  let rebgLoader = new RGBELoader();
  rebgLoader.load("./textues/sky2.hdr", function (texture) {
    // 设置球形纹理映射
    texture.mapping = THREE.EquirectangularReflectionMapping;
    scene.background = texture;
    scene.environment = texture;
  });

  // 初始化加载器draco
  const dracoLoader = new DRACOLoader();
  // 设置draco路径
  dracoLoader.setDecoderPath("./draco/");
  const gltfLoader = new GLTFLoader();
  // 设置gltf加载器draco解码器
  gltfLoader.setDRACOLoader(dracoLoader);

  // 加载模型
  gltfLoader.load("./model/jc.glb", function (gltf) {
    const model = gltf.scene;
    model.traverse((child) => {
      if (child.isMesh) {
        child.material.side = THREE.DoubleSide; // 模型双面渲染
        child.castShadow = true; // 光照是否有阴影
        child.receiveShadow = true; // 是否接收阴影
        child.frustumCulled = false;
      }
    });
    model.position.set(0, -3, 0);
    scene.add(model);

    // gui
    const gui = new GUI();
    gui.add(model.position, "x").name("模型坐标轴x位置");
    gui.add(model.position, "y").name("模型坐标轴y位置");
    gui.add(model.position, "z").name("模型坐标轴z位置");
  }, function (xhr) {
    // 计算加载进度
    const percent = xhr.loaded / xhr.total * 100;
    loadProcess.value = parseInt(percent);
    // console.log('加载进度------', parseInt(loadProcess.value),parseInt(percent));
  });

  // 点光源
  const pointLight = new THREE.PointLight(0xffffff, 10); //光源颜色 光照强度
  pointLight.decay = 0.0; //设置光源不随距离衰减 默认2.0
  pointLight.position.set(5, 10, 0); // 点光源位置
  pointLight.castShadow = true;
  scene.add(pointLight);

  // 环境光设置
  const ambient = new THREE.AmbientLight(0xffffff, 1);
  scene.add(ambient);
  // 添加平行光
  const light = new THREE.DirectionalLight(0xffffff, 1);
  light.position.set(0, 60, 10);
  light.target.position.set(0, 4, -50);
  scene.add(light);

  // 平行光辅助观察
  // const lightHelper = new THREE.DirectionalLightHelper(light, 1000);
  // scene.add(lightHelper);
  // lightHelper.position.set(300, 200, 120);

  function render() {
    requestAnimationFrame(render);
    renderer.render(scene, camera);
    controls.update();
  }

  render();

  document.body.addEventListener("click", function (event) {
    const px = event.offsetX;
    const py = event.offsetY;
    const Sx = event.clientX; //鼠标单击位置横坐标
    const Sy = event.clientY; //鼠标单击位置纵坐标
    //屏幕坐标转WebGL标准设备坐标
    // 这里的window.innerWidth和window.innerHeight其实是容器的宽高,并且默认按照(0,0)来计算的,需要考虑偏移的位置(px,py)
    const x = (Sx / renderer.domElement.clientWidth) * 2 - 1;
    const y = -(Sy / renderer.domElement.clientHeight) * 2 + 1;
    //创建一个射线投射器Raycaster
    const raycaster = new THREE.Raycaster();
    //通过鼠标单击位置标准设备坐标和相机参数计算射线投射器`Raycaster`的射线属性.ray
    raycaster.setFromCamera(new THREE.Vector2(x, y), camera); //参数: 标准化的二维坐标 相机
    //返回.intersectObjects()参数中射线选中的网格模型对象
    // 未选中对象返回空数组[],选中一个数组1个元素,选中两个数组两个元素
    const intersects = raycaster.intersectObjects(scene.children); //参数 计算范围
    console.log(intersects, '----------intersects')
    if (intersects.length > 0) {
      modelObj.value = intersects[0].object;
      // console.log(intersects[0], '--------------intersects[0]')
      labelBox.value.position.set(intersects[0].point.x, intersects[0].point.y, intersects[0].point.z);
      // labelBox.value.element.innerHTML = modelObj.value.name + '哈哈哈哈哈';
      labelBox.value.element.innerHTML = `
      <div>
        <div>
          <div>名称:${modelObj.value.name}</div>
          <div>提示:哈哈哈哈哈</div>
        </div>
      </div>
      `;
      labelBox.value.element.style.visibility = 'visible';
    } else {
      labelBox.value.element.style.visibility = 'hidden'; //没有选中mesh,隐藏标签
    }
    scene.add(labelBox.value);
    labelRenderer.render(scene, camera); // 标签渲染
  });
</script>

<style scoped>
  * {
    margin: 0;
    padding: 0;
  }

  canvas {
    width: 100vw;
    height: 100vh;
    position: fixed;
    left: 0;
    top: 0;
  }

  .pro-box {
    position: absolute;
    left: 50px;
    top: 30px;
    width: 500px;
    height: 10px;
  }
</style>

label.js 

import {
  CSS2DRenderer,
  CSS2DObject,
} from "three/examples/jsm/renderers/CSS2DRenderer.js";
import labelBg from "@/assets/tips-border.png"; // 导入标签背景

// 创建一个HTML标签
function tag() {
  // 创建div元素(作为标签)
  var div = document.createElement("div");
  div.style.visibility = "hidden";
  div.innerHTML = "";
  div.style.width = "200px";
  div.style.height = "100px";
  div.style.padding = "5px 10px";
  div.style.color = "#fff";
  div.style.fontSize = "16px";
  div.style.position = "absolute";
  // div.style.backgroundColor = "rgba(25,25,25,0.5)";
  div.style.background = `rgba(25,25,25,0.5) url(${labelBg})no-repeat center center`;
  div.style.backgroundSize = "100% 100%";
  div.style.borderRadius = "5px";
  //div元素包装为CSS2模型对象CSS2DObject
  var label = new CSS2DObject(div);
  div.style.pointerEvents = "none"; //避免HTML标签遮挡三维场景的鼠标事件
  // 设置HTML元素标签在three.js世界坐标中位置
  // label.position.set(x, y, z);
  return label; //返回CSS2模型标签
}

// 创建一个CSS2渲染器CSS2DRenderer
var labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = "absolute";
// // 避免renderer.domElement影响HTMl标签定位,设置top为0px
labelRenderer.domElement.style.top = "0px";
labelRenderer.domElement.style.left = "0px";
// //设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
labelRenderer.domElement.style.pointerEvents = "none";
document.body.appendChild(labelRenderer.domElement);

export { tag, labelRenderer };

 五、效果展示

这世界很喧嚣,做你自己就好 

 

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值