本文以Fast, Minimum Storage Ray Triangle Intersection为参考,在此感谢原作者,大家也可以直接阅读原版。

概述

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

        

理论部分

一个直观的方法

我想大多数人在看到这个问题时,可能都会想到一个简单而直观的方法:首先判断射线是否与三角形所在的平面相交,如果相交,再判断交点是否在三角形内。

判断射线是否与平面相交

判断点是否在三角形内

但是,上面的方法效率并不很高,因为需要一个额外的计算,那就是计算出三角形所在的平面,而下面要介绍的方法则可以省去这个计算。

本文的方法

接下来会涉及到一些数学知识,不过没关系,我会详细解释每一个步骤,不至于太晦涩,只要您不觉得烦就行了,好了开始!

射线的参数方程如下,其中O是射线的起点,D是射线的方向。

我们可以这样理解射线,一个点从起点O开始,沿着方向D移动任意长度,得到终点R,根据t值的不同,得到的R值也不同,所有这些不同的R值便构成了整条射线,比如下面的射线,起点是P0,方向是u,p0 + tu也就构成了整条射线。

三角形的参数方程如下,其中V0,V1和V2是三角形的三个点,u, v是V1和V2的权重,1-u-v是V0的权重,并且满足u>=0, v >= 0,u+v<=1。

确切的说,上面的方程是三角形及其内部所有点的方程,因为三角形内任意一点都可以理解为从顶点V0开始,沿着边V0V1移动一段距离,然后再沿着边V0V2移动一段距离,然后求他们的和向量。至于移动多大距离,就是由参数u和v控制的。

于是,求射线与三角形的交点也就变成了解下面这个方程-其中t,u,v是未知数,其他都是已知的

移项并整理,将t,u,v提取出来作为未知数,得到下面的线性方程组

现在开始解这个方程组,这里要用到两个知识点,一是克莱姆法则,二是向量的混合积。

令E1 = V1 - V0,E2 = V2 - V0,T = O - V0上式可以改写成

根据克莱姆法则,可得到t,u,v的解分别是

将这三个解联合起来写就是

根据混合积公式

上式可以改写成

得到最终的公式,这便是下面代码中用到的最终公式了,之所以提炼出P和Q是为了避免重复计算

代码部分

理论部分阐述完毕,开始上代码,这份代码来自DirectX SDK中的Demo,名字叫做Picking(拾取),该函数位于文件Pick.cpp的最末尾。这个函数有一个特点,就是判断语句特别多,因为对于一个频繁被调用的函数来说,效率是最重要的,这么多判断就是为了在某个条件不满足时,及时返回,避免后续不必要的计算。

 1 // Determine whether a ray intersect with a triangle
2 // Parameters
3 // orig: origin of the ray
4 // dir: direction of the ray
5 // v0, v1, v2: vertices of triangle
6 // t(out): weight of the intersection for the ray
7 // u(out), v(out): barycentric coordinate of intersection
8
9 bool IntersectTriangle(const Vector3& orig, const Vector3& dir,
10 Vector3& v0, Vector3& v1, Vector3& v2,
11 float* t, float* u, float* v)
12 {
13 // E1
14 Vector3 E1 = v1 - v0;
15
16 // E2
17 Vector3 E2 = v2 - v0;
18
19 // P
20 Vector3 P = dir.Cross(E2);
21
22 // determinant
23 float det = E1.Dot(P);
24
25 // keep det > 0, modify T accordingly
26 Vector3 T;
27 if( det >0 )
28 {
29 T = orig - v0;
30 }
31 else
32 {
33 T = v0 - orig;
34 det = -det;
35 }
36
37 // If determinant is near zero, ray lies in plane of triangle
38 if( det < 0.0001f )
39 return false;
40
41 // Calculate u and make sure u <= 1
42 *u = T.Dot(P);
43 if( *u < 0.0f || *u > det )
44 return false;
45
46 // Q
47 Vector3 Q = T.Cross(E1);
48
49 // Calculate v and make sure u + v <= 1
50 *v = dir.Dot(Q);
51 if( *v < 0.0f || *u + *v > det )
52 return false;
53
54 // Calculate t, scale parameters, ray intersects triangle
55 *t = E2.Dot(Q);
56
57 float fInvDet = 1.0f / det;
58 *t *= fInvDet;
59 *u *= fInvDet;
60 *v *= fInvDet;
61
62 return true;
63 }

参数说明

输入参数:前两个参数orig和dir是射线的起点和方向,中间三个参数v0,v1和v2是三角形的三个顶点。

输出参数:t是交点对应的射线方程中的t值,u,v则是交点的纹理坐标值

代码说明

变量的命名方式:为了方便阅读,代码中的变量命名与上面公式中的变量保持一致,如E1,E2,T等。

变量det表示矩阵的行列式值

27-35行用来确保det>0,如果det<0则令det = -det,并对T做相应的调整,这样做是为了方便后续计算,否则的话需要分别处理det>0和det<0两种情况。

第38行,注意浮点数和0的比较,一般不用 == 0的方式,而是给定一个Epsilon值,并与这个值比较。

第43行,这里实际上u还没有计算完毕,此时的值是Dot(P,T),如果Dot(P,T) > det, 那么u > 1,无交点。

第51行,要确保u + v <= 1,也即 [Dot(P,T) + Dot(Q, D)] / det 必须不能大于1,否则无交点。

第57-60行,走到这里时,表明前面的条件都已经满足,开始计算t, u, v的最终值。

交点坐标

根据上面代码求出的t,u,v的值,交点的最终坐标可以用下面两种方法计算

O + Dt

(1 - u - v)V0 + uV1 + vV2

后记

