首页 > 其他分享 >three.js中使用r3f在模型上打多处孔洞(模型相减)

three.js中使用r3f在模型上打多处孔洞(模型相减)

时间:2022-10-20 13:34:51浏览次数:74  
标签:const 模型 THREE three let result position new r3f

在日常开发中接到一个需求,需要在three.js构建的3D场景中模拟激光打孔的操作,通过一个名为three-csg-ts库实现模型相减的操作。

之前使用一个名为@react-three/csg的库,但是官方文档中也注明,此库功能不算完善,使用之后发现,多个模型相减做不到相对动态的构建。所以换了three-csg-ts,动态构建模型相减比较灵活。
对于射线和目标物相交的点,一开始的想法是引入物理库,但是测试后发现,物理库的碰撞点有不小的偏差,与直接使用THREE.Ray区别不大,而且还需要下载额外的包,所以直接使用的THREE.Ray,得到和标的物相交的世界坐标后,转化成组内的局部坐标,在局部坐标中的标的物上打上对应孔洞。

新建孔洞函数:

//新建孔洞
const hole = new THREE.Mesh(new THREE.CylinderGeometry(0.01, 0.01, 5, 32));
hole.visible = false;
const createHole = (position: number[]) => {
  // const hole = new THREE.Mesh(new THREE.CylinderGeometry(0.01, 0.01, 5, 32));
  hole.rotation.x = Math.PI / 2;
  hole.position.set(position[0], position[1], position[2]);
  hole.updateMatrix();
  return hole;
};

组合多个相减形状:

//组合形状
const additionShap = (
  list = [
    [0, 0, 0],
    [0.03, 0.24, -1.04],
  ]
): THREE.Mesh => {
  let result;
  if (list.length > 1) {
    result = list.reduce((pre: any, item): any => {
      // console.log("-----",pre instanceof Array);
      return CSG.union(
        pre instanceof Array ? createHole(pre) : pre,
        createHole(item)
      );
    });
  }
  if (list.length == 1) {
    result = CSG.union(createHole(list[0]), createHole([1000, 1000, 1000]));
  }

  return result as unknown as THREE.Mesh;
};

确定射线和透镜相交位置:

//确定射线和透镜相交位置
//targetRef 目标物
//局部坐标标志物
const getRayIntersect = (
  target: THREE.Mesh,
  localBox: THREE.Object3D
): THREE.Vector3 => {
  console.log("调用函数");
  let ray = new THREE.Ray(); //初始化射线
  ray.origin = new THREE.Vector3(1.5, 0.24, -1.04); //确定射线发出原点
  ray.direction = new THREE.Vector3(-1, 0, 0).normalize(); //射线发出方向
  let result: THREE.Vector3 = new THREE.Vector3(0, 0, 0); //结果

  let box = new THREE.Box3(); //初始化碰撞盒子
  target.geometry.computeBoundingBox(); //计算盒子边界
  box //转化盒子边界坐标
    .copy(target.geometry.boundingBox as THREE.Box3)
    .applyMatrix4(target.matrixWorld);
  const point: any = ray.intersectBox(box, result) ?? { x: 5, y: 5, z: 5 };
  let localPosition = localBox.worldToLocal(point);
  // ray.recast();
  return localPosition;
};

模型上打孔:

//模型打孔
const modelPerforate = (
  model: THREE.Mesh,
  holesPosition: number[][]
): THREE.Mesh => {
  const subResult = CSG.subtract(model, additionShap(holesPosition));
  return subResult;
};

完整源代码:

import React, { useRef, useEffect, useState } from "react";
import * as THREE from "three";
import { useThree } from "@react-three/fiber";
import { useGLTF, Html } from "@react-three/drei";
import { CSG } from "three-csg-ts";

type Props = {};

