射线和段交叉点

我正在写一些代码来检查所有的矩形段和射线之间的交集。 即使当它明显位于矩形的顶部(面AB)时,它有时也会使用矩形的左侧和右侧作为最接近的面击中(面DA和BC)。

这是Rectangle v Ray碰撞的样子。

// Ray intersections static public IntersectData IntersectRectRay(Rectanglef rect, Vector2 origin, float dir) { IntersectData data = new IntersectData(); // Create segments for all the sides of a rectangle Segment topLeftRight = new Segment(rect.TopLeft, rect.TopRight); Segment leftTopBottom = new Segment(rect.TopLeft, rect.BottomLeft); Segment bottomLeftRight = new Segment(rect.BottomLeft, rect.BottomRight); Segment rightTopBottom = new Segment(rect.TopRight, rect.BottomRight); // Check collision with all the faces and use the closet one float smallestDepth = float.PositiveInfinity; IntersectData topLeftRightData = IntersectSegmentRay(topLeftRight, origin, dir); if (topLeftRightData.intersecting && topLeftRightData.depth.Length() < smallestDepth) { smallestDepth = topLeftRightData.depth.Length(); data = topLeftRightData; data.face = "AB"; data.dir = MathHelper.PI / 2f; } IntersectData leftTopBottomData = IntersectSegmentRay(leftTopBottom, origin, dir); if (leftTopBottomData.intersecting && leftTopBottomData.depth.Length() < smallestDepth) { smallestDepth = leftTopBottomData.depth.Length(); data = leftTopBottomData; data.face = "DA"; data.dir = 0f; } IntersectData bottomLeftRightData = IntersectSegmentRay(bottomLeftRight, origin, dir); if (bottomLeftRightData.intersecting && bottomLeftRightData.depth.Length() < smallestDepth) { smallestDepth = bottomLeftRightData.depth.Length(); data = bottomLeftRightData; data.face = "CD"; data.dir = MathHelper.PI / 2f; } IntersectData rightTopBottomData = IntersectSegmentRay(rightTopBottom, origin, dir); if (rightTopBottomData.intersecting && rightTopBottomData.depth.Length() < smallestDepth) { data = rightTopBottomData; data.face = "BC"; data.dir = 0f; } return data; } 

这是射线v段交叉

  static public IntersectData IntersectSegmentRay(Segment seg, Vector2 origin, float dir) { IntersectData data = new IntersectData(); // Get vector between p1 and p2 Vector2 q = seg.p1; Vector2 s = seg.p2 - seg.p1; Vector2 p = origin; Vector2 r = new Vector2((float)Math.Cos(dir), (float)Math.Sin(dir)) * 100000f; float t = MathHelper.Vec2CrossProduct(q - p, s) / MathHelper.Vec2CrossProduct(r, s); float u = MathHelper.Vec2CrossProduct(q - p, r) / MathHelper.Vec2CrossProduct(r, s); if (MathHelper.Vec2CrossProduct(r, s) != 0 && t <= 1 && t >= 0 && u <= 1 && u >= 0) { data.intersecting = true; data.depth = p + t * r;//q + u * s; data.dir = dir; } return data; } 

额外信息:我的Vec2CrossProduct是V×W是Vx Wy – Vy Wx

我想像射线段交叉点有问题,因为如果我试图用float.PositiveInfinity()replace那个100000f,它将不起作用。

那么简单的光线与线段的交集就是:

 function RayToLineSegment(x, y, dx, dy, x1, y1, x2, y2) { var r, s, d; //Make sure the lines aren't parallel, can use an epsilon here instead // Division by zero in C# at run-time is infinity. In JS it's NaN if (dy / dx != (y2 - y1) / (x2 - x1)) { d = ((dx * (y2 - y1)) - dy * (x2 - x1)); if (d != 0) { r = (((y - y1) * (x2 - x1)) - (x - x1) * (y2 - y1)) / d; s = (((y - y1) * dx) - (x - x1) * dy) / d; if (r >= 0 && s >= 0 && s <= 1) { return { x: x + r * dx, y: y + r * dy }; } } } return null; } 

也就是说,由于线段都是水平的或垂直的,因此可以在不进行正常的光线对线段检查的情况下完成矩形或轴alignment边界框(AABB)的光线,最终得到一个非常简单的algorithm: 最有效的AABB对射线碰撞algorithm