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

射线与空间内三角形的相交检测是游戏程序设计中一个常见的问题,最典型的应用就是拾取(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. ethtool - 命令

    ethtool 导览:     1. 如何查看 Linux 中可用的网卡接口     2. 如何查看 Linux 中网卡信息     3. 如何查看网卡驱动版本以及硬件版本     4. 如何查看网络 ...

  2. Nacos 2.0 正式发布,性能提升 10 倍!!

    3月20号,Nacos 2.0.0 正式发布了! Nacos 简介: 一个更易于构建云原生应用的动态服务发现.配置管理和服务管理平台. 通俗点讲,Nacos 就是一把微服务双刃剑:注册中心 + 配置中 ...

  3. 实验一 Python程序设计入门

    学号20184307 2019-2020-2 <Python程序设计>实验1报告 课程:<Python程序设计> 班级: 1843 姓名: 章森洋 学号:20184307 实验 ...

  4. Java 并发编程 生产者消费者模式

    本文部分摘自<Java 并发编程的艺术> 模式概述 在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的数据.生产者和消费者彼此之间不直接通信,而是通过阻塞队列进行通信,所以生产 ...

  5. shell算数和逻辑运算

    算术运算 Shell允许在某些情况下对算术表达式进行求值,比如:let和declare 内置命令,(( ))复合命令和算术扩 展.求值以固定宽度的整数进行,不检查溢出,尽管除以0 被困并标记为错误.运 ...

  6. C++并发与多线程学习笔记--线程之间调度

    condition_variable wait() notify_one notify_all condition_variable 条件变量的实际用途: 比如有两个线程A和B,在线程A中等待一个条件 ...

  7. HTML(〇):简介导读

    网页 什么是网页 网站(Website):是指在因特网上根据一定的规则,使用HTML(标准通用标记语言)等工具制作的用于展示特定内容相关网页的集合. 网页(webpage):是网站中的一页,通常是HT ...

  8. Spring Boot demo系列(四):Spring Web+Validation

    2021.2.24 更新 1 概述 本文主要讲述了如何使用Hibernate Validator以及@Valid/@Validate注解. 2 校验 对于一个普通的Spring Boot应用,经常可以 ...

  9. Spring/SpringBoot整合QuartZ

    https://www.bilibili.com/video/av55637917/?p=2

  10. 22. VUE 插槽-详解

    插槽 一直对插槽不理解,今天学习,并整理一下,希望日后可以灵活运用. (一)插槽内容 先简单来个例子,看一下插槽的租作用. 1.1 不使用插槽 父组件中: <div id="app&q ...