three.js 点乘判断是否在扇形内

效果:

点乘判断是否在扇形内

代码:

<template>
  <div>
    <el-container>
      <el-main>
        <div class="box-card-left">
          <div id="threejs"></div>
          <div>
            <el-button @click="count" type="primary" plain>计算</el-button>:{{
              res_text
            }}
          </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 { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
// 渲染通道
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
// 发光描边OutlinePass
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
import {
  CSS2DObject,
  CSS2DRenderer,
} from "three/examples/jsm/renderers/CSS2DRenderer.js";
import TWEEN from "@tweenjs/tween.js";
import {
  CSS3DObject,
  CSS3DSprite,
  CSS3DRenderer,
} from "three/examples/jsm/renderers/CSS3DRenderer.js";
// TextGeometry 是一个附加组件,必须显式导入。 three/examples/jsm/geometries
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
// FontLoader 是一个附加组件,必须显式导入。
import { FontLoader } from "three/examples/jsm/loaders/FontLoader.js";
export default {
  data() {
    return {
      scene: null, // 场景对象
      camera: null, // 相机对象
      group: null, // 组对象
      person: null, // 人对象
      res_text: "",
      deg1: "",
      deg2: "",
    };
  },
  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(100);
      this.scene.add(axesHelper);
      this.createMesh();
      // 创建箭头对象
      const arrowHelper1 = new this.$three.ArrowHelper(
        new this.$three.Vector3(8, 0, 5).normalize(),
        new this.$three.Vector3(0, 0, 0),
        5,
        0xffffff
      );

      this.scene.add(arrowHelper1);
      const arrowHelper2 = new this.$three.ArrowHelper(
        new this.$three.Vector3(-1, 0, 5).normalize(),
        new this.$three.Vector3(0, 0, 0),
        5,
        0xffffff
      );

      this.scene.add(arrowHelper2);

      // 创建环境光对象
      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(-1, 8, -1);
      this.camera.lookAt(0, 0, 0);
      // 创建组对象
      this.group = new this.$three.Group();
      // 创建渲染器对象
      this.renderer = new this.$three.WebGLRenderer();
      this.renderer.setSize(1000, 800);
      // 创建GLTFLoader对象
      const gltfLoader = new GLTFLoader();
      gltfLoader.load("/models/gltf/person2/scene.gltf", (gltf) => {
        this.person = gltf.scene;
        this.group.add(gltf.scene);
        this.scene.add(this.group);
        
        this.drawArc();
        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);
        });
      });
    },
    createMesh() {
      const geometry = new this.$three.BoxGeometry(2, 2, 2);
      const material = new this.$three.MeshBasicMaterial({ color: 0xffaddd });
      this.mesh = new this.$three.Mesh(geometry, material);
      this.mesh.position.set(-3, 0, 5);
      this.scene.add(this.mesh);
    },
    count() {
      // 从人指向物体的向量
      let b = this.mesh.position.clone().sub(this.person.position.clone());
      // 先计算距离是否大于圆弧半径 5
      // 这里距离去整数了
      if (Math.trunc(b.length()) - 1 > 5) {
        // alert();
        this.res_text = "物体不在扇形内--1";
      } else {
        this.createArc();
      }
    },
    // 生成圆弧
    createArc() {
      // 计算两个箭头之间的角度 -- start
      let a = new this.$three.Vector3(8, 0, 5).normalize(); // 计算后的单位向量
      let b = new this.$three.Vector3(-1, 0, 5).normalize(); // 计算后的单位向量
      let a_dot_b = a.dot(b); // 两个单位向量点乘得到 他们夹角的余弦值
      let cos = Math.acos(a_dot_b); // 反余弦计算得到弧度
      let deg = this.$three.MathUtils.radToDeg(cos); // 弧度转角度
      // 计算两个箭头之间的角度 -- end

      // 计算箭头1 和人正前方的单位向量的夹角
      let deg_1 = this.countDeg(8, 0, 5);
      // 计算箭头2 和人正前方的单位向量的夹角
      let deg_2 = this.countDeg(-1, 0, 5);
      // 判断人正前方的单位向量和两个箭头的夹角和(deg_1 + deg_2)   与 两个箭头的夹角(deg)的大小比较;
      // if(deg_1 + deg_2 > deg) 说明人正前方不在两个箭头中间;反之在中间

      if (deg_1 + deg_2 > deg) {
        this.res_text = "物体不在扇形内--3";
      } else {
        this.res_text = "物体在扇形内--2";
      }
    },
    countDeg(x, y, z, flag = true) {
      let a = new this.$three.Vector3(x, y, z).normalize(); // 计算后的单位向量
      if (flag) {
        let b = this.person.position.clone().normalize(); // 计算后的单位向量
        let a_dot_b = a.dot(b); // 两个单位向量点乘得到 他们夹角的余弦值
        let cos = Math.acos(a_dot_b); // 反余弦计算得到弧度
        let deg = this.$three.MathUtils.radToDeg(cos); // 弧度转角度
        return deg;
      } else {
        let b = new this.$three.Vector3(1, 0, 0); // 计算后的单位向量
        let a_dot_b = a.dot(b); // 两个单位向量点乘得到 他们夹角的余弦值
        let cos = Math.acos(a_dot_b); // 反余弦计算得到弧度
        let deg = this.$three.MathUtils.radToDeg(cos); // 弧度转角度
        return cos;
      }
    },
    drawArc() {
      // 计算箭头1 和人正前方的单位向量的夹角
      let deg_1 = this.countDeg(8, 0, 5, false);
      // 计算箭头2 和人正前方的单位向量的夹角
      let deg_2 = this.countDeg(-1, 0, 5, false);
      console.log(deg_1, deg_2);
      this.deg1 = deg_1;
      this.deg2 = deg_2;
      // EllipseCurve 的第五个、第六个参数用弧度
      const arcCurve = new this.$three.EllipseCurve( 0, 0, 5, 5, deg_1, deg_2, false,);
      // const arcCurve = new this.$three.EllipseCurve( 0, 0, 5, 5, 0, 2 * Math.PI, false, 0 );
      const points = arcCurve.getPoints(100);
      const geometry = new this.$three.BufferGeometry();
      geometry.setFromPoints(points);
      const material = new this.$three.LineBasicMaterial({ color: 0xffffff });
      const line = new this.$three.Line(geometry, material);
      line.rotateX(Math.PI / 2);
      this.scene.add(line);
    },
  },
};
</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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值