//新建孔洞
const hole = new THREE.Mesh(new THREE.CylinderGeometry(0.01, 0.01, 5, 32));
hole.visible = false;
const createHole = (position: number[]) => {
  // const hole = new THREE.Mesh(new THREE.CylinderGeometry(0.01, 0.01, 5, 32));
  hole.rotation.x = Math.PI / 2;
  hole.position.set(position[0], position[1], position[2]);
  hole.updateMatrix();
  return hole;
};
//组合形状
const additionShap = (
  list = [
    [0, 0, 0],
    [0.03, 0.24, -1.04],
  ]
): THREE.Mesh => {
  let result;
  if (list.length > 1) {
    result = list.reduce((pre: any, item): any => {
      // console.log("-----",pre instanceof Array);
      return CSG.union(
        pre instanceof Array ? createHole(pre) : pre,
        createHole(item)
      );
    });
  }
  if (list.length == 1) {
    result = CSG.union(createHole(list[0]), createHole([1000, 1000, 1000]));
  }

  return result as unknown as THREE.Mesh;
};
//确定射线和透镜相交位置
//targetRef 目标物
//局部坐标标志物
const getRayIntersect = (
  target: THREE.Mesh,
  localBox: THREE.Object3D
): THREE.Vector3 => {
  console.log("调用函数");
  let ray = new THREE.Ray(); //初始化射线
  ray.origin = new THREE.Vector3(1.5, 0.24, -1.04); //确定射线发出原点
  ray.direction = new THREE.Vector3(-1, 0, 0).normalize(); //射线发出方向
  let result: THREE.Vector3 = new THREE.Vector3(0, 0, 0); //结果

  let box = new THREE.Box3(); //初始化碰撞盒子
  target.geometry.computeBoundingBox(); //计算盒子边界
  box //转化盒子边界坐标
    .copy(target.geometry.boundingBox as THREE.Box3)
    .applyMatrix4(target.matrixWorld);
  const point: any = ray.intersectBox(box, result) ?? { x: 5, y: 5, z: 5 };
  let localPosition = localBox.worldToLocal(point);
  // ray.recast();
  return localPosition;
};
//模型打孔
const modelPerforate = (
  model: THREE.Mesh,
  holesPosition: number[][]
): THREE.Mesh => {
  const subResult = CSG.subtract(model, additionShap(holesPosition));
  return subResult;
};

function LaserAndTarget({}: Props) {
  const localGroup = useRef<any | null>(); //本地组
  const lineRef = useRef<any | null>(); //红色射线
  const theMirror = useRef<any | null>(); //目标镜子
  const [holesPosition, setHolesPosition] = useState<number[][]>([]); //孔洞位置
  const [mirrorPosition, setMirrorPosition] = useState<THREE.Vector3>(
    new THREE.Vector3(0.05, 0.31, -0.33)
  ); //镜子位置
  const { nodes, materials } = useGLTF("/glb/MarkerMirror.glb") as any;
  materials.材质 = new THREE.MeshPhysicalMaterial({
    color: 0xffffff,
    transmission: 0.4,
    opacity: 1,
    metalness: 0,
    roughness: 0,
    ior: 1.45,
    specularIntensity: 10,
    specularColor: new THREE.Color("red"),
  });

  //确定位置
  useEffect(() => {
    console.log("位置重绘");
    let result = getRayIntersect(theMirror.current, localGroup.current);
    console.log("命中位置", result);
    let position = holesPosition;
    position.push([result.x - 0.01, result.y, result.z]);
    setHolesPosition(position);
    // const cube2 = new THREE.Mesh(
    //   new THREE.BoxGeometry(0.02, 0.02, 0.02),
    //   new THREE.MeshNormalMaterial()
    // );
    // cube2.position.set(result.x, result.y, result.z);
    // localGroup.current.add(cube2);
  }, [mirrorPosition]);
  //打孔
  useEffect(() => {
    theMirror.current.updateMatrix();
    theMirror.current.needsUpdate = true;
    theMirror.current.geometry = modelPerforate(
      theMirror.current,
      holesPosition
    ).geometry;
  }, [holesPosition, mirrorPosition]);
  return (
    <group ref={localGroup}>
      <mesh
        ref={lineRef}
        rotation-x={Math.PI / 2}
        position={[0.03, 0.24, -1.04]}
      >
        <cylinderGeometry args={[0.008, 0.008, 1000, 32]} />
        <meshBasicMaterial color={"red"} side={THREE.DoubleSide} />
      </mesh>
      {/* 射线起点 */}
      <mesh position={[0.03, 0.24, 5.55]}>
        <boxGeometry args={[0.03, 0.03, 0.03]} />
        <meshStandardMaterial color={"gold"} />
      </mesh>
      {/* 目标 */}
      <mesh
        ref={theMirror}
        scale={0.06}
        position={mirrorPosition}
        rotation-x={Math.PI / 2}
        geometry={nodes.圆柱.geometry}
        material={materials["材质"]}
      />
      <Html>
        <button
          onClick={() => {
            console.log("点击");
            let position = new THREE.Vector3(
              mirrorPosition.x,
              mirrorPosition.y,
              mirrorPosition.z
            );
            position.x += 0.02;
            setMirrorPosition(position);
          }}
        >
          右
        </button>
        <button
          onClick={() => {
            console.log("点击");
            let position = new THREE.Vector3(
              mirrorPosition.x,
              mirrorPosition.y,
              mirrorPosition.z
            );
            position.y += 0.02;
            setMirrorPosition(position);
          }}
        >
          上
        </button>
        <button onClick={()=>{
          console.log("点击")
          let position = new THREE.Vector3(mirrorPosition.x,mirrorPosition.y,mirrorPosition.z);
          position.y-=0.02;
          setMirrorPosition(position)
        }}>下</button>
          <button onClick={()=>{
          console.log("点击")
          let position = new THREE.Vector3(mirrorPosition.x,mirrorPosition.y,mirrorPosition.z);
          position.x-=0.02;
          setMirrorPosition(position)
        }}>左</button>
      </Html>
    </group>
  );
}

