C++ 中射线和矩形的交集
Intersection of Ray and Rectangle in C++
我需要射线和矩形之间的交集。到目前为止,我已经遵循了答案here,但在测试之后(如下所述)我意识到实现是错误的。
bool checkRayLightIntersection(Vec Origin, Vec Dir) {
//(-10,20,9) is Hard-code of the light position and we add (5.0f) on X and Z axis to
//make it an area instead of a vertex
//"Origin" is the position of the eye. Hard coded: (-25, 8, 5)
//"Dir" is LightPosition - Origin
float randX = 5.0f; //in the future it will be random, that's why this name
float randZ = 5.0f;
Vec P1 = Vec(-10 - randX, 20, 9 + randZ);
Vec P2 = Vec(-10 + randX, 20, 9 + randZ);
Vec P3 = Vec(-10 + randX, 20, 9 - randZ);
Vec P4 = Vec(-10 - randX, 20, 9 - randZ);
//the majority of the methods first find out where the ray intersects the
//plane that the rectangle lies on, Ax + By + Cz + D = 0
//in our case the equation of that plane is easy (I think) -> D = 20
float t = -(-Origin.y + 20) / (-Dir.y);
if (t > 0) {
Vec hitPoint = Origin + Dir * t;
Vec V1 = (P2 - P1).norm();
Vec V3 = (P4 - P3).norm();
Vec V4 = (hitPoint - P1).norm();
Vec V5 = (hitPoint - P3).norm();
float V1dotV4 = V1.dot(V4);
float V3dotV5 = V3.dot(V5);
if (V1dotV4 > 0 && V3dotV5 > 0) {
return true;
}
}
return false;
}
我的 Vec 是一个结构体,定义如下:
struct Vec {
double x, y, z; // position, also color (r,g,b)
Vec(double x_ = 0, double y_ = 0, double z_ = 0){ x = x_; y = y_; z = z_; }
Vec operator+(const Vec &b) const { return Vec(x + b.x, y + b.y, z + b.z); }
Vec operator-(const Vec &b) const { return Vec(x - b.x, y - b.y, z - b.z); }
Vec operator-() const { return Vec(-x, -y, -z); }
Vec operator*(double b) const { return Vec(x*b, y*b, z*b); }
Vec operator/(double b) const { return Vec(x / b, y / b, z / b); }
Vec mult(const Vec &b) const { return Vec(x*b.x, y*b.y, z*b.z); }
Vec& norm(){ return *this = *this * (1 / sqrtf(x*x + y*y + z*z)); }
double dot(const Vec &b) const { return x*b.x + y*b.y + z*b.z; }
// cross:
Vec operator%(Vec&b){ return Vec(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); }
double max() const { return x>y && x>z ? x : y > z ? y : z; }
};
我测试了这个方法。所以我试图做的是从我的原点创建一条射线到应该在矩形外部的点,例如 (-10, 20, 19),我在 Z 轴上添加了 9,而矩形应该只大 5 个单位在每个方向 (X,-X,Z,-Z)。因此在我的例子中:
Dir = (-10, 20, 19) - Orig
方法 return 正确,而它应该 return 错误。
你能帮我理解我做错了什么吗?提前致谢。
看起来你离这里很近了,但我很好奇 V1dotV4
和 V3dotV5
.
你得到了什么结果
就是说,看起来您正在正确求解 t
,因为您的平面方程式得出 -y + 20 = 0(即 y = 20 处的平面,法线为 [0, -1 , 0]).
您可以通过将其重新代入您的飞机的方程式并检查结果是否为 0 来验证您得到的 hitPoint
是否合理。
假设 hitPoint
在平面上是正确的,看起来你检查 hitPoint 在矩形内是错误的。您正在使用点积来确保 hitPoint
到边 [P1,P2] 和 [P4,P3] 的投影位于该边内。问题是 [P1,P2] 和 [P4,P3] 是矩形的 opposite/parallel 边,因此您的检查不会告诉您 hitPoint
相对于边 [P2,P3] 的位置] 和 [P4,P1].
既然你知道这是一个矩形,我想计算应该足够了
Vec hitPoint = Origin + Dir * t;
Vec V1 = (P2 - P1).norm();
Vec V2 = (P3 - P2).norm();
Vec V3 = (P4 - P3).norm();
Vec V4 = (P1 - P4).norm();
Vec V5 = (hitPoint - P1).norm();
Vec V6 = (hitPoint - P2).norm();
Vec V7 = (hitPoint - P3).norm();
Vec V8 = (hitPoint - P4).norm();
if (V1.dot(V5) < 0.0) return false;
if (V2.dot(V6) < 0.0) return false;
if (V3.dot(V7) < 0.0) return false;
if (V4.dot(V8) < 0.0) return false;
return true;
Edit 我最初的断言是检查两个边的范围 [0,1],但这实际上是不正确的。更新了示例代码以检查所有 4 个边。
另一个注意事项 您可以通过
对照矩形的两个垂直边检查 hitPoint
来做到这一点
Vec hitPoint = Origin + Dir * t;
Vec V1 = P2 - P1;
float lengthV1 = V1.length();
Vec normV1 = V1.norm();
Vec V2 = P4 - P1;
float lengthV2 = V2.length();
Vec normV2 = V2.norm();
Vec hitVec = P - P1;
a = normV1.dot(hitVec);
b = normV2.dot(hitVec);
return (0.0f <= a && a <= lengthV1 && 0.0f <= b && b <= lengthV2);
我需要射线和矩形之间的交集。到目前为止,我已经遵循了答案here,但在测试之后(如下所述)我意识到实现是错误的。
bool checkRayLightIntersection(Vec Origin, Vec Dir) {
//(-10,20,9) is Hard-code of the light position and we add (5.0f) on X and Z axis to
//make it an area instead of a vertex
//"Origin" is the position of the eye. Hard coded: (-25, 8, 5)
//"Dir" is LightPosition - Origin
float randX = 5.0f; //in the future it will be random, that's why this name
float randZ = 5.0f;
Vec P1 = Vec(-10 - randX, 20, 9 + randZ);
Vec P2 = Vec(-10 + randX, 20, 9 + randZ);
Vec P3 = Vec(-10 + randX, 20, 9 - randZ);
Vec P4 = Vec(-10 - randX, 20, 9 - randZ);
//the majority of the methods first find out where the ray intersects the
//plane that the rectangle lies on, Ax + By + Cz + D = 0
//in our case the equation of that plane is easy (I think) -> D = 20
float t = -(-Origin.y + 20) / (-Dir.y);
if (t > 0) {
Vec hitPoint = Origin + Dir * t;
Vec V1 = (P2 - P1).norm();
Vec V3 = (P4 - P3).norm();
Vec V4 = (hitPoint - P1).norm();
Vec V5 = (hitPoint - P3).norm();
float V1dotV4 = V1.dot(V4);
float V3dotV5 = V3.dot(V5);
if (V1dotV4 > 0 && V3dotV5 > 0) {
return true;
}
}
return false;
}
我的 Vec 是一个结构体,定义如下:
struct Vec {
double x, y, z; // position, also color (r,g,b)
Vec(double x_ = 0, double y_ = 0, double z_ = 0){ x = x_; y = y_; z = z_; }
Vec operator+(const Vec &b) const { return Vec(x + b.x, y + b.y, z + b.z); }
Vec operator-(const Vec &b) const { return Vec(x - b.x, y - b.y, z - b.z); }
Vec operator-() const { return Vec(-x, -y, -z); }
Vec operator*(double b) const { return Vec(x*b, y*b, z*b); }
Vec operator/(double b) const { return Vec(x / b, y / b, z / b); }
Vec mult(const Vec &b) const { return Vec(x*b.x, y*b.y, z*b.z); }
Vec& norm(){ return *this = *this * (1 / sqrtf(x*x + y*y + z*z)); }
double dot(const Vec &b) const { return x*b.x + y*b.y + z*b.z; }
// cross:
Vec operator%(Vec&b){ return Vec(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); }
double max() const { return x>y && x>z ? x : y > z ? y : z; }
};
我测试了这个方法。所以我试图做的是从我的原点创建一条射线到应该在矩形外部的点,例如 (-10, 20, 19),我在 Z 轴上添加了 9,而矩形应该只大 5 个单位在每个方向 (X,-X,Z,-Z)。因此在我的例子中:
Dir = (-10, 20, 19) - Orig
方法 return 正确,而它应该 return 错误。 你能帮我理解我做错了什么吗?提前致谢。
看起来你离这里很近了,但我很好奇 V1dotV4
和 V3dotV5
.
就是说,看起来您正在正确求解 t
,因为您的平面方程式得出 -y + 20 = 0(即 y = 20 处的平面,法线为 [0, -1 , 0]).
您可以通过将其重新代入您的飞机的方程式并检查结果是否为 0 来验证您得到的 hitPoint
是否合理。
假设 hitPoint
在平面上是正确的,看起来你检查 hitPoint 在矩形内是错误的。您正在使用点积来确保 hitPoint
到边 [P1,P2] 和 [P4,P3] 的投影位于该边内。问题是 [P1,P2] 和 [P4,P3] 是矩形的 opposite/parallel 边,因此您的检查不会告诉您 hitPoint
相对于边 [P2,P3] 的位置] 和 [P4,P1].
既然你知道这是一个矩形,我想计算应该足够了
Vec hitPoint = Origin + Dir * t;
Vec V1 = (P2 - P1).norm();
Vec V2 = (P3 - P2).norm();
Vec V3 = (P4 - P3).norm();
Vec V4 = (P1 - P4).norm();
Vec V5 = (hitPoint - P1).norm();
Vec V6 = (hitPoint - P2).norm();
Vec V7 = (hitPoint - P3).norm();
Vec V8 = (hitPoint - P4).norm();
if (V1.dot(V5) < 0.0) return false;
if (V2.dot(V6) < 0.0) return false;
if (V3.dot(V7) < 0.0) return false;
if (V4.dot(V8) < 0.0) return false;
return true;
Edit 我最初的断言是检查两个边的范围 [0,1],但这实际上是不正确的。更新了示例代码以检查所有 4 个边。
另一个注意事项 您可以通过
对照矩形的两个垂直边检查hitPoint
来做到这一点
Vec hitPoint = Origin + Dir * t;
Vec V1 = P2 - P1;
float lengthV1 = V1.length();
Vec normV1 = V1.norm();
Vec V2 = P4 - P1;
float lengthV2 = V2.length();
Vec normV2 = V2.norm();
Vec hitVec = P - P1;
a = normV1.dot(hitVec);
b = normV2.dot(hitVec);
return (0.0f <= a && a <= lengthV1 && 0.0f <= b && b <= lengthV2);