我在 3D 空间的某处有一条线和一个三角形。换句话说,三角形有 3 个点(每个 [x,y,z]),直线有两个点(也有 [x,y,z])。
我需要找出一种方法,希望使用 C++ 来确定这条线是否穿过三角形。一条平行于三角形的线,并且有多个共同点,应该算作“不相交”。
我已经编写了一些代码,但它不起作用,即使视觉表示清楚地显示了交叉点,我也总是会出错。
ofVec3f P1, P2;
P1 = ray.s;
P2 = ray.s + ray.t;
ofVec3f p1, p2, p3;
p1 = face.getVertex(0);
p2 = face.getVertex(1);
p3 = face.getVertex(2);
ofVec3f v1 = p1 - p2;
ofVec3f v2 = p3 - p2;
float a, b, c, d;
a = v1.y * v2.z - v1.z * v2.y;
b = -(v1.x * v2.z - v1.z * v2.x);
c = v1.x * v2.y - v1.y * v2.x;
d = -(a * p1.x + b * p1.y + c * p1.z);
ofVec3f O = P1;
ofVec3f V = P2 - P1;
float t;
t = -(a * O.x + b * O.y + c * O.z + d) / (a * V.x + b * V.y + c * V.z);
ofVec3f p = O + V * t;
float xmin = std::min(P1.x, P2.x);
float ymin = std::min(P1.y, P2.y);
float zmin = std::min(P1.z, P2.z);
float xmax = std::max(P1.x, P2.x);
float ymax = std::max(P1.y, P2.y);
float zmax = std::max(P1.z, P2.z);
if (inside(p, xmin, xmax, ymin, ymax, zmin, zmax)) {
*result = p.length();
return true;
}
return false;
这是 inside() 的定义
bool primitive3d::inside(ofVec3f p, float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) const {
if (p.x >= xmin && p.x <= xmax && p.y >= ymin && p.y <= ymax && p.z >= zmin && p.z <= zmax)
return true;
return false;
}
原文由 Kaito Kid 发布,翻译遵循 CC BY-SA 4.0 许可协议
1)如果您只想知道直线 是否 与三角形相交(不需要实际的交点):
让
p1,p2,p3
表示你的三角形选择两个点
q1,q2
在线上两个方向都很远。让
SignedVolume(a,b,c,d)
表示四面体 a,b,c,d 的有符号体积。If
SignedVolume(q1,p1,p2,p3)
andSignedVolume(q2,p1,p2,p3)
have different signs ANDSignedVolume(q1,q2,p1,p2)
,SignedVolume(q1,q2,p2,p3)
andSignedVolume(q1,q2,p3,p1)
have the same sign, then there是一个路口。2)现在如果你想要交叉点,当1)中的测试通过时
以参数形式写出直线方程:
p(t) = q1 + t*(q2-q1)
写出平面方程:
dot(p-p1,N) = 0
其中将
p(t)
注入平面方程:dot(q1 + t*(q2-q1) - p1, N) = 0
展开:
dot(q1-p1,N) + t dot(q2-q1,N) = 0
t = -dot(q1-p1,N)/dot(q2-q1,N)
交点是
q1 + t*(q2-q1)
3)更高效的算法
我们现在研究算法:
Möller 和 Trumbore,《快速、最小存储射线三角交点》,图形工具杂志,第一卷。 2,1997 年,第21–28
(也可以看看:)
https://en.wikipedia.org/wiki/M%C3%B6ller%E2%8%93Trumbore_intersection_algorithm
该算法最终更简单(比我们在 1)和 2)中所做的指令更少),但理解起来要复杂得多。让我们一步一步推导出来。
符号:
O
= 射线的原点,D
= 光线的方向矢量,A,B,C
= 三角形的顶点射线上的任意一点 P 可以写成
P = O + tD
三角形上的任意点 P 可以写成
P = A + uE1 + vE2
其中E1 = B-A
和E2 = C-A, u>=0, v>=0
和(u+v)<=1
写出 P 的两个表达式给出:
或者:
以矩阵形式:
(其中 [E1|E2|-D] 是以 E1,E2,-D 作为列的 3x3 矩阵)
使用克莱默公式求解:
给出:
现在我们得到:
其中 (A,B,C) 表示以 A,B,C 作为列向量的 3x3 矩阵的行列式。
现在我们使用以下身份:
现在我们得到:
使用:
我们最终得到以下代码(这里是 GLSL,很容易翻译成其他语言):
当函数返回
true
时,交点由R.Origin + t * R.Dir
给出。三角形中交点的重心坐标为u
、v
、1-u-v
(用于 Gouraud 着色或纹理映射)。好消息是您可以免费获得它们!请注意,代码是无分支的。我在 ShaderToy 上的一些着色器使用它