export default LaserAndTarget;

标签:const,模型,THREE,three,let,result,position,new,r3f
From: https://www.cnblogs.com/sunyan97/p/16809548.html

相关文章

  • 【总结】配置一台用于跑深度学习模型的主机
    看了李沐的视频安静、高性价比双卡装机【100亿模型计划】想配置一台可以跑大模型的机器。不是因为有必要的需求,而是觉得很有意思,想要玩一玩。挑选硬件我并不是很在行,就直......
  • EasyNLP发布融合语言学和事实知识的中文预训练模型CKBERT
    导读预训练语言模型在NLP的各个应用中都有及其广泛的应用;然而,经典的预训练语言模型(例如BERT)缺乏对知识的理解,例如知识图谱中的关系三元组。知识增强预训练模型使用外部知识(......
  • ModStartCMS v5.0.0 模块市场优化,模型类型修改
    企业内容建站系统ModStartCMSv5.0.0模块市场优化,模型类型修改系统介绍ModStart是一个基于Laravel模块化极速开发框架。模块市场拥有丰富的功能应用,支持后台一键快速安......
  • 一文详尽系列之模型评估指标
    在机器学习领域通常会根据实际的业务场景拟定相应的不同的业务指标,针对不同机器学习问题如回归、分类、排序,其评估指标也会不同。准确率、精确率、召回率、F1值定义准确率(Ac......
  • C++类模型漫谈(二)
    系统基于32位,MSVC编译器,VS开发工具1、通过对象对成员函数的调用,默认会给参数传进去一个this指针,该指针为对象的首地址,这个过程通常被编译器隐藏起来了。对象直接调用成员......
  • [原创]一款基于Reactor线程模型的java网络爬虫框架
    AJSpridergithub:​​https://github.com/zhuchangwu/AJSpider​​概述AJSprider是笔者基于Reactor线程模式+Jsoup+HttpClient封装的一款轻量级java多线程网络爬虫框架,简......
  • 深度学习模型部署:落实产品部署前至关重要的一件事!
    作者:Edison_G前段时间,”计算机视觉研究院“推出一期专门说部署模型的专题,今天我来和大家说说部署模型前的一件重要事情,那就是训练,如何加速训练也是一件不可忽略的流程,今天技......
  • C++类模型漫谈(一)
    系统基于32位,MSVC编译器,VS开发工具1、一个简单的类型TypeA,类型对象a_obj总共占8个字节。char类型a1占1个字节,但是为了考虑到32位系统存储效率,所以a1变量后面会分配3个字节......
  • 建立KS评估模型
    导入资源包fromsklearn.linear_modelimportLogisticRegressionimportpandasaspdfromsklearn.model_selectionimporttrain_test_splitfromsklearn.metricsim......
  • Dubbo——Dubbo中的URL统一资源模型与Dubbo协议
    一、URL简介在互联网领域,每个信息资源都有统一的且在网上唯一的地址,该地址就叫URL(UniformResourceLocator,统一资源定位符),它是互联网的统一资源定位标志,也就是指网络地址......