关于数学:如何将视线截断在视锥上?

关于数学:如何将视线截断在视锥上?

How do I clip a line segment against a frustum?

给定两个向量A和B,它们形成线段L = A-B。
此外,给出了视锥台F,该视锥台由其左,右,底,顶,近和远平面定义。

如何将L夹在F上?

也就是说,测试一个交集,以及该交集出现在L的哪个位置?
(请注意,如果线段与某个角的两侧相交,则该线段与平截头体可以有多个交集。)

如果可能,请提供代码示例(首选C ++或Python)。


我现在不想为此编写代码,但是如果我正确理解"平截头体",则应该可以进行以下工作。

  • 将直线与所有给定平面相交
  • 如果您有两个交叉路口,那么就完成了。
  • 如果只有一个相交,请计算前平面并相交。
  • 如果仍然只有一个相交,请计算背板并相交。
  • 但是我可能完全被误解了。在这种情况下,请详细说明:)


    首先从视图矩阵中提取平面。

    然后使用您的点将向量和最小值/最大值定义为(0,1),然后在平面上迭代并将它们与线段相交,更新最小值/最大值,如果min > max,则尽早进行。

    这是一个纯Python函数的示例,没有外部dep。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    def clip_segment_v3_plane_n(p1, p2, planes):
       """
        - p1, p2: pair of 3d vectors defining a line segment.
        - planes: a sequence of (4 floats): `(x, y, z, d)`.

        Returns 2 vector triplets (the clipped segment)
        or (None, None) then segment is entirely outside.
       """
        dp = sub_v3v3(p2, p1)

        p1_fac = 0.0
        p2_fac = 1.0

        for p in planes:
            div = dot_v3v3(p, dp)
            if div != 0.0:
                t = -plane_point_side_v3(p, p1)
                if div > 0.0:  # clip p1 lower bounds
                    if t >= div:
                        return None, None
                    if t > 0.0:
                        fac = (t / div)
                        if fac > p1_fac:
                            p1_fac = fac
                            if p1_fac > p2_fac:
                                return None, None
                elif div < 0.0:  # clip p2 upper bounds
                    if t > 0.0:
                        return None, None
                    if t > div:
                        fac = (t / div)
                        if fac < p2_fac:
                            p2_fac = fac
                            if p1_fac > p2_fac:
                                return None, None

        p1_clip = add_v3v3(p1, mul_v3_fl(dp, p1_fac))
        p2_clip = add_v3v3(p1, mul_v3_fl(dp, p2_fac))

        return p1_clip, p2_clip


    # inline math library
    def add_v3v3(v0, v1):
        return (
            v0[0] + v1[0],
            v0[1] + v1[1],
            v0[2] + v1[2],
            )

    def sub_v3v3(v0, v1):
        return (
            v0[0] - v1[0],
            v0[1] - v1[1],
            v0[2] - v1[2],
            )

    def dot_v3v3(v0, v1):
        return (
            (v0[0] * v1[0]) +
            (v0[1] * v1[1]) +
            (v0[2] * v1[2])
            )

    def mul_v3_fl(v0, f):
        return (
            v0[0] * f,
            v0[1] * f,
            v0[2] * f,
            )

    def plane_point_side_v3(p, v):
        return dot_v3v3(p, v) + p[3]

    除了上面所说的Cor??poral Touchy所说的,您还需要知道如何使线段与平面相交。在该页面的描述中,u表示行的参数定义中的参数。首先,使用上述2种方法之一计算u。如果u的值落在0.0到1.0的范围内,则平面会将线夹到线段上的某处。将u重新插入到线方程中,可以得出发生交叉的点。

    另一种方法是找到每个点到平面的定向距离。如果一个点的距离为正,而另一个点的距离为负,则它们位于平面的相对侧。然后,您会知道哪个点位于您的视锥之外(基于您的平面法线点的方向)。使用这种方法,可以根据有向距离的比率进行线性插值,从而更快地找到交点。例如。如果一个点的距离是+12,另一个点的距离是-12,则您知道平面将线段切成两半,并且u参数是0.5。

    希望这可以帮助。


    推荐阅读