以下是解决方案的概述:
计算正方形的平面方程(假设四个点共面),
做一个射线/平面相交,这给你任何东西(平行于正方形的射线,我忽略了射线嵌入平面的情况)或一个点,
获得交点后,将其投影在正方形平面上的局部 2D 基础上,这将给出平面上点的 2D 坐标 (u, v),
检查 2D 坐标 (u, v) 是否在正方形内(假设四个点形成一个平行四边形,并且您为局部 2D 基础选择了两个相邻边),如果是,则存在交叉点(并且您有 u/v 坐标)。
现在有了实际的方程,假设四个正方形顶点如下放置:
S1 +------+ S2
| |
| |
S3 +------+ S4
平面的法线为:n = (S2 - S1) x (S3 - S1)
一个点 M 属于这个平面,如果它满足这个方程: n 。( M - S1 ) = 0
一个点 M 属于射线当且仅当它可以写成: M = R1 + t * dR 其中 dR = R2 - R1
计算射线/平面的交点(等同于前面的两个方程):
ñ。(M-S1)=0=n。(R1 + t * dR - S1) = n。(R1 - S1) + t * n 。dR
如果 n 。dR 为 0 则平面平行于射线,并且没有相交(同样,忽略射线嵌入平面的情况)。
否则 t = -n 。(R1 - S1) / n 。dR 并将这个结果代入前面的方程 M = R1 + t * dR 给出了交点 M 的 3D 坐标。
将向量 M - S1 投影到两个向量 S2 - S1 和 S3 - S1(从 S1 开始的方形边),这给出了两个数字 (u, v):
u = (M - S1) 。(S2 - S1)
v = (M - S1) 。(S3 - S1)
如果 0 <= u <= |S2 - S1|^2 和 0 <= v <= |S3 - S1|^2,则交点 M 在正方形内,否则在正方形外。
最后是前面等式的示例 Java 实现(为便于阅读而优化......):
public class Test {
static class Vector3 {
public float x, y, z;
public Vector3(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3 add(Vector3 other) {
return new Vector3(x + other.x, y + other.y, z + other.z);
}
public Vector3 sub(Vector3 other) {
return new Vector3(x - other.x, y - other.y, z - other.z);
}
public Vector3 scale(float f) {
return new Vector3(x * f, y * f, z * f);
}
public Vector3 cross(Vector3 other) {
return new Vector3(y * other.z - z * other.y,
z - other.x - x * other.z,
x - other.y - y * other.x);
}
public float dot(Vector3 other) {
return x * other.x + y * other.y + z * other.z;
}
}
public static boolean intersectRayWithSquare(Vector3 R1, Vector3 R2,
Vector3 S1, Vector3 S2, Vector3 S3) {
// 1.
Vector3 dS21 = S2.sub(S1);
Vector3 dS31 = S3.sub(S1);
Vector3 n = dS21.cross(dS31);
// 2.
Vector3 dR = R1.sub(R2);
float ndotdR = n.dot(dR);
if (Math.abs(ndotdR) < 1e-6f) { // Choose your tolerance
return false;
}
float t = -n.dot(R1.sub(S1)) / ndotdR;
Vector3 M = R1.add(dR.scale(t));
// 3.
Vector3 dMS1 = M.sub(S1);
float u = dMS1.dot(dS21);
float v = dMS1.dot(dS31);
// 4.
return (u >= 0.0f && u <= dS21.dot(dS21)
&& v >= 0.0f && v <= dS31.dot(dS31));
}
public static void main(String... args) {
Vector3 R1 = new Vector3(0.0f, 0.0f, -1.0f);
Vector3 R2 = new Vector3(0.0f, 0.0f, 1.0f);
Vector3 S1 = new Vector3(-1.0f, 1.0f, 0.0f);
Vector3 S2 = new Vector3( 1.0f, 1.0f, 0.0f);
Vector3 S3 = new Vector3(-1.0f,-1.0f, 0.0f);
boolean b = intersectRayWithSquare(R1, R2, S1, S2, S3);
assert b;
R1 = new Vector3(1.5f, 1.5f, -1.0f);
R2 = new Vector3(1.5f, 1.5f, 1.0f);
b = intersectRayWithSquare(R1, R2, S1, S2, S3);
assert !b;
}
}