three.js 学习笔记 | 光线投射技术 - 包围盒(碰撞检测)

文章目录

  • three.js 学习笔记
    • 光线投射技术实现3D场景交互事件 THREE.Raycaster
      • 坐标系的转换
      • 案例:选中的模型变为红色
    • 包围盒Box3 - 碰撞检测
      • AABB包围盒辅助器Box3Helper
        • 案例1:创建AABB包围盒/包围球
          • computeBoundingBox与boundingBox 搭配使用,创建包围盒
          • box3的常用场景

three.js 学习笔记

光线投射技术实现3D场景交互事件 THREE.Raycaster

语法:new THREE.Raycaster()
作用:光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)
参数
origin:光线投射的原点向量。
direction:向射线提供方向的方向向量(归一化)

向量归一化:将向量的方向保持不变,大小归一化到1。
在这里插入图片描述在这里插入图片描述

Raycaster对象的属性及方法

属性及方法参数描述
setFromCamera ( coords : Vector2, camera : Camera ) : undefinedcoords:在标准化设备坐标中鼠标的二维坐标(x,y分量在-1,1之间)
camera:射线来源的相机
使用一个新的原点和方向来更新射线
intersectObjects ( objects : Array, recursive : Boolean, optionalTarget : Array ) : Arrayobjects:检测和射线相交的一组物体。
recursive :检测物体本身外,是否同时检测所有物体的后代。默认值为true。
optionalTarget:设置存储结果的目标数组,最后返回的也是这个数组,如果没有设置则每次调用都会初始化一个新数组。采用push的方式向数组中添加,所以如果设置的是全局数组会不断的向里面push新元素

返回值:相交对象组成的数组
检测射线与这些物体之间是否有相交点

鼠标拾取(射线追踪法)
场景最终被输出在一个画布canvas元素上,所以没办法获取鼠标hover处模型的DOM对象。
在2D中,判断鼠标是否选中的标准是鼠标位置是否在该区域中。在3D场景中,由于屏幕是2D显示的,判断选中的标准是从相机为起点位置穿过鼠标位置的射线是否也穿过模型,如果穿过则认为该模型被选中。
在这里插入图片描述

坐标系的转换

  • 三维空间的坐标系:屏幕中心为原点,坐标取值范围(-1,1)
  • 页面的二维坐标:左上角为原点,取值范围是按照屏幕大小的实际像素位置来获取(x, y)。

三维空间的坐标系(世界坐标)
屏幕中心为原点,坐标取值范围(-1,1)
在这里插入图片描述

页面的二维坐标系
左上角为原点,取值范围是按照屏幕大小的实际像素位置来获取(x, y)。
在这里插入图片描述
转换步骤

  1. 鼠标位置(x1,y1)是二维画面中的坐标位置

    鼠标位置(x1,y1) = (event.clientX, event.clientY)

  2. 在鼠标拾取时,需要将该坐标转换为中间位置(x2,y2),该中间位置以屏幕中心为原点,实际像素为x和y

    在二维页面中,屏幕中点的位置是(innerWidth / 2, innerHeight / 2)。可以得到任意点(x1,y1)的中间位置 x2 = x1 - (innerWidth / 2)y2 = (innerHeight / 2) - y1
    在这里插入图片描述

  3. 由于三维坐标系中x与y分量的取值范围是(-1,1),所以需要将(x2,y2)归一化处理得到(x3,y3)

    考虑极端点`(x2,y2) = (w/2,h/2)`对应的`(x3,y3)=(1,1)` 
    x3 = (x2  / (w/2))
    = (  ( x1 -  ( w/2 )  ) /  (w/2) )
    =  (2   x1 / w) - 1
    = (x1 / w)* 2 - 1 
    同理得到 y3 = - ( y1 /  h) * 2  + 1
    
    将x1与y1待入可得 鼠标点击位置在三维坐标系中的坐标
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
    

案例:选中的模型变为红色

