背景介绍(学习算法之前需要先了解)

射线与空间内三角形的相交检测是游戏程序设计中一个常见的问题,最典型的应用就是拾取(Picking),本文介绍一个最常见的方法,这个方法也是DirectX中采用的方法,该方法速度快,而且存储空间少。先讲述理论,然后文章末尾给出对应的代码实现与Unity中的显示。

简单而直观的方法是:先判断射线是否与三角形所在的平面相交,如果相交,再判断交点是否在三角形内。但这种方法效率并不高,因为多计算了三角形所在的平面

Möller-Trumbore射线三角相交算法是一种快速计算射线与空间内三角形的交点的方法,通过向量与矩阵计算可以快速得出交点与重心坐标,而无需对包含三角形的平面方程进行预计算。它应用于计算机图形学中以实现涉及三角形网格的光线跟踪计算。算法名字是以发明者TomasMöller和Ben Trumbore的名字来命名的。



参数说明

设射线的方程Ray=O+td(O为起点,D为射线方向(方向向量),t为权重),一个点从起点O开始,沿着方向D移动任意长度,得到终点R,根据t值得不同,得到得R值也不同,所有这些不同的R值便构成了整条射线。

三角形三个顶点P0,P1,P2。u为P1的权重,v为P2的权重,而1-u-v是P0的权重,可以理解为沿着边AC移动一段距离,然后再沿着边AB移动一段距离,最后求它们的和向量。至于移动多大距离,就是由参数u和v控制的,所表达的数学意义是三角形及其内部所有点的方程。

\[Ray:O+td{\quad}(t≥0)
\]
\[(1-u-v)P0+uP1+vP2
\]

推导过程

两个重要的定理

克莱姆法则



解线性方程组时

\[\left[ \begin{matrix} -D & E_1 & E_2\end{matrix} \right]\left[ \begin{matrix} t \\ u \\ v\end{matrix} \right]=S
\]

D,E1,E2是含有三个参数的行列式,即可表达成

\[ t=\frac{det{\left[ \begin{matrix} S & E_1 & E_2\end{matrix} \right]}}{det{\left[ \begin{matrix} -D & E_1 & E_2\end{matrix} \right]}}=\frac{\left[ \begin{matrix} S_x & E_{x1} & E_{x2} \\ S_y & E_{y1} & E_{y2} \\ S_{z} & E_{z1} & E_{z2}\end{matrix} \right]}{\left[ \begin{matrix} -D_x & E_{x1} & E_{x2} \\ -D_y & E_{y1} & E_{y2} \\ -D_{z} & E_{z1} & E_{z2}\end{matrix} \right]}
\]
\[ u=\frac{det{\left[ \begin{matrix} -D & S & E_2\end{matrix} \right]}}{det{\left[ \begin{matrix} -D & E_1 & E_2\end{matrix} \right]}}=\frac{\left[ \begin{matrix} -D_x & S_x & E_{x2} \\ -D_y & S_y & E_{y2} \\ -D_{z} & S_z & E_{z2}\end{matrix} \right]}{\left[ \begin{matrix} -D_x & E_{x1} & E_{x2} \\ -D_y & E_{y1} & E_{y2} \\ -D_{z} & E_{z1} & E_{z2}\end{matrix} \right]}
\]
\[ v=\frac{det{\left[ \begin{matrix} -D & E_1 & S\end{matrix} \right]}}{det{\left[ \begin{matrix} -D & E_1 & E_2\end{matrix} \right]}}=\frac{\left[ \begin{matrix} -D_x & E_{x1} & S_{x} \\ -D_y & E_{y1} & S_y \\ -D_{z} & E_{z1} & S_z\end{matrix} \right]}{\left[ \begin{matrix} -D_x & E_{x1} & E_{x2} \\ -D_y & E_{y1} & E_{y2} \\ -D_{z} & E_{z1} & E_{z2}\end{matrix} \right]}
\]

向量混合积

\[ a·(b×c)=b·(c×a)=c·(a×b)\\
a·(b×c)=-a·(c×b)\\
a·(b×c)=-b·(a×c)\\
a·(b×c)=-c·(b×a)
\]

将方程联立

\[O+td=(1-u-v)P0+P1+P2{\quad}(u≥0,v≥0,u+v≤1)
\]

化简

