射线界平面相交
Ray-bounded plane intersection
我正在尝试在空闲时间编写光线追踪器。目前正在尝试做射线 - 有界平面相交。
我的程序已经在处理无限平面了。我正在尝试计算非无限平面的数学。尝试 google,但所有资源都只讨论无限平面。
我的平面有一个角点(称为位置),两个向量(u 和 v)从该点延伸(它们的长度对应于边的长度)。光线有起点和方向。
首先我用公式
计算与无限平面的交点
t = normal * (position - origin) / (normal * direction)
法线计算为 u 和 v 的叉积。
然后用公式
origin + direction * t
我得到交点本身。
下一步是检查这个点是否在矩形的边界内,这就是我遇到问题的地方。
我的想法是获取从平面角延伸到交点的相对向量intersection - position
,然后将其转换为新的u、法线和v基础,然后检查长度是否的转换向量比 u 和 v 向量短。
bool BoundedPlane::intersect(const Vec3f &origin, const Vec3f &direction, float &t) const {
t = normal * (position - origin) / (normal * direction);
Vec3f relative = (origin + direction * t) - position;
Mat3f transform{
Vec3f(u.x, normal.x, v.x),
Vec3f(u.y, normal.y, v.y),
Vec3f(u.z, normal.z, v.z)
};
Vec3f local = transform.mul(relative);
return t > 0 && local.x >= 0 && local.x <= u.x && local.z <= 0 && local.z <= v.z;
}
最后我检查 t 是否大于 0,这意味着交点在相机前面,以及向量的长度是否在边界内。这给了我一条奇怪的线:
。
平面应该像这样出现在球体下方:
(如果数字正确,这使用手动检查以查看它是否正确显示)。
我不确定我做错了什么,以及是否有更简单的方法来检查边界。提前致谢。
编辑1:
我将变换矩阵计算移到了构造函数中,所以现在交集测试是:
bool BoundedPlane::intersect(const Vec3f &origin, const Vec3f &direction, float &t) const {
if (!InfinitePlane::intersect(origin, direction, t)) {
return false;
}
Vec3f local = transform.mul((origin + direction * t) - position);
return local.x >= 0 && local.x <= 1 && local.z >= 0 && local.z <= 1;
}
变换成员是变换矩阵的逆。
我可以建议另一种方法吗?考虑有原点的框架
position
和基础向量
u = { u.x, u.y, u.z }
v = { v.x, v.y, v.z }
direction = { direction.x, direction.y, direction.z}
第一步:形成矩阵
M = {
{u.x, v.x, direction.x},
{u.y, v.y, direction.y},
{u.z, v.z, direction.z}
}
第 2 步: 计算向量 w
,它是 3 x 3 线性方程组
的解
M * w = origin - position
,即
w = inverse(M) * (origin - position);
确保direction
与u, v
不共面,否则无交集,inverse(M)
不存在。
步骤 3: 如果 0.0 <= w.x && w.x <= 1.0 && 0.0 <= w.y && w.y <= 1.0
则直线与向量 u, v
所张成的平行四边形相交,交点为
w0 = { w.x, w.y , 0 };
intersection = position + M * w0;
否则,直线不与向量 u, v
所张成的平行四边形相交
这个算法的思路是考虑(non-orthonormal)帧position, u, v, direction
。然后矩阵 M
改变了这个新帧坐标中的所有内容。在这个框架中,线是垂直的,平行于 "z-" 轴,点 origin
的坐标是 w
,通过 w
的垂直线与平面相交于 w0
。
编辑 1: 这是 3x3 矩阵逆的模板公式:
若原始矩阵M为
a b c
d e f
g h i
逆是
(1 / det(M)) * {
{e*i - f*h, c*h - b*i, b*f - c*e},
{f*g - d*i, a*i - c*g, c*d - a*f},
{d*h - e*g, b*g - a*h, a*e - b*d},
}
其中
det(M) = a*(e*i - f*h) + b*(f*g - d*i) + c*(d*h - e*h)
是M的行列式
所以反演算法可以如下:
给定
M = {
{a, b, c},
{d, e, f},
{g, h, i},
}
- 计算
inv_M = {
{e*i - f*h, c*h - b*i, b*f - c*e},
{f*g - d*i, a*i - c*g, c*d - a*f},
{d*h - e*g, b*g - a*h, a*e - b*d},
};
- 计算
det_M = a*inv_M[1][1] + b*inv_M[2][1] + c*inv_M[3][1];
- Return M的逆矩阵
inv_M = (1/det_M) * inv_M;
编辑 2: 让我们尝试另一种方法来加快速度。
第 1 步: 对于由点 position
和两个向量 u
和 v
确定的每个平面,预先计算以下内容数量:
normal = cross(u, v);
u_dot_u = dot(u, u);
u_dot_v = dot(u, v);
v_dot_v = dot(v, v); // all these need to be computed only once for the u and v vectors
det = u_dot_u * v_dot_v - u_dot_v * u_dot_v; // again only once per u and v
第 2 步: 现在,对于给定的点 origin
和方向 direction
的线,和以前一样,计算交点 int_point
平面由 u
和 v
:
t = dot(normal, position - origin) / dot(normal, direction);
int_point = origin + t * direction;
rhs = int_point - position;
步骤 3: 计算器
u_dot_rhs = dot(u, rhs);
v_dot_rhs = dot(v, rhs);
w1 = (v_dot_v * u_dot_rhs - u_dot_v * v_dot_rhs) / det;
w2 = (- u_dot_v * u_dot_rhs + u_dot_u * v_dot_rhs) / det;
第 4 步:
if (0 < = w1 && w1 <= 1 && 0 < = w2 && w2 <= 1 ){
int_point is in the parallelogram;
}
else{
int_point is not in the parallelogram;
}
所以我在这里所做的基本上是找到线 origin, direction
与 position, u, v
给出的平面的交点,并将自己限制在平面内,这让我可以在 2D 中工作,而不是比3D。我代表
int_point = position + w1 * u + w2 * v;
rhs = int_point - position = w1 * u + w2 * v
并通过 dot-multiplying 使用基向量 u
和 v
找到 w1
和 w2
,这导致 2x2 线性系统,我直接解决。
我正在尝试在空闲时间编写光线追踪器。目前正在尝试做射线 - 有界平面相交。
我的程序已经在处理无限平面了。我正在尝试计算非无限平面的数学。尝试 google,但所有资源都只讨论无限平面。
我的平面有一个角点(称为位置),两个向量(u 和 v)从该点延伸(它们的长度对应于边的长度)。光线有起点和方向。
首先我用公式
计算与无限平面的交点t = normal * (position - origin) / (normal * direction)
法线计算为 u 和 v 的叉积。 然后用公式
origin + direction * t
我得到交点本身。
下一步是检查这个点是否在矩形的边界内,这就是我遇到问题的地方。
我的想法是获取从平面角延伸到交点的相对向量intersection - position
,然后将其转换为新的u、法线和v基础,然后检查长度是否的转换向量比 u 和 v 向量短。
bool BoundedPlane::intersect(const Vec3f &origin, const Vec3f &direction, float &t) const {
t = normal * (position - origin) / (normal * direction);
Vec3f relative = (origin + direction * t) - position;
Mat3f transform{
Vec3f(u.x, normal.x, v.x),
Vec3f(u.y, normal.y, v.y),
Vec3f(u.z, normal.z, v.z)
};
Vec3f local = transform.mul(relative);
return t > 0 && local.x >= 0 && local.x <= u.x && local.z <= 0 && local.z <= v.z;
}
最后我检查 t 是否大于 0,这意味着交点在相机前面,以及向量的长度是否在边界内。这给了我一条奇怪的线:
平面应该像这样出现在球体下方:
(如果数字正确,这使用手动检查以查看它是否正确显示)。
我不确定我做错了什么,以及是否有更简单的方法来检查边界。提前致谢。
编辑1:
我将变换矩阵计算移到了构造函数中,所以现在交集测试是:
bool BoundedPlane::intersect(const Vec3f &origin, const Vec3f &direction, float &t) const {
if (!InfinitePlane::intersect(origin, direction, t)) {
return false;
}
Vec3f local = transform.mul((origin + direction * t) - position);
return local.x >= 0 && local.x <= 1 && local.z >= 0 && local.z <= 1;
}
变换成员是变换矩阵的逆。
我可以建议另一种方法吗?考虑有原点的框架
position
和基础向量
u = { u.x, u.y, u.z }
v = { v.x, v.y, v.z }
direction = { direction.x, direction.y, direction.z}
第一步:形成矩阵
M = {
{u.x, v.x, direction.x},
{u.y, v.y, direction.y},
{u.z, v.z, direction.z}
}
第 2 步: 计算向量 w
,它是 3 x 3 线性方程组
M * w = origin - position
,即
w = inverse(M) * (origin - position);
确保direction
与u, v
不共面,否则无交集,inverse(M)
不存在。
步骤 3: 如果 0.0 <= w.x && w.x <= 1.0 && 0.0 <= w.y && w.y <= 1.0
则直线与向量 u, v
所张成的平行四边形相交,交点为
w0 = { w.x, w.y , 0 };
intersection = position + M * w0;
否则,直线不与向量 u, v
这个算法的思路是考虑(non-orthonormal)帧position, u, v, direction
。然后矩阵 M
改变了这个新帧坐标中的所有内容。在这个框架中,线是垂直的,平行于 "z-" 轴,点 origin
的坐标是 w
,通过 w
的垂直线与平面相交于 w0
。
编辑 1: 这是 3x3 矩阵逆的模板公式:
若原始矩阵M为
a b c
d e f
g h i
逆是
(1 / det(M)) * {
{e*i - f*h, c*h - b*i, b*f - c*e},
{f*g - d*i, a*i - c*g, c*d - a*f},
{d*h - e*g, b*g - a*h, a*e - b*d},
}
其中
det(M) = a*(e*i - f*h) + b*(f*g - d*i) + c*(d*h - e*h)
是M的行列式
所以反演算法可以如下:
给定
M = {
{a, b, c},
{d, e, f},
{g, h, i},
}
- 计算
inv_M = {
{e*i - f*h, c*h - b*i, b*f - c*e},
{f*g - d*i, a*i - c*g, c*d - a*f},
{d*h - e*g, b*g - a*h, a*e - b*d},
};
- 计算
det_M = a*inv_M[1][1] + b*inv_M[2][1] + c*inv_M[3][1];
- Return M的逆矩阵
inv_M = (1/det_M) * inv_M;
编辑 2: 让我们尝试另一种方法来加快速度。
第 1 步: 对于由点 position
和两个向量 u
和 v
确定的每个平面,预先计算以下内容数量:
normal = cross(u, v);
u_dot_u = dot(u, u);
u_dot_v = dot(u, v);
v_dot_v = dot(v, v); // all these need to be computed only once for the u and v vectors
det = u_dot_u * v_dot_v - u_dot_v * u_dot_v; // again only once per u and v
第 2 步: 现在,对于给定的点 origin
和方向 direction
的线,和以前一样,计算交点 int_point
平面由 u
和 v
:
t = dot(normal, position - origin) / dot(normal, direction);
int_point = origin + t * direction;
rhs = int_point - position;
步骤 3: 计算器
u_dot_rhs = dot(u, rhs);
v_dot_rhs = dot(v, rhs);
w1 = (v_dot_v * u_dot_rhs - u_dot_v * v_dot_rhs) / det;
w2 = (- u_dot_v * u_dot_rhs + u_dot_u * v_dot_rhs) / det;
第 4 步:
if (0 < = w1 && w1 <= 1 && 0 < = w2 && w2 <= 1 ){
int_point is in the parallelogram;
}
else{
int_point is not in the parallelogram;
}
所以我在这里所做的基本上是找到线 origin, direction
与 position, u, v
给出的平面的交点,并将自己限制在平面内,这让我可以在 2D 中工作,而不是比3D。我代表
int_point = position + w1 * u + w2 * v;
rhs = int_point - position = w1 * u + w2 * v
并通过 dot-multiplying 使用基向量 u
和 v
找到 w1
和 w2
,这导致 2x2 线性系统,我直接解决。