方法:intersectObjects ( objects : Array, recursive : Boolean, optionalTarget : Array ) : Array 检测射线与这些物体之间是否有相交点
参数
objects:检测和射线相交的一组物体。
recursive :检测物体本身外,是否同时检测所有物体的后代。默认值为true。
optionalTarget:设置存储结果的目标数组,最后返回的也是这个数组,如果没有设置则每次调用都会初始化一个新数组。采用push的方式向数组中添加,所以如果设置的是全局数组会不断的向里面push新元素
返回值:相交对象组成的数组

相交对象的属性描述
distance相机到相交对象交点的距离
face相交对象(鼠标点击的物体)面的信息
normal:Vector3相交点的法线向量
object相交对象
point:Vector3交点的坐标
uv
const sphere1 = new THREE.Mesh(new THREE.SphereGeometry(25, 32,32), new THREE.MeshBasicMaterial ({
  color: 0x00ff00,
}));
sphere1.position.x= -140;
scene.add(sphere1);

const sphere2 = new THREE.Mesh(new THREE.SphereGeometry(25, 32,32), new THREE.MeshBasicMaterial ({
  color: 0x0000ff,
}));
scene.add(sphere2);


const sphere3 = new THREE.Mesh(new THREE.SphereGeometry(25, 32,32), new THREE.MeshBasicMaterial ({
  color: 0xff00ff,
}));
sphere3.position.x= 140;
scene.add(sphere3);

scene.add(new THREE.AxesHelper(100));


// 创建射向
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// 监听鼠标移动
window.addEventListener('click', (event) => {
  //设置鼠标在三维坐标系中的点击位置x,y
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
 console.log(mouse)
})

包围盒Box3 - 碰撞检测

包围盒是一个简单的几何空间,里面包含着复杂形状的物体。
为物体添加包围体的目的是快速的进行碰撞检测或者进行精确的碰撞检测之前进行过滤(即当包围体碰撞,才进行精确碰撞检测和处理)。

包围盒广泛地应用于①碰撞检测②摄像机视锥体的可见性判断,每一个物体都有自己的包围盒。因为包围盒一般为规则物体,因此用它来代替物体本身进行计算,会比直接用物体本身更加高效和简单。

在这里插入图片描述

常见包围盒描述特点
轴对齐包围盒(AABB)与坐标轴是对齐的,但不随物体旋转(图1刚好最小包围盒,图2老虎旋转了一个角度后,AABB包围盒便增加了较大的空隙)总是与世界坐标系(固定)的三个轴平行
有向包围盒(OBB)始终沿着物体的主成分方向生成最小的一个矩形包围盒,可以随物体旋转,可用于较精确的碰撞检测与物体局部坐标系(经常变换的坐标系)的三个轴平行

AABB包围盒辅助器Box3Helper

AABB包围盒的简单介绍
AABB盒:一个3D的AABB就是一个简单的六面体,每一边都平行于一个坐标平面,它的长、宽、高可以彼此不同。外边界是一个规整的形状,所以只需要两个坐标就能计算出该形状。
特别重要的两个顶点为:Pmin = [Xmin Ymin Zmin]Pmax = [ Xmax Ymax Zmax](最小点一般是左后下方的点,最大点是右前上方的一个点)

AABB内的点满足以下条件
xmin≤x≤xmax
ymin≤y≤ymax
zmin≤z≤zmax