在本文开头已经说了,射线和三角形的相交检测最典型的应用就是拾取,比如在一个三维场景中用鼠标选择某个物体。那么拾取是如何实现的呢?我们知道在物体的三维模型表示中,三角形是最小的几何图元,最小意味着不可再分,也就是说任何模型,无论它多么复杂,都可以由若干个三角形组合而成。拾取过程实际是判断拾取射线是否与模型相交,而这又可以转化为-只要射线与模型中的任何一个三角形相交即可。下面是模型的线框表示法,可见如果想要判断某条射线是否与这个茶壶相交,只要判断该射线是否与茶壶模型中某个三角形相交即可。

需要注意的是,虽然射线和三角形的相交检测可以用来实现拾取,但是大多数程序并不采用这个方法,原因是这个方法效率很低,我们可以设想,一个大型的3D在线游戏,它的模型数量以及复杂程度都是很高的,如果用这种方法来判断,需要对模型中每个三角形都做一次判断,效率极其低下,一种可行的方案是,用包围球或者包围盒来代替,计算出能容纳模型的最小球体或者矩形体,只要判断出射线与包围球或者包围盒相交,我们就认为射线与模型相交,这样效率会显著提高,只是精确度上会有一定误差,但是足以满足多数程序的需要。

Happy Coding

== The End ==

作者:zdd
出处:http://www.cnblogs.com/graphics/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
 
http://www.cnblogs.com/graphics/archive/2010/08/09/1795348.html

射线和三角形的相交检测(ray triangle intersection test)【转】的更多相关文章

  1. 射线与空间内三角形的相交检测算法(Möller-Trumbore)的推导与实践

    背景介绍(学习算法之前需要先了解) 射线与空间内三角形的相交检测是游戏程序设计中一个常见的问题,最典型的应用就是拾取(Picking),本文介绍一个最常见的方法,这个方法也是DirectX中采用的方法 ...

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

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

  3. 射线与平面的相交检测(Ray-Plane intersection test)【转】

    射线的定义 在欧几里德几何中,射线的定义是:直线上一点和它一旁的部分.由此可知,射线有两个性质,一是只有一个端点,二是一端无限延伸. 射线的参数方程 其中p0是射线的起点, u是射线的方向向量,t & ...

  4. 光线求交-面、三角形、球 (Ray intersection)

    光线求交 光线定义:position \(a(t)\) = \(o\) + \(t\vec{d}\); 球定义: center p, radius r; 平面定义:normal \(\vec{n}\) ...

  5. 3D空间中射线与三角形的交叉检測算法

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

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

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

  7. 用OpenGL绘制平滑着色的三角形与相交区域的混合着色

    一.三角形的绘制 在OpenGL中,面是由多边形构成的.三角形可能是最简单的多边形,它有三条边.可以使用GL_TRIANGLES模式通过把三个顶点连接到一起而绘出三角形. 使用GL_TRIANGLE_ ...

  8. libgdx学习记录27——线段与线段相交检测

    给定p1, p2, p3, p4四个点,p1,p2为一条线段,p3,p4为一条线段,检测其是否有交点. 可分为三种情况: 1. L2与x轴平行 2. L2与y轴平行 3. L2与坐标轴不平行. (L1 ...

  9. hdu1174(3维射线与圆是否相交)

    简单的题意,要注意z2 = h2*0.9-r2 #include <iostream> #include <cmath> #include <vector> #in ...

随机推荐

  1. BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource

    Tomcat报错如下: BeanDefinitionStoreException: IOException parsing XML document from ServletContext resou ...

  2. 第五章 二叉树(e5)重构

  3. Python oct() 函数

    Python oct() 函数  Python 内置函数 描述 oct() 函数将一个整数转换成8进制字符串. 语法 oct 语法: oct(x) 参数说明: x -- 整数. 返回值 返回8进制字符 ...

  4. mysql中float、double、decimal三种类型,以及数值产生误差的原因

    单精度浮点数用4字节(32bit)表示浮点数,采用IEEE754标准的计算机浮点数,在内部是用二进制表示的,如:7.22用32位二进制是表示不下的,所以就导致不精确了,存取会出现误差. mysql中f ...

  5. 一定要 先删除 sc表 中的 某元组 行,,, 再删除 course表中的 元组行

    一定要  先删除 sc表 中的  某元组   行,,, 再删除  course表中的  元组行 course表 SC表 删除  course表中的  元组行,,出现错误 sc    ---->参 ...

  6. Codeforces 677C. Coloring Trees dp

    C. Coloring Trees time limit per test:2 seconds memory limit per test:256 megabytes input:standard i ...

  7. UI设计初学者如何避免走弯路?

    对于初学UI设计的人而言,可能对UI具体是做什么,或者自己是否能顺利转行胜任这样的岗位存在一定的顾虑,今天我们就来重点说说UI是做什么的,以及想学UI到底要如何避免走弯路,快速的学成. 问题一:UI设 ...

  8. Jmeter发送某个request时而成功,时而失败(处理办法:失败的时候尝试重新发送这个HTTP request)

    Jmeter发送某个request时而成功,时而失败 Maybe it’s Jmeter’s problem, after all, is not a commercial software. And ...

  9. 【UXPA大赛企业专访】Mockplus:“设计替代开发”将成为现实

    “过去,是‘设计服务于开发’,现在,我认为是‘设计驱动开发’,而在不远的将来,随着AI的落地.大数据和云计算能力的提升,‘设计替代开发’将成为现实.Mockplus也正在为此部署并行动.” 近日,UX ...

  10. struts2值栈ValueStack中都有哪些东西?

    com.opensymphony.xwork2.dispatcher.HttpServletRequest application com.opensymphony.xwork2.dispatcher ...