threejs加载人体模型,添加菲尼尔效果,并添加点击事件

threejs加载人体模型,添加菲尼尔效果,并添加点击事件

例图

在这里插入图片描述

注意事项

3D文件请放在public文件下,或者使用网络地址

代码

<template>
  <div>
    <div id="colorBar" style="display: none" ref="colorBar">
      {{ activeFloor }}
    </div>
    <div id="body" ref="body" style="position: relative"></div>
    <div id="showWindow" v-if="showWindow.name">
      <p>当前选中信息:</p>
      <p>类型:{{ showWindow.type }}</p>
      <p>名称:{{ showWindow.name }}</p>
      <p
        v-if="showWindow.type == 'Floor' && !showWindow.isShow"
        style="text-align: center"
      >
        <button @click="showFloor">查看</button>
      </p>
      <button v-if="showWindow.isShow" @click="showAll">返回</button>
    </div>
  </div>
</template>

<script>
// 方式 1: 导入整个 three.js核心库
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";

import { OrbitControls } from "three/addons/controls/OrbitControls.js";

export default {
  name: "HelloWorld",
  props: {
    names: { type: Array, default: () => ["肾", "足部", "大脑"] },
    sex: { type: Number, default: () => 1 }, //0 女  1 男
  },
  watch: {
    sex() {
      alert(1);
      this.loaderModel();
    },
  },
  data() {
    return {
      scene: null,
      camera: null,
      light: null,
      model1: null, //模型
      clock: null, //时钟
      loader: null, //加载器
      controls: null, //控制器
      // 渲染器
      renderer: null,
      // 动画数量
      animateLength: 0,
      // 相机轨迹动画
      nowCameraAnimation: null,
      cameraMixer: null,
      cameraAnimations: [],
      // 模型动画
      model1Animations: null,
      mixer: null,
      nowanimation: [], //当前动画
      floorAnimation: [], //楼层动画
      uuid: null, //当前选中楼层
      activeFloor: null,
      activeType: "",
      lastTime: 0,
      currentFrameRate: 0,
      //是否正在旋转
      isXuanzhuan: false,

      otherColor: [], //保存楼层历史颜色
      raycaster: null, //点击位置
      Vector2: null, //(二维向量)的类

      // 右侧窗口信息
      showWindow: {
        type: "",
        name: "",
        isShow: false,
      },
    };
  },
  filters: {
    formatName(name) {
      return name.split("_")[2];
    },
  },
  mounted() {
    //  相机
    this.setCamera();

    // 场景
    this.scene = new THREE.Scene();
    this.setScene();

    // 灯光
    this.setLoght();

    // // 渲染器
    this.renderer = new THREE.WebGLRenderer({
      alpha: true, //遣染器活明
      antialias: true, //抗据货
      precision: "highp",
    });
    this.renderer.shadowMap.enabled = true;
    // 设置渲染器尺寸
    this.renderer.setSize(
      this.$refs.body.offsetWidth,
      this.$refs.body.offsetHeight
    );
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    this.$refs.body.appendChild(this.renderer.domElement);

    // 添加点击事件
    this.addJH();

    // 设置控制器
    this.setControls();

    this.loader = new GLTFLoader();
    // 加载模型
    this.loaderModel(1);

    // this.setLine(1, 0, 0, "#f00");
    // this.setLine(0, 1, 0, "green");
    // this.setLine(0, 0, 1, "#000");

    this.clock = new THREE.Clock();
    this.raycaster = new THREE.Raycaster();
    this.Vector2 = new THREE.Vector2();
    // 添加一个地板
    // this.addBackground();
    this.animate();
  },
  methods: {
    loaderModel(scale = 1) {
      let url = "";
      if (this.sex === 0) {
        url = "/Female_1.2.gltf";
      } else {
        url = "/Male_1.2.gltf";
      }
      this.loader.load(
        url,
        (gltf) => {
          console.log(gltf);
          gltf.scene.traverse((object) => {
            console.log(object.name);
            if (
              object.name != "血管" &&
              object.name != "血管2" &&
              object.name != "静脉" &&
              object.name != "动脉" &&
              object.name
            ) {
              console.log(object.name, this.names.includes(object.name));
              if (this.names.includes(object.name)) {
                var material = new THREE.MeshBasicMaterial({
                  color: "#f00",
                  transparent: true,
                  opacity: 0.5,
                });
                object.material = material;
                object.isIll = true;
              } else {
                // 创建一个透明的材质
                var customMaterial = new THREE.ShaderMaterial({
                  uniforms: {
                    // 在这里定义uniform变量,用于控制透明度、边缘和反光效果
                    edgeColor: { value: new THREE.Vector3(1.0, 1.0, 1.0) }, // 边缘颜色
                    edgeThickness: { value: 0.05 }, // 边缘厚度
                    reflectionStrength: { value: 0.9 }, // 反光强度
                  },
                  vertexShader: `
                      varying vec3 vNormal;

                      void main() {
                          vNormal = normal;
                          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
                      }
                  `,
                  fragmentShader: `
                      varying vec3 vNormal;
                      uniform vec3 edgeColor;
                      uniform float edgeThickness;
                      uniform float reflectionStrength;

                      void main() {
                          // 计算透明度
                          float alpha = 0.5; // 透明度的基础值

                          // 计算边缘效果
                          float edge = fwidth(length(vNormal));
                          alpha *= smoothstep(0.0, edgeThickness, edge);

                          // 计算反光效果
                          vec3 reflection = reflect(normalize(-vNormal), vec3(0.455, 0.749, 0.890));
                          float reflectionIntensity = max(dot(reflection, normalize(-vNormal)), 0.0);
                          vec3 reflectionColor = reflectionStrength * reflectionIntensity * edgeColor;

                          // 最终颜色设置为"#74bfe3"
                          vec3 finalColor = vec3(0.455, 0.749, 0.890); // "#74bfe3"的RGB值
                          finalColor += reflectionColor; // 添加反光效果

                          // 设置透明度
                          gl_FragColor = vec4(finalColor, alpha);
                      }
                  `,
                  transparent: true,
                });
                object.material = customMaterial;
                object.isIll = false;
              }
            }
            if (object.name == "骨架") {
              object.visible = false;
            }
          });
          this.model1 = gltf.scene;

          this.scene.add(this.model1);
          this.model1.scale.set(scale, scale, scale);
          this.mixer = new THREE.AnimationMixer(this.model1);

          this.model1.position.set(0, 0, 0);
          this.model1Animations = gltf.animations;
          // 获取相机的动画剪辑数组
          this.cameraAnimations = gltf.animations;
          // 获取到楼层的动画
          this.floorAnimation = gltf.animations
            .filter((item) => item)
            .sort((a, b) => a.name.split("_")[1] - b.name.split("_")[1])
            .reverse();
        },
        undefined,
        function (e) {
          console.error(e);
        }
      );
    },
    setCamera() {
      // PerspectiveCamera 相机
      // fov — 摄像机视锥体垂直视野角度
      // aspect — 摄像机视锥体长宽比
      // near — 摄像机视锥体近端面
      // far — 摄像机视锥体远端面
      // Vector3 三维向量(标记为x、y和z)
      // this.camera.lookAt(new THREE.Vector3(0, 1, 0));
      // this.cameraMixer = new THREE.AnimationMixer(this.camera);
      this.camera = new THREE.PerspectiveCamera(
        75,
        this.$refs.body.offsetWidth / this.$refs.body.offsetHeight,
        0.1,
        1000
      );
      this.camera.position.set(0, 0, 2);
    },
    setScene() {
      this.scene.background = new THREE.Color("#000");
      // 一个fog实例定义了影响场景中的每个物体的雾的类型。默认值为null。
      // this.scene.fog = new THREE.Fog("#000", 20, 1000);
      // this.scene.position.x -= 2;
      this.scene.position.y -= 1;
    },
    setControls(type = false) {
      if (type) {
        this.controls.dispose();
        return;
      }
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    },
    setLoght() {
      // 从上方照射的白色平行光,强度为 0.5。
      // 创建平行光源
      this.light = new THREE.DirectionalLight(0xffffff, 2);
      this.light.position.set(-10, 10, 20).normalize();
      // this.light.castShadow = true; // default false
      this.scene.add(this.light);

      const light = new THREE.AmbientLight(0x404040, 0.5); // 柔和的白光
      this.scene.add(light);
    },
    animate() {
      // currentTime
      // // 更新动画时间
      // var delta = this.clock.getDelta();

      // const deltaTime = currentTime - this.lastTime; // 计算距离上一帧的时间间隔,转换为秒
      // this.lastTime = currentTime; // 更新上一帧的时间戳
      // const currentFrameRate = 1 / deltaTime;

      // if (this.mixer) {
      //   this.nowanimation.forEach((item) => {
      //     // 检查动画是否已经结束
      //     let sumTime = item.getClip().duration;
      //     if (!item.paused && item.time >= sumTime - currentFrameRate) {
      //       // 暂停动画
      //       item.paused = true;
      //       item.time = sumTime;
      //       this.animateLength--;
      //     }
      //   });
      //   this.mixer.update(delta);
      // }
      // if (this.cameraMixer && this.nowCameraAnimation) {
      //   this.cameraMixer.update(delta);
      // }
      this.controls.update();
      // this.CSS2DRenderer.render(this.scene, this.camera);
      this.renderer.render(this.scene, this.camera);
      requestAnimationFrame(this.animate);
    },
    setLine(x, y, z, color) {
      const dir = new THREE.Vector3(x, y, z);
      //normalize the direction vector (convert to vector of length 1)
      dir.normalize();
      const origin = new THREE.Vector3(0, 0, 0);
      const length = 10;
      const arrowHelper = new THREE.ArrowHelper(dir, origin, length, color);
      this.scene.add(arrowHelper);
    },
    addJH() {
      this.renderer.domElement.addEventListener(
        "mousedown",
        this.onMouseDown,
        false
      );
      // this.renderer.domElement.addEventListener(
      //   "mousemove",
      //   this.onMouseMove,
      //   false
      // );
      // this.renderer.domElement.addEventListener(
      //   "mouseup",
      //   this.onMouseUp,
      //   false
      // );
    },
    onMouseDown(event) {
      // 在这里处理鼠标点击事件的逻辑
      this.Vector2.set(
        (event.clientX / this.$refs.body.clientWidth) * 2 - 1,
        -(event.clientY / this.$refs.body.clientHeight) * 2 + 1
      );
      // 射线投射
      this.raycaster.setFromCamera(this.Vector2, this.camera);
      // 获取与射线相交的对象列表
      const intersects = this.raycaster.intersectObjects(this.scene.children);
      // 遍历相交对象列表
      for (let i = 0; i < intersects.length; i++) {
        const intersect = intersects[i];
        if (intersect.object.isIll) {
          const name = intersect.object.name;
          this.$emit("organOnclick", name);
        }
      }
    },
    onMouseMove() {
      // 在这里处理鼠标点击事件的逻辑
      // console.log("点击了 Three.js 元素", event);
    },
    onMouseUp() {
      // 在这里处理鼠标点击事件的逻辑
    },
    // console.log("点击了 Three.js 元素", event);
    changeColor(name, type = "name") {
      this.model1.traverse((child) => {
        if (type == "name") {
          for (let index = 0; index < this.otherColor.length; index++) {
            const element = this.otherColor[index];
            if (element.name == child.name) {
              child.material = element.material;
              this.otherColor.splice(index, 1);
              index--;
            }
          }
          if (name && child.name == name) {
            this.otherColor.push({
              name: name,
              material: child.material.clone(),
            });
            child.material = new THREE.MeshBasicMaterial({
              color: "#f00",
              transparent: true,
              opacity: 0.5,
            });
          }
        }
      });
      this.renderer.render(this.scene, this.camera);
    },
    getRandomColor() {
      var letters = "0123456789ABCDEF";
      var color = "#";
      for (var i = 0; i < 6; i++) {
        color += letters[Math.floor(Math.random() * 16)];
      }
      console.log(color);
      return color;
    },

    addBackground() {
      // // 创建地面的几何体;
      var dgeometry = new THREE.PlaneGeometry(100, 100); // 根据需要指定地面的宽度和长度
      dgeometry.rotateX(-Math.PI / 2);
      // 创建地面的材质
      var dmaterial = new THREE.MeshBasicMaterial({ color: "#333" }); // 根据需要指定地面的颜色或纹理
      // 创建地面的网格对象
      var ground = new THREE.Mesh(dgeometry, dmaterial);
      ground.receiveShadow = true;
      // 将地面网格对象添加到场景中
      this.scene.add(ground);
    },
  },
};
</script>
<style scoped>
#body {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  overflow: hidden;
}
#btns {
  position: fixed;
  right: 5px;
  top: 20px;
  width: 100px;
  display: flex;
  justify-content: center;
  flex-direction: column;
}
#colorBar {
  width: 50px;
  height: 10px;
  font-size: 16px;
  color: #f00;
}
#showWindow {
  position: absolute;
  z-index: 2;
  right: 5px;
  top: 5px;
  background-color: #fff;
  border-radius: 4px;
  padding: 5px;
}
#showWindow p {
  text-align: left;
}
</style>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值