我在3D空间中有一条直线和一个三角形,换句话说,三角形有3个点(每个点为[x,y,z]),直线有2个点(也是[x,y,z])。
我需要找到一种方法,希望使用C++,来计算出这条线是否与三角形相交。一条平行于三角形的线,并且有多个公共点,应该算作“不相交”。
我已经编写了一些代码,但它不起作用,而且即使在可视化表示清楚地显示交叉点时,我也总是得到false。
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;
}
4条答案
按热度按时间9udxz4iz1#
设
p1,p2,p3
表示三角形在两个方向上都非常远的直线上选择两个点
q1,q2
。设
SignedVolume(a,b,c,d)
表示四面体a,b,c,d的有符号体积。如果
SignedVolume(q1,p1,p2,p3)
和SignedVolume(q2,p1,p2,p3)
具有不同的符号,并且SignedVolume(q1,q2,p1,p2)
、SignedVolume(q1,q2,p2,p3)
和SignedVolume(q1,q2,p3,p1)
具有相同的符号,则存在交集。以参数形式写出直线方程:
p(t) = q1 + t*(q2-q1)
写出平面的方程:
dot(p-p1,N) = 0
其中将
p(t)
代入平面方程:x1米11米1x展开:
dot(q1-p1,N) + t dot(q2-q1,N) = 0
推导
t = -dot(q1-p1,N)/dot(q2-q1,N)
交点为
q1 + t*(q2-q1)
我们现在研究算法:
Möller和Trumbore,"快速、最小存储的光线-三角形相交",图形工具杂志,第2卷,1997年,第21 - 28页(另请参见:https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%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|第二代|- D]是列为E1、E2、-D的3x3矩阵)
使用Cramer公式求解:
给出:
现在我们得到:
其中(A,B,C)表示以A,B,C作为其列向量的3x3矩阵的行列式。
现在我们使用以下标识:
现在我们得到:
使用:
我们最终获得以下代码(此处为GLSL,易于翻译为其他语言):
当函数返回
true
时,交点由R.Origin + t * R.Dir
给出。三角形中交点的重心坐标为u
,v
,1-u-v
(对Gouraud着色或纹理Map很有用)。好的是你可以免费得到它们!注意代码是无分支的。它被我的一些着色器在ShaderToy上使用
sdnqo3pr2#
@BrunoLevi:你的算法好像不起作用,看下面的python实现:
我的测试代码是:
给出:
而不是预期的
看着这条线
从法线减去p1对我来说没有意义,你想从q1投影到三角形的平面上,所以你需要沿着法线投影,投影距离正比于q1到平面的距离和q1-q2沿着法线的距离的比值,对吗?
以下代码修复了此问题:
ltqd579y3#
若要在3D中查找直线和三角形之间的交点,请遵循以下方法:
下面是一些示例代码,其中包含应该可以工作的详细计算:
对随问题发布的代码的一些评论:
ofVec3f
的预定义运算(几何积的.dot()
和.cross()
等)应优先使用(可读性更强、避免实现错误等),t0ybt7op4#
我有一个不同的方法来做这件事,我发现在我的渲染器是远远快于第一种方式由BrunoLevy。(我还没有实现第二种方式)
点A、B、C是三角形的顶点
O是射线的原点
D是射线的方向(不需要规格化,只是比三角形更靠近原点)
检查方向(D+O)是否在四面体A、B、C、O内部
如果D变化,而其他一切保持不变(例如在光线投射渲染器中),那么normal和dotP不需要重新计算;这就是为什么我发现它这么快
代码来自此答案https://stackoverflow.com/a/25180294/18244401