Box3的介绍
Box3类表示与三维空间中的一个轴对齐包围盒(axis-aligned bounding boxAABB
构造函数Box3( min : Vector3, max : Vector3 )

  • min:Vector3 可选,表示包围盒的下边界。 默认值是(+ Infinity, + Infinity, + Infinity )
  • max:Vector3 可选,表示包围盒的上边界。 默认值是( - Infinity, - Infinity, - Infinity )

AABB包围盒辅助器Box3Helper
语法:new THREE.Box3Helper( box : Box3, color : Color )
box:被三维包围盒包围的物体参数Box3
color:可选表示线框盒子的颜色, 默认为0xffff00
作用:模拟3维包围盒 Box3 的辅助对象,创建一个新的线框盒子用以表示指定的3维包围盒。
继承链:Object3D → Line → LineSegments → Box3Helper

包围球没有辅助器,通过创建球来代替辅助器。

// 获取包围球
const sphere = bufferGeometry.boundingSphere;
// 创建辅助球
let sphereHelper = new THERE.SphereGeometry(sphere.radius)
let sphereHelperMaterial = new THERE.MeshBasicMaterial();
let sphereMesh = new THERE.Mesh(sphereHelper ,sphereHelperMaterial);
sphereMesh.position.copy(sphere.center)
scene.add(sphereMesh)
案例1:创建AABB包围盒/包围球
computeBoundingBox与boundingBox 搭配使用,创建包围盒

获取包围盒Box3bufferGeometry.boundingBox : Box3 ,获取当前 bufferGeometry 的外边界矩形,默认值是 null。
返回Box3:{max:{最大点的坐标},min:{最小点的坐标}}

计算被包围物体的包围盒bufferGeometry.computeBoundingBox(),计算当前几何体的边界矩形,该操作会更新已有boundingBox属性

为了可视化包围盒,我们会使用包围盒辅助器可视化包围盒,将bufferGeometry.boundingBox的值作为参数传递给包围盒辅助器Box3Helper的构造器参数。

const sphereGeometry = new THREE.SphereGeometry(50, 32, 32);
const sphereMaterial = new THREE.MeshBasicMaterial();
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
// 计算包裹盒,并将结果设置给bufferGeometry.boundingBox
sphereGeometry.computeBoundingBox()
const box3Helper = new THREE.Box3Helper( sphereGeometry.boundingBox)
scene.add(box3Helper);

在这里插入图片描述

box3的常用场景
  1. 包围盒应用与被包围三维物体同样的变化矩阵,包围盒应用变换矩阵applyMatrix4
    包围盒会受物体的形态影响,比如物体进行了变换。包围盒也需要应用同样的变换bufferGeometry.boundingBox.applyMatrix4 ( matrix : Matrix4 )

    可能包围盒根据物体顶点计算出来?但是有些变换不改变几何体的顶点信息?所以需要手动进行世界矩阵更新·

    box3.applyMatrix4 ( matrix : Matrix4 ):将参数的变换矩阵应用在当前Box3上,包围盒8个顶点都会乘以这个变换矩阵。

    // 三维物体更新世界矩阵	
    mesh.updateWorldMatrix(true,true)
    // box - 需要复制的包围盒 Box3  应用几何体的变化矩阵mesh.matrixWorld
    box.copy( mesh.geometry.boundingBox ).applyMatrix4( mesh.matrixWorld );
    
  2. 让几何体居中 获取包围盒/包围球的中心位置 = 获取三维物体的中心位置
    几何体.center():根据边界矩形将几何体居中,本质就是获取包围盒/包围球的中心位置。
    box3.getCenter ( target : Vector3 ) : Vector3:获取包围盒的中心位置。 如果指定了target ,结果将会被拷贝到target,返回中心位置。

    //1.计算几何体包围盒
    bufferGeometry.computeBoundingBox()
    //2.设置几何体居中
    bufferGeometry.center();
    
    // 1.获取包围盒
    const box = bufferGeometry.boundingBox()
    // 2.获取包围盒的中心 打印法相两个值相等
    const center = box .getCenter()
    
  3. 获取多个物体的包围盒

    思路:循环每一个几何体,获取单个的几何体包围盒不断合并成一个大包围盒
    方法:box3.union ( box : Box3 ) : this :在 box 参数的上边界和已有box对象的上边界之间取较大者,而对两者的下边界取较小者,这样获得一个新的较大的联合盒子。
    box3.setFromObject ( object : Object3D ) : this 参数是需要计算包围盒的3D对象 Object3D。计算和世界轴对齐的一个对象 Object3D (含其子对象)的包围盒,计算对象和子对象的世界坐标变换。 该方法可能会导致一个比严格需要的更大的框。

    没理解这个是和世界轴对齐的包围盒AABB还是说物体对齐,对象和世界轴对齐是指什么意思?

    let box = new THREE.Box3
    for(let i=0;i<scene.children.length;i++){
    	// 获取包围盒的方法1
    	// ① 获取当前几何体的包围盒  如果有场景中有其他不相关东西需要排出一下
    	scene.children[i].geometry.computeBoundingBox();
    	// ② 获取包围盒
    	let box3 = scene.children[i].geometry.boundingBox;
    	// ③ 更新世界矩阵
    	scene.children[i].updateWorldMatrix(true,true)
    	box3.applyMatrix4(scene.children[i].metrixWorld)
    	
    	// 获取包围盒的方法2
    	//②
    	let box3 = new THREE.Box3().setFromObject (scene.children[i])
    	
    	//合并包围盒
    	box.unioin(box3);
    }
    // 使用包围盒辅助器显示
    const box3Helper = new THREE.Box3Helper( box )
    scene.add(box3Helper);
    
    
    
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/579009.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】二叉树(带图详解)

文章目录 1.树的概念1.2 树的结构孩子表示法孩子兄弟表示法 1.3 相关概念 2.二叉树的概念及结构2.1 二叉树的概念2.2 数据结构中的二叉树-五种形态2.3 特殊的二叉树2.4 二叉树的存储结构顺序存储链式存储 2.5 二叉树的性质 3. 堆3.1 堆的定义3.2 堆的实现堆的结构堆的插入向上调…

springcloud按版本发布微服务达到不停机更新的效果

本文基于以下环境完成 spring-boot 2.3.2.RELEASEspring-cloud Hoxton.SR9spring-cloud-alibaba 2.2.6.RELEASEspring-cloud-starter-gateway 2.2.6.RELEASEspring-cloud-starter-loadbalancer 2.2.6.RELEASEnacos 2.0.3 一、思路 实现思路&#xff1a; 前端项目在请求后端接…

SVN--基本原理与使用(超详细)

目录 一、SVN概述二、SVN服务端软件安装三、SVN服务端配置四、SVN客户端软件安装与使用五、SVN三大指令六、SVN图标集与忽略功能6.1 图标集6.2 忽略功能 七、SVN版本回退八、SVN版本冲突九、SVN配置多仓库与权限控制9.1 配置多仓库9.2 权限控制 十、服务配置与管理十一、模拟真…

Java | Leetcode Java题解之第52题N皇后II

题目&#xff1a; 题解&#xff1a; class Solution {public int totalNQueens(int n) {Set<Integer> columns new HashSet<Integer>();Set<Integer> diagonals1 new HashSet<Integer>();Set<Integer> diagonals2 new HashSet<Integer>…

手写文本识别系统的最佳实践

手写文本识别系统的最佳实践 摘要IntroductionRelated WorkProposed HTR SystemConvolutional Backbone:Flattening Operation:Recurrent Head:CTC shortcut: Best Practices for a Handwritten Text Recognition System 摘要 手写文本识别在近年来随着深度学习及其应用的兴起…

文件夹惊变文件?揭秘原因及解决方案

在日常工作和生活中&#xff0c;电脑已经成为我们不可或缺的助手。然而&#xff0c;有时我们会遇到一些令人困惑的问题&#xff0c;比如&#xff0c;文件夹突然变成了文件。这听起来可能有些匪夷所思&#xff0c;但它确实会发生&#xff0c;而且给用户带来了不小的麻烦。当熟悉…

java-spring-mvc(知识点讲解-第一天)-欢迎各位大佬提建议

目录 &#x1f383;MVC定义 &#x1f9e8;创建工程 &#x1f3a8;SpringMVC处理请求 请求分类及处理方式 静态请求 处理静态前端页面方式 动态请求 处理动态前端页面方式 ⚙小试牛刀 &#x1f3c6;常见问题 &#x1f4cc;HTTP协议 超文本传输协议 请求 &#x1f383;MVC…

Web前端开发 小实训(二) 简易计算器

实训目的 学生能够使用函数完成简易计算器编写 操作步骤 1、请将加减乘除四个方法生成为以下函数&#xff0c;且有返回值 中文英语加法add减法subtract乘法multi除法division次幂pow()平方根sqrt() 提示&#xff1a; 除法中的除数不能为0&#xff01; 参考代码&#xff1…

在线培训考试系统在线考试功能注意事项

在线培训考试系统在线考试功能注意事项 考试前务必注意是否开启防切屏、摄像头监考等防作弊措施&#xff0c;系统一旦检测到触发了疑似作弊行为会立刻自动交卷&#xff0c;考试终止&#xff1b; 答题者准备好后&#xff0c;可点击“开始答题”按钮进入考试&#xff0c;注意考…

代码随想录第49天|121. 买卖股票的最佳时机 122.买卖股票的最佳时机II

121. 买卖股票的最佳时机 121. 买卖股票的最佳时机 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 动态规划之 LeetCode&#xff1a;121.买卖股票的最佳时机1_哔哩哔哩_bilibili 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一…

#C++里的引用#

目录 引用概念 定义引用类型 引用特性 常引用 传引用传参 传引用做返回值 1.引用概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。 比如&#xff1a…

【AI】一文介绍索引增强生成RAG的原理和结构

今天向大家介绍一下关于RAG的一些知识和经验。 这里说的RAG可以理解为目前针对企业知识库问答等AI应用场景的解决方案,这个场景就是利用自然语言大模型LLM与用户自有的文件进行对话的能力。 【RAG的优势】 首先,讲一讲RAG的优势特征。 如果把AI想象成一个待上岗的人类助手,…

1、Flink DataStreamAPI 概述(上)

一、DataStream API 1、概述 1&#xff09;Flink程序剖析 1.Flink程序组成 a&#xff09;Flink程序基本组成 获取一个执行环境&#xff08;execution environment&#xff09;&#xff1b;加载/创建初始数据&#xff1b;指定数据相关的转换&#xff1b;指定计算结果的存储…

模型 AIPL(认知、兴趣、购买、忠诚)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。品牌营销的量化与链路化。 1 AIPL模型的应用 1.1 耐克如何利用AIPL模型来推广其运动鞋和服装产品? 耐克利用AIPL模型来推广其运动鞋和服装产品&#xff0c;具体如下&#xff1a; 认知&#xff08;…

为什么总说抖店做不好?做抖店需要注意这些问题,是不是都占了?

大家好&#xff0c;我是电商花花。 今天给大家总结了4个新手不注意又常犯的几个原因&#xff0c;这些建议都是我们做店这些年的实操经验&#xff0c;可以帮助大家做店自查&#xff0c;有责改之&#xff0c;无责加冕。 1、抖店核心是选品而不是玩法 很多新手在刚开始做店抖音小…

8K+/月!学习就业一把梭,祝贺誉天数通陈同学收获心仪offer !

大家好&#xff0c;我是誉天数通的陈同学。 在武汉&#xff0c;网络运维圈的人应该都会听过誉天的名字。作为华为的授权培训合作伙伴&#xff0c;誉天不仅提供专业全面的工程师培训&#xff0c;还以其独特的环境和氛围吸引着无数有志于在网络领域发展的朋友。 一年前的我&#…

用坐标与碰撞检测效果

用pygame碰撞检测子弹 与 子弹 格子检查是否在墙内 结果差不了多少 输出的是碰撞点坐标 在计算角色与强的距离是 你用了几条射线 2024/4/26 13:20:48 在代码中&#xff0c;用于计算角色与墙之间距离的部分是通过投射多条射线来实现的。具体来说&#xff0c;在循环中&#xf…

IDEA插件

POJO to JSON —— 实体转Json Smart Input —— 自动切换中英文 Translation —— 翻译 Maven Helper —— 依赖管理 .ignore —— 忽略提交文件 enum-quick-generate —— 枚举代码生成 粘贴到项目包下

前端更优雅的使用 jsonp

前端更优雅的使用 jsonp 背景&#xff1a;最近项目中又使用到了 jsonp 这一项跨域的技术&#xff0c;&#xff08;主要还是受同源策略影响&#xff09;&#xff0c;下面有为大家提供封装好的函数及对应使用示例&#xff0c;欢迎大家阅读理解 文章目录 前端更优雅的使用 jsonp同…

基于车载点云数据的城市道路特征目标提取与三维重构

作者&#xff1a;邓宇彤&#xff0c;李峰&#xff0c;周思齐等 来源&#xff1a;《北京工业大学学报》 编辑&#xff1a;东岸因为一点人工一点智能公众号 基于车载点云数据的城市道路特征目标提取与三维重构本研究旨在弥补现有研究在处理复杂环境和大数据量上的不足&#xf…
最新文章