three.js 叉乘判断物体在人前左,前右,后左、后右

时间:2024-03-07 17:47:27

效果:

代码:

<template>
  <div>
    <el-container>
      <el-main>
        <div class="box-card-left">
          <div id="threejs"></div>
          <div style="padding: 10px;text-align: left;">
            叉乘判断物体在人左前、右前还是左后,右后方向
            <div style="margin: 10px;">
              <el-button @click="judge">开始判断</el-button>
              <div v-for="(item,index) in this.positon_arr" :key="index" style="line-height: 28px;">{{ item }}</div>
            </div>
          </div>
        </div>
      </el-main>
    </el-container>
  </div>
</template>
<script>
// 引入轨道控制器扩展库OrbitControls.js
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import {
  CSS3DSprite,
  CSS3DRenderer,
} from "three/examples/jsm/renderers/CSS3DRenderer.js";
export default {
  data() {
    return {
      scene: null, // 场景对象
      camera: null, // 相机对象
      group: null, // 组对象
      person: null, // 人对象
      renderer: null, // 渲染器对象
      css3DRenderer: null, // 渲染器对象
      a: new this.$three.Vector3(0, 0, 1),
      // b: new this.$three.Vector3(30, 0, 30),
      meshPosition:[],
      positon_arr:[],

    };
  },
  created() {},
  mounted() {
    this.name = this.$route.query.name;
    this.init();
  },
  methods: {
    goBack() {
      this.$router.go(-1);
    },
    /**
     * 如何判断物体是在人的前方还是后方
     * 思路:借助两个单位向量的点乘结果来判断的;
     */
    init() {
      // 创建场景对象
      this.scene = new this.$three.Scene();
      // 创建辅助坐标轴对象
      const axesHelper = new this.$three.AxesHelper(10);
      this.scene.add(axesHelper);
      // 创建环境光对象
      const ambientLight = new this.$three.AmbientLight(0xffffff, 10);
      this.scene.add(ambientLight);
      // 创建相机对象
      this.camera = new this.$three.PerspectiveCamera(60,1,0.01,2000);
      this.camera.position.set(-4,5,-5);
      this.camera.lookAt(0,0,0);

      this.css3DRenderer = new CSS3DRenderer();
      this.css3DRenderer.setSize(1000,800);
      this.css3DRenderer.render(this.scene, this.camera);
      this.css3DRenderer.domElement.style.position = 'absolute';
      this.css3DRenderer.domElement.style.top = 0;
      this.css3DRenderer.domElement.style.pointerEvents = 'none';

      this.css3DRenderer.render(this.scene, this.camera);
      window.document.getElementById("threejs").appendChild(this.css3DRenderer.domElement);

      this.createMesh(0xffaadd, new this.$three.Vector3(3,0,3), '球1');
      this.createMesh(0xddcc11, new this.$three.Vector3(-3,0,3), '球2');
      this.createMesh(0x1199dd, new this.$three.Vector3(3,0,-3), '球3');
      this.createMesh(0xbbaadd, new this.$three.Vector3(-3,0,-3), '球4');
      
      this.createArrow(this.a);

      // 创建渲染器对象
      this.renderer = new this.$three.WebGLRenderer();
      this.renderer.setSize(1000,800);
      // 创建gltfLoader加载器对象
      const gltfLoader = new GLTFLoader();
      gltfLoader.load("/models/gltf/person2/scene.gltf", gltf => {
        console.log(gltf);
        gltf.scene.children[0].scale.set(2,2,2);
        this.scene.add(gltf.scene);
        this.renderer.render(this.scene, this.camera);
        window.document.getElementById("threejs").appendChild(this.renderer.domElement);
        const controls = new OrbitControls(this.camera, this.renderer.domElement);
        controls.addEventListener("change", () => {
          this.renderer.render(this.scene, this.camera);
        })
        this.renderFun();
      })
    },
    // 创建箭头用于显示人前方的单位向量 this.a
    createArrow(dir, l=2, color=0xffffff) {
      let arrow = new this.$three.ArrowHelper(dir, new this.$three.Vector3(0,0,0), l, color);
      this.scene.add(arrow);
    },
    // 创建模型的方法
    createMesh(color, position, name) {
      // 创建球缓冲几何体
      const geometry = new this.$three.SphereGeometry(1,32,16);
      // 创建材质对象
      const material = new this.$three.MeshBasicMaterial({color: color});
      const mesh = new this.$three.Mesh(geometry, material);
      mesh.name = name;
      mesh.position.set(position.x, position.y, position.z);
      this.meshPosition.push({name: name, position: mesh.position});
      let dom = this.createDom(name);
      mesh.add(dom);
      this.scene.add(mesh);
      this.createArrow(mesh.position, mesh.position.length()*0.7,color);
    },
    createDom(name) {
      let dom = document.createElement("div");
      dom.innerText = name;
      let css3DObject = new CSS3DSprite(dom);
      css3DObject.scale.set(0.03,0.03,0.03);
      return css3DObject;
    },
    renderFun() {
      this.css3DRenderer.render(this.scene, this.camera);
      requestAnimationFrame(this.renderFun); // 一定要设置这一句,否则,不渲染
    },
    /**
     * 人前方的单位向量a 与 物体到原点的向量m 叉乘后,
     * 可以通过叉乘结果的y值判断物体是在人左侧还是右侧
     * 
     */
    judge() {
      if(this.meshPosition) {
        this.positon_arr = [];
        this.meshPosition.forEach(item => {
          let c = this.a.clone().cross(item.position);
          let p_str = "";
          if(c.y > 0) {
            p_str += item.name +"在人左";
            p_str += this.dotDeg(item.position);
          } else {
            p_str += item.name +"在人右";
            p_str += this.dotDeg(item.position);
          }
          this.positon_arr.push(p_str);
        })
      }
    },
    // 点乘判断两个向量的夹角
    dotDeg(meshPosition) {
      let c = this.a.clone().dot(meshPosition.clone().normalize());
      let cos = Math.acos(c);
      let deg = this.$three.MathUtils.radToDeg(cos);
      console.log(deg);
      let pos = "";
      if(deg > 0 && deg < 90) {
        pos = "前";
      } else if (deg == 90) {
        pos = "平行";
      } else {
        pos = "后";
      }
      return pos;
    }
  },
};
</script>
<style lang="less" scoped>
.box-card-left {
  display: flex;
  align-items: flex-start;
  flex-direction: row;

  width: 100%;

  .box-right {
    img {
      width: 500px;
      user-select: none;
    }
  }
}
</style>