\[O-P0=u(P1-P0)+v(P2-P0)-td\\
S=uE_1+vE_2-td\\{\quad}(S=O-P0,E_1=P1-P0,E_2=P2-P0)
\]
\[\left[ \begin{matrix} -D & E_1 & E_2\end{matrix} \right]\left[ \begin{matrix} t \\ u \\ v\end{matrix} \right]=S
\]

克拉姆法则

\[t=\frac{det{\left[ \begin{matrix} S & E_1 & E_2\end{matrix} \right]}}{det{\left[ \begin{matrix} -D & E_1 & E_2\end{matrix} \right]}}
\]

向量混合积

分母部分

运用向量混合积定理,D前的-号,被抵消了

\[ det{\left[ \begin{matrix} -D & E_1 & E_2\end{matrix} \right]}=-D·(E_1×E_2)=E_1·(D×E_2)
\]

\[ S1=D×E_2
\]

分子部分:

\[ det{\left[ \begin{matrix} S & E_1 & E_2\end{matrix} \right]}=((S×E_1)·E_2)
\]

\[ S2=S×E_1
\]

原式等于

\[ det{\left[ \begin{matrix} S & E_1 & E_2\end{matrix} \right]}=S2·E_2
\]

因此

\[ t=\frac{S2·E_2}{E_1·S1}
\]

同理可推得其他两个参数u,v

\[ u=\frac{S1·S}{E_1·S1}
\]
\[ v=\frac{S2·D}{E_1·S1}
\]

总结

我们在最后可以通过已知的数据,求出t、u、v三个参数,通过三个参数的范围限制判断是否相交,如果不满足则不相交,如果满足则相交

代码实现

    // Vector3 a b c triangle vertexs
// orig is ray original point, dir is direction vector
bool rayTriangleIntersect(Vector3 orig, Vector3 dir,
Vector3 a, Vector3 b, Vector3 c, float t, float b1, float b2)
{
bool isIn = false;
Vector3 E1 = b - a;
Vector3 E2 = c - a;
Vector3 S = orig - a;
Vector3 S1 = Vector3.Cross(dir, E2);
Vector3 S2 = Vector3.Cross(S, E1); // 共同系数
float coeff = 1.0f / Vector3.Dot(S1, E1);
t = coeff * Vector3.Dot(S2, E2);
b1 = coeff * Vector3.Dot(S1, S);
b2 = coeff * Vector3.Dot(S2, dir); Debug.Log($"t = {t}, b1 = {b1}, b2 = {b2}"); if (t >= 0 && b1 >= 0 && b2 >= 0 && (1 - b1 - b2) >= 0)
{
isIn = true;
} return isIn;
}

Unity中的演示效果

当射线与三角形相交时,三角形的材质会变成红色,射线是采用LineRenderer的形式,三角形用了编辑材质顶点的脚本,过几天会分享出来~

项目地址:https://github.com/shadow-lr/RayTriangleIntersect

题外话

虽然射线和三角形的相交检测可以用来实现拾取(Picking),但是大多数程序并不采用这个方法,原因是这个方法效率很低,我们可以设想,一个大型的3D游戏,某个模型的三角形数量很可能是百万级的,在此情况下,对模型上的每个三角形求交是一件极其耗费时间的事情。

所以一般可行的方法是,用包围球和包围盒(AABB、OBB、FDH)来代替,计算出能容纳模型的最小球体或者举行提,只要判断射线与包围球或者包围盒求交即可,只是精确度上有一定误差,但是足以满足多数程序的需要。

