当前位置:网站首页>射线与OBB相交检测
射线与OBB相交检测
2022-07-07 14:27:00 【[奋斗不止]】
在上一篇 射线与AABB相交检测
射线与OBB3D 相交检测的原理跟射线与AABB相交检测的原理相同,本篇不再讲解原理
上篇推论出:射线与平面相交点距离射线起点距离t的距离公式为 t = (d - Dot(p, n)) / Dot(rayDir, n)
OBB3D 与 AABB 属性上的区别
AABB 三个轴向量固定: (1, 0, 0), (0, 1, 0), (0, 0, 1)
OBB3D 三个轴向量随旋转变化
AABB 的表示可以使用 min, max 坐标点表示
OBB3D 则没有 min, max 坐标点
所以AABB计算逻辑中使用到的坐标点和固定轴向,需要替换为 OBB3D 的属性,
轴向分别使用 _axisX,_axisY, _axisZ
取 OBB3D 两个顶点,每个顶点分别是三个面的交点或者叫顶点
_vertexs[0] = center + (axisX * size.x + axisY * size.y + axisZ * size.z) * 0.5f;
_vertexs[5] = center + (-axisX * size.x - axisY * size.y - axisZ * size.z) * 0.5f;
逻辑代码如下
public class OBB3D
{
/// <summary>
/// X 轴方向向量
/// </summary>
public Vector3 _axisX;
/// <summary>
/// Y 轴方向向量
/// </summary>
public Vector3 _axisY;
/// <summary>
/// Z 轴方向向量
/// </summary>
public Vector3 _axisZ;
/// <summary>
/// 中心点坐标
/// </summary>
public Vector3 _center;
/// <summary>
/// 三条边长度
/// </summary>
public Vector3 _size;
/// <summary>
/// 八个顶点坐标
/// </summary>
public Vector3[] _vertexs;
public OBB3D(){
}
public void Set(Vector3 axisX, Vector3 axisY, Vector3 axisZ, Vector3 center, Vector3 size)
{
_axisX = axisX;
_axisY = axisY;
_axisZ = axisZ;
_center = center;
_size = size;
_vertexs = new Vector3[8];
_vertexs[0] = center + (axisX * size.x + axisY * size.y + axisZ * size.z) * 0.5f;
_vertexs[1] = center + (axisX * size.x - axisY * size.y + axisZ * size.z) * 0.5f;
_vertexs[2] = center + (axisX * size.x + axisY * size.y - axisZ * size.z) * 0.5f;
_vertexs[3] = center + (axisX * size.x - axisY * size.y - axisZ * size.z) * 0.5f;
_vertexs[4] = center + (-axisX * size.x + axisY * size.y - axisZ * size.z) * 0.5f;
_vertexs[5] = center + (-axisX * size.x - axisY * size.y - axisZ * size.z) * 0.5f;
_vertexs[6] = center + (-axisX * size.x - axisY * size.y + axisZ * size.z) * 0.5f;
_vertexs[7] = center + (-axisX * size.x + axisY * size.y + axisZ * size.z) * 0.5f;
}
}
/// <summary>
/// 射线与AABB相交检测
/// 下面方法是 射线与 3D AABB 相交的计算
/// 如果想计算 射线与 2D AABB 相交,则将下方关于 z 坐标的部分删除即可
/// </summary>
public class RayOBBCollision
{
/// <summary>
/// 判断射线与AABB是否相交
/// </summary>
/// <param name="raySource">射线起点</param>
/// <param name="rayDir">射线方向</param>
/// <param name="aabb">AABB</param>
/// <param name="point">射线与AABB交点坐标</param>
/// <returns></returns>
public bool IsCollision(Vector3 raySource, Vector3 rayDir, OBB3D obb3D, ref Vector3 point)
{
float length = 0;
bool collision = IsCollision(raySource, rayDir, obb3D, ref length);
point = raySource + rayDir * length;
return collision;
}
/// <summary>
/// 判断射线与AABB是否相交
/// </summary>
/// <param name="raySource">射线起点</param>
/// <param name="rayDir">射线方向向量</param>
/// <param name="obb3D">OBB3D</param>
/// <param name="length">射线起点到相交点距离</param>
/// <returns></returns>
public bool IsCollision(Vector3 raySource, Vector3 rayDir, OBB3D obb3D, ref float length)
{
float t1 = 0;
float t2 = 0;
bool collision = Calculate(raySource, rayDir, obb3D, obb3D._axisX, ref t1, ref t2);
if (!collision || !CheckValue(ref t1, ref t2))
{
return false;
}
float t3 = 0;
float t4 = 0;
collision = Calculate(raySource, rayDir, obb3D, obb3D._axisY, ref t3, ref t4);
if (!collision || !CheckValue(ref t3, ref t4))
{
return false;
}
float t5 = 0;
float t6 = 0;
collision = Calculate(raySource, rayDir, obb3D, obb3D._axisZ, ref t5, ref t6);
if (!collision || !CheckValue(ref t5, ref t6))
{
return false;
}
float entryT1 = Math.Max(Math.Max(t1, t3), t5);
float entryT2 = Math.Min(Math.Min(t2, t4), t6);
length = entryT1;
return (entryT1 < entryT2);
}
/// <summary>
/// 射线与平面相交计算
/// </summary>
/// <param name="raySource">射线起点</param>
/// <param name="rayDir">射线方向向量</param>
/// <param name="obb3D">aabb</param>
/// <param name="normal">平面法向量</param>
/// t = (d - Dot(raySource, normal)) / Dot(rayDir, normal)
/// d = Dot(planePoint, normal)
/// t = (Dot(planePoint, normal) - Dot(raySource, normal)) / Dot(rayDir, normal)
/// t = Dot((planePoint - raySource), normal) / Dot(rayDir, normal)
private bool Calculate(Vector3 raySource, Vector3 rayDir, OBB3D obb3D, Vector3 normal, ref float t1, ref float t2)
{
float p_sub_r_dot_n1 = Vector3.Dot(obb3D._vertexs[0] - raySource, normal);
float p_sub_r_dot_n2 = Vector3.Dot(obb3D._vertexs[5] - raySource, normal);
float r_dot_n = Vector3.Dot(rayDir, normal);
if (Math.Abs(r_dot_n) <= float.Epsilon) // 射线垂直于平面法向量,所以射线与平面平行
{
float dot1 = Vector3.Dot(obb3D._vertexs[0] - raySource, normal);
float dot2 = Vector3.Dot(obb3D._vertexs[5] - raySource, normal);
if (dot1 * dot2 > 0)
{
return false;
}
}
t1 = p_sub_r_dot_n1 / r_dot_n;
t2 = p_sub_r_dot_n2 / r_dot_n;
return true;
}
private bool CheckValue(ref float value1, ref float value2)
{
if (value1 < 0 && value2 < 0)
{
return false;
}
value1 = Mathf.Clamp(value1, 0, value1);
value2 = Mathf.Clamp(value2, 0, value2);
if (value1 > value2)
{
float temp = value1;
value1 = value2;
value2 = temp;
}
return true;
}
}
测试代码如下
using System;
using UnityEngine;
public class RayOBBController : MonoBehaviour
{
// Cube 立方体 A
public Transform cube;
// 定义两个 OBB3D 立方体
private OBB3D obb3D;
// 相交检测逻辑
private RayOBBCollision rayOBBCollision;
void Start()
{
// 实例化
obb3D = new OBB3D();
rayOBBCollision = new RayOBBCollision();
}
private bool result = false;
// 创建一个小球,当射线与AABB相交时,将交点坐标设置给小球
private GameObject go;
void Update()
{
// 将两个Cube 的数据分别赋值给 OBB3D
SetOBB(cube, obb3D);
Vector3 point = Vector3.zero;
// 判断是否相交
bool isCollision = rayOBBCollision.IsCollision(transform.position, transform.forward, obb3D, ref point);
if (result != isCollision)
{
result = isCollision;
// 如果设想与AABB相交,将AABB物体设置为红色
cube.GetComponent<Renderer>().material.color = result ? Color.red : Color.gray;
}
if (result)
{
// 如果射线与AABB相交,将小球坐标设置为相交点
if (!go)
{
go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.transform.localScale = Vector3.one * 0.2f;
}
go.transform.position = point;
}
}
private void SetOBB(Transform tr, OBB3D obb)
{
// OBB3D 的三个轴分别使用 Cube.transform:right、up、forward
// OBB3D 的坐标使用 Cube.transform.position
// OBB3D 的size 使用 Cube.transform.localScale
obb.Set(tr.right, tr.up, tr.forward, tr.position, tr.localScale);
}
}
边栏推荐
- Build an all in one application development platform, light flow, and establish a code free industry benchmark
- [flower carving experience] 15 try to build the Arduino development environment of beetle esp32 C3
- Common training data set formats for target tracking
- null == undefined
- laravel怎么获取到public路径
- logback. XML configure logs of different levels and set color output
- 95.(cesium篇)cesium动态单体化-3D建筑物(楼栋)
- [hcsd celebrity live broadcast] teach the interview tips of big companies in person - brief notes
- Iptables only allows the specified IP address to access the specified port
- 01tire+ chain forward star +dfs+ greedy exercise one
猜你喜欢
【MySql进阶】索引详解(一):索引数据页结构
删除 console 语句引发的惨案
Lecturer solicitation order | Apache seatunnel (cultivating) meetup sharing guests are in hot Recruitment!
Continuous creation depends on it!
Vs2019 configuration matrix library eigen
Mysql database basic operation DQL basic query
What about the pointer in neural network C language
全网“追杀”钟薛高
【DesignMode】代理模式(proxy pattern)
Personal notes of graphics (2)
随机推荐
二叉搜索树(特性篇)
Xcode Revoke certificate
【PHP】PHP接口继承及接口多继承原理与实现方法
【Vulnhub靶场】THALES:1
laravel构造函数和中间件执行顺序问题
How can laravel get the public path
Deep listening array deep listening watch
Opencv configuration 2019vs
Personal notes of graphics (3)
偶然升职的内心独白
Xcode Revoke certificate
95. (cesium chapter) cesium dynamic monomer-3d building (building)
js中复选框checkbox如何判定为被选中
Leetcode-136-只出现一次的数(用异或来解答)
Find tags in prefab in unity editing mode
二叉搜索树(基操篇)
torch. Numel action
Laravel5.1 路由 -路由分组
Bidding announcement: Fujian Rural Credit Union database audit system procurement project (re bidding)
PHP中exit,exit(0),exit(1),exit(‘0’),exit(‘1’),die,return的区别