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

射线与空间内三角形的相交检测是游戏程序设计中一个常见的问题,最典型的应用就是拾取(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. 涂鸦基于OAuth2在开发者平台上的探索与实践

    前言 开发授权(OAuth2)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资料(如照片.视频.联系人列表),而无需将用户名和密码提供给第三方应用. OAuth2允许用户提供一 ...

  2. python 序列与字典

    序列概念: 序列的成员有序排列,可以通过下标访问到一个或几个元素,就类似与c语言的数组. 序列的通用的操作: 1:索引 11 = [1,2,3,4] 11[0] = 1 2:切片 11[1,2,3,4 ...

  3. vue文本滚动组件

    看了好多网上的文本组件,发现好多都有这样那样的问题:特别是滚动的时候失真的感觉,今天整合了文本滚动的方式用CSS的 animation写出一套组件:VUE项目直接用.感觉有用的朋友关注下   效果图, ...

  4. 基于开源的GOCW和Directshow.net,实现摄像头预览、采集、录像等操作

    本文基于开源的GOCW和Directshow.net,实现图像采集等操作.最为关键的部分在于可以实现摄像头的控制,同时关于视频采集进行了实现. 具体的内容请关注首发于51CTO的课程<基于Csh ...

  5. 如何配置Nginx,实现http访问重定向到https?

    现在越来越多的网站,当我们输入域名时,会自动重定向到https,我们只需要简单修改下Nginx配置文件/usr/local/nginx/conf/nginx.conf(根据个人的实际存储路径)即可. ...

  6. java例题_44 一个偶数总能表示为两个素数之和

    1 /*44 [程序 44 偶数的素数和] 2 题目:一个偶数总能表示为两个素数之和. 3 */ 4 5 /*分析 6 * 1.从键盘得到一个偶数(大于2的偶数,因为1不是素数) 7 * 2.用for ...

  7. java例题_23 递归求年龄

    1 /*23 [程序 23 求岁数] 2 题目:有 5 个人坐在一起,问第五个人多少岁,他说比第 4 个人大 2 岁.问第 4 个人岁数,他说比第 3 个 3 人大 2 岁.问第三个人,又说比第 2 ...

  8. Linux就该这么学:重定向,管道符,通配符,转义符,环境变量

    第三章:重定向,管道符,环境变量 3.1 输入输出重定向 定义:输入重定向是指将文件导入命令中,输出重定向是指将原本显示到屏幕的信息输出问文件.相较于输入重定向,输出重定向使用的更多,下面重点介绍输出 ...

  9. 【macOS】屏蔽Big Sur更新提示 消除更新红点

    下面介绍如何屏蔽macOS系统更新,屏蔽Big Sur更新提示,消除更新红点. 取消自动更新选项 请选取苹果菜单  > "系统偏好设置",然后点按"软件更新&qu ...

  10. C/C++中的字符串相关姿势

    这是我在<程序设计实习>课程上作的pre,目标是对C/C++中字符串的相关内容作一个尽量完整的介绍.(对于OIer可能不太实用) 课件链接: https://files.cnblogs.c ...