射线与空间内三角形的相交检测算法(Möller-Trumbore)的推导与实践的更多相关文章

  1. 射线和三角形的相交检测(ray triangle intersection test)【转】

    本文以Fast, Minimum Storage Ray Triangle Intersection为参考,在此感谢原作者,大家也可以直接阅读原版. 概述 射线和三角形的相交检测是游戏程序设计中一个常 ...

  2. 3D空间中射线与三角形的交叉检测算法【转】

    引言 射线Ray,在3D图形学中有很多重要的应用.比如,pick操作就是使用射线Ray来实现的,还有诸如子弹射线的碰撞检测等等都可以使用射线Ray来完成.所以,在本次博客中,将会简单的像大家介绍下,如 ...

  3. 3D空间中射线与轴向包围盒AABB的交叉检测算法【转】

    引言 在上一节中,我讲述了如何实现射线与三角形的交叉检测算法.但是,我们应该知道,在游戏开发中,一个模型有很多的三角形构成,如果要对所有的物体,所有的三角形进行这种检测,就算现在的计算机运算能力,也是 ...

  4. 3D空间中射线与轴向包围盒AABB的交叉检测算法 【转】

    http://blog.csdn.net/i_dovelemon/article/details/38342739 引言 在上一节中,我讲述了如何实现射线与三角形的交叉检测算法. 但是,我们应该知道, ...

  5. 求空间内两条直线的最近距离以及最近点的坐标(C++)

    关键词:空间几何 用途:总有地方会用到吧 文章类型:C++函数展示 @Author:VShawn(singlex@foxmail.com) @Date:2016-11-19 @Lab: CvLab20 ...

  6. hihoCoder-1633 ACM-ICPC北京赛区2017 G.Liaoning Ship’s Voyage 线段与三角形规范相交

    题面 题意:给你一个20*20的地图,起点(0,0),终点(n-1,n-1),有障碍的点为‘#’,每次可以向8个方向走一步,还给了一个三角形,除了障碍以外,到这8个方向上的点的线段如果没有与三角形相交 ...

  7. Ray-AABB交叉检测算法

      最近在解决三维问题时,需要判断线段是否与立方体交叉,这个问题可以引申为:射线是否穿过立方体AABB.   在3D游戏开发中碰撞检测普遍采用的算法是轴对齐矩形边界框(Axially Aligned ...

  8. [转]前景检测算法--ViBe算法

    原文:http://blog.csdn.net/zouxy09/article/details/9622285 转自:http://blog.csdn.net/app_12062011/article ...

  9. 【深度学习】目标检测算法总结(R-CNN、Fast R-CNN、Faster R-CNN、FPN、YOLO、SSD、RetinaNet)

    目标检测是很多计算机视觉任务的基础,不论我们需要实现图像与文字的交互还是需要识别精细类别,它都提供了可靠的信息.本文对目标检测进行了整体回顾,第一部分从RCNN开始介绍基于候选区域的目标检测器,包括F ...

随机推荐

  1. 《逆向工程核心原理》——API HOOK

    编写dll处理hook逻辑,注入到目标进程,实现api hook. Windows10 notepad,通过hook kernel32.dll.WriteFile,实现小写字母转大写保存到文件. ho ...

  2. Python基础之异常定义

    技术背景 在各类python的项目中,总会涉及到项目自身相关的一些约束条件.这些约束条件体现在,当用户输入的参数或者文件不符合项目要求时,就拒绝这个参数的输入并且播报出来,提醒用户自行修改,而这一过程 ...

  3. redis数据库如何批量删除键和设置过期时间?

    我们可以借助Linux中的xargs,在终端中执行命令来实现这两个功能. 一.批量删除键 批量删除以"key"开头key的方法,需要借助Linux中的xargs,在终端中执行以下命 ...

  4. java例题_01 不死神兔!

    1 /*1 [程序 1 不死神兔] 2 题目:古典问题:有一对兔子,从出生后第 3 个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少? 3 程 ...

  5. 全网最详细的Linux命令系列-nl命令

    nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等 ...

  6. Simulink中Scope数据保存至Workspace制图

    0 问题 通常情况下,仿真模型中scope波形可编辑程度并不高,尽管高版本MATLAB中已经可以将其直接导出到figure,但效果并不是特别理想.在需要高质量输出波形图场合,就需要将其中数据导出到wo ...

  7. C语言利用for循环打印菱形

    C语言利用for循环打印菱形(高度为奇数) 这次用的方法是上下部分分开打印,先打印上部分,再打印下部分. 先举个简单的例子打印,再改进代码,登堂入室从而理解. 例:打印一个高度(高度必须为奇数)为 5 ...

  8. Ambassador-08-跨域

    官方文档:https://www.getambassador.io/docs/latest/topics/using/cors/ Cross-Origin Resource Sharing-CORS ...

  9. SQL Server 审计(Audit)

    审计(Audit)用于追踪和记录SQL Server实例,或者单个数据库中发生的事件(Event),审计运作的机制是通过捕获事件(Event),把事件包含的信息写入到事件日志(Event Log)或审 ...

  10. Innodb中的快照读和当前读

    一.前言 上篇文章记录了对MVCC的相关理解,其中有提到快照读.其实在MVCC并发控制中,读操作可以分为两类:快照读(snapshot read)和当前读(current read)   二.什么是快 ...