You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenGL 2D矩形碰撞时如何选择正确碰撞段计算反射向量

提问内容

我正在开发一个简单的OpenGL项目,目标是实现受力物体与被碰撞物体碰撞后的正确反射效果。我使用形状的线段(segment为由两个点构成的线)进行碰撞检测,以正方形为例,其线段定义如下:

self.segments = [
            Segment2D(self.bottom_left, self.bottom_right),  # Bottom side
            Segment2D(self.bottom_right, self.top_right),  # Right side
            Segment2D(self.top_right, self.top_left),  # Top side
            Segment2D(self.top_left, self.bottom_left)  # Left side
        ]

碰撞检测通过检查物体的任意线段是否与外部物体的线段相交实现。反射计算使用公式 r=d−2(d⋅n)n,各参数定义如下:

  • d : 力向量
  • b : 被碰撞表面向量
  • n : 与被碰撞表面呈90度的归一化(单位向量)法向量
  • r : 力向量的反射向量
    但检测到物体碰撞后,我在计算反射向量时遇到了问题:以两个正方形碰撞为例(我目前仅实现了正方形形状),碰撞检测有时会检测到两个线段同时发生碰撞,我不知道应该选择哪一个作为被碰撞段,导致反射方向有时会出错。

补充说明

我已经实现了Blindman67建议的解决方案,碰撞(选择碰撞边的逻辑)在约80%的场景下生效,但当碰撞位置接近边角时,正方形有时仍会出现反射方向错误的问题,我无法确定这是我自己的代码bug还是方案本身的问题,目前尚未解决该问题。
此外当被碰撞的正方形在两侧都被穿透、被碰撞线段相互平行时,现有检查逻辑也会失效。
我目前的实现逻辑如下:

def rect_rect_collision(self, other, force: Vector2D, segments: List[Segment2D]) -> Segment2D:
        """
        Returns the segment that is the most correct one for collision
        :param other: Colliding RigidBody
        :param force: The force applied on the object
        :param segments: The segments that were detected as colliding by the general detection
        :return: The correct collision segment
        """
        if segments[0].angle() == segments[1].angle():
            # TODO: decide which to collide of the remaining 2
            raise Exception("Lines are parallel")

        # Shared corner of the impacted segments
        common_corner = RigidRect2D.get_common_corner(segments[0], segments[1])
        E: Point2D = self.add_width_height_relative_to_center(common_corner, other.shape.center)
        # Segment 0 is EF
        seg_0_other = segments[0].p1 if segments[0].p2 == common_corner else segments[0].p2
        F: Point2D = self.add_width_height_relative_to_center(seg_0_other, other.shape.center)
        # Segment 1 is EJ
        seg_1_other = segments[1].p1 if segments[1].p2 == common_corner else segments[1].p2
        J: Point2D = self.add_width_height_relative_to_center(seg_1_other, other.shape.center)

        A: Point2D = self.shape.center
        ABx: float = force.x
        ABy: float = force.y
        uu = ABx * (A.y - E.y) - ABy * (A.x - E.x)

        EFx = F.x - E.x
        EFy = F.y - E.y
        c = ABx * EFy - ABy * EFx
        if c != 0:
            u = uu / c
            print("U - EF: ", u)
            if 0 <= u <= 1:
                # Hits line E-F
                return segments[0]

        EJx = J.x - E.x
        EJy = J.y - E.y
        c = ABx * EJy - ABy * EJx
        if c != 0:
            u = uu / c
            print("U - EJ: ", u)
            if 0 <= u <= 1:
                # Hits line E-J
                return segments[1]

        raise Exception("Returned no segment from the collision detection")

    @staticmethod
    def get_common_corner(segment1: Segment2D, segment2: Segment2D):
        if segment1.has_point(segment2.p1):
            return segment2.p1
        if segment1.has_point(segment2.p2):
            return segment2.p2
        print(segment1)
        print(segment2)
        raise Exception("No common corner")

    def add_width_height_relative_to_center(self, point: Point2D, center: Point2D) -> Point2D:
        newPoint = Point2D(point.x, point.y)
        if newPoint.y >= center.y:
            newPoint.y += self.shape.height / 2
        else:
            newPoint.y -= self.shape.height / 2

        if newPoint.x >= center.x:
            newPoint.x += self.shape.width / 2
        else:
            newPoint.x -= self.shape.width / 2

        return newPoint

反射方法从rect_rect_collision方法获取到错误冲击向量的场景:绿色轮廓是可碰撞的刚体,红色箭头是反射前力向量的方向,计算反射后得到的反射方向是错误的。

解决方案

边角双碰撞段选择逻辑修正

你当前的选边方案只判断力向量和扩展边的交点,没有引入碰撞面和入射方向的相关性校验,边角场景下容易出错。优化逻辑如下:

  • 优先计算每个碰撞段的法向量和入射力向量的点积,取点积绝对值更大的边作为碰撞面,这个边是阻挡当前运动的主要受力面。
  • 如果两个边的点积差值小于1e-3(说明接近正碰边角),不用硬选单一边,直接将两个边的法向量归一化后叠加,得到等效碰撞法向量代入反射公式即可。

平行碰撞段处理

当检测到两条平行的碰撞边时,说明物体沿边的法向穿透了整个被碰撞矩形,此时计算两条边对应的碰撞穿透深度(即两个矩形在边的法向方向上的重叠长度),选择穿透深度更小的边作为碰撞面即可。

现有代码bug排查点

你目前的实现有三个容易触发错误的点:

  • add_width_height_relative_to_center方法仅适用于轴对齐矩形,如果你后续要支持旋转矩形,这个扩展边的计算逻辑会完全失效。
  • 你用的离散线段相交检测在物体运动速度过快时会出现穿模,导致同时检测到多条碰撞边。你可以拆分物理更新步长,或者增加连续碰撞检测逻辑避免该问题。
  • 你可以补充碰撞后位置修正步骤:检测到碰撞后,先将物体沿碰撞法向量反方向移动到刚好不穿透的位置,再计算反射,能大幅降低方向错误的概率。

内容的提问来源于stack exchange,提问作者Beefcake

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 18:18:03