如何确定网格中的哪些像元与给定的三角形相交?


10

我目前正在编写2D AI模拟,但是我不确定如何检查代理的位置是否在另一个代理的视野内。

目前,我的世界分区是简单的单元空间分区(网格)。我想使用三角形表示视野,但是如何计算与三角形相交的像元?

与此图片相似: 在此处输入图片说明

红色区域是我要计算的单元格,方法是检查三角形是否与这些单元格相交。

提前致谢。

编辑:

只是增加了混乱(甚至可能使其更简单)。每个像元都有一个最小值和最大值向量,其中最小值是左下角,最大值是右上角。


您不能将单元格拆分为三角形,然后测试三角形吗?
共产党鸭子

单元不是物理多边形,仅是空间表示,它利用了数组的O(1)访问时间。如果我在代理周围有一个邻域圆,以近似单元,则可以使用圆的半径创建AABB并轻松找到交点。这里的问题是只需要我前面的单元格。我敢肯定,有一些几何方程式可以帮上忙,我只是一辈子都想不出来。
Ray Dey

不喜欢这里的任何答案;但是,这个问题有一些非常好的答案:gamedev.stackexchange.com/q/81267/63053
Andrew

Answers:


6

计算您的fov三角形的三个角,将它们旋转为面向正确的方向,然后执行以下操作之一:

1)对所有潜在目标进行三角测试

2)计算该三角形的边界框,并对该边界框内单元中的所有潜在目标进行三角测试-这将是非常简单的调试代码

一种类似的方法是使用四叉树而不是网格,并在其上进行交点。如果O(1)切片访问使您加快了速度,则只需测试fov三角形边界内的所有像元以获取三角形内,就应该如此之快以至于可以立即执行。当您在寻找其他选项时,我认为它不是,并且O(1)实际上在您破坏高速缓存时承担了巨大的高速缓存未命中成本。您当然可以查看预取说明,以注释您的边界框游标...

3)“栅格化”这个三角形并检查它“绘制”的单元格-可能是最有效的代码,但可能只是微不足道,因为我推测其所有内容都由缓存未命中时间决定,并且取决于您的单元格的复杂程度和占用程度他们是。

另一种方法是将FOV渲染到屏幕外的位图,然后读取每个对象的像素值。您不能“取消混合油漆”,而只能使用数量有限的对象,并且通过仔细选择油漆可以推断出谁看见了谁。这种方法类似于有多少游戏可以算出用户点击的内容-他们使用纯色为点击区域在屏幕外绘制场景。GPU的三角形填充速度非常快...


+1,谢谢!为此,我使用了三角形的边界框来快速选择适当的单元格,并使用了三角测试来确定这些单元格的哪些成员在视场内:)
Ray Dey

3

我认为,软件渲染器的规范解决方案(每次渲染三角形时必须执行此精确算法)是一次扫描三角形一行像素。每行的左边缘和右边缘,通过使用Bresenham沿着三角形的边沿走而计算,然后在它们之间填充行。

我在掩盖许多细节,但这是基本思想。如果您四处寻找“软件渲染”和“三角栅格化”,则可能会发现更多详细信息。这是一个很好解决的问题。您的图形卡在一帧中执行了数百万次。

如果您想要一个更像roguelike的解决方案,这就是我在我的方法中实现FOV的方法。它似乎工作很快。从本质上讲,它是一个简单的阴影投射器,一次处理八分圆,从播放器向外扫描。


1
那是很酷的方法。
Notabene 2011年

2

我正在使用scanline算法的一种变体来解决完全相同的问题。我首先按其高度对三个三角形点进行排序。然后,我基本上检查两个边缘是在左侧还是在右侧。对于具有两个边缘的一侧,您必须将它们标记为行,并在其中更改界定行的边缘。对于只有一条边的一侧,您始终可以使用它。

因此,对于每一行,我知道哪两条边将其定界,并且可以计算x方向的上限和下限。听起来很复杂,但仅压缩了几行代码。确保处理一条边缘完全水平的特殊情况!


2

如何为三角形中的每一行保持一定范围的列?您可以做的是为每个点的位置和每条三角形线与水平行分隔线交叉的每一行设置最小值和最大值列。

public class Point
{
    public float X;
    public float Y;
    public Point(float x, float y) { this.X = x; this.Y = y; }
}

public class Line
{
    float ROW_SIZE = 100f;
    float COL_SIZE = 100f;

    public Point P1, P2; // P1 has the lowest Y
    public float Slope, Intercept; // set in constructor
    public bool IsVertical;

    public Line(Point p1, Point p2)
    {
        if (p1.Y > p2.Y) { P1 = p2; P2 = p1; } // p1 has lowest Y
        else { P1 = p1; P2 = p2; }
        IsVertical = (p1.X == p2.X);
        if (!IsVertical) { Slope = (p2.Y - p1.Y) / (p2.X - p1.X); Intercept = p1.Y - Slope * p1.X; }
    }

    public void ExpandRanges(int[] minCol, int[] maxCol)
    {
        // start out at row, col where P1 is, which has lowest Y
        int row = (int)(P1.Y / ROW_SIZE);
        int col = (int)(P1.X / COL_SIZE);
        int lastRow = (int)(P2.Y / ROW_SIZE);
        int lastCol = (int)(P2.X / COL_SIZE);

        // expand row to include P1
        minCol[row] = Math.Min(col, minCol[row]); maxCol[row] = Math.Max(col, maxCol[row]);

        // now we find where our line intercepts each horizontal line up to P2
        float currY = P1.Y;
        float currX = P1.X;
        while (row < lastRow)
        {
            row = row + 1;
            float rowY = row * ROW_SIZE;
            float diffY = rowY - currY;
            float diffX = IsVertical ? 0f : diffY / Slope;
            currY = currY + diffY;
            currX = currX + diffX;
            col = (int)(currX / COL_SIZE);

            // expand rows above and below dividing line to include point
            minCol[row - 1] = Math.Min(col, minCol[row - 1]);
            maxCol[row - 1] = Math.Max(col, maxCol[row - 1]);
            minCol[row] = Math.Min(col, minCol[row]);
            maxCol[row] = Math.Max(col, maxCol[row]);
        }

        // expand last row to include P2
        minCol[lastRow] = Math.Min(lastCol, minCol[lastRow]);
        maxCol[lastRow] = Math.Max(lastCol, maxCol[lastRow]);
    }

    public static void Test()
    {
        Point p1 = new Point(160, 250);
        Point p2 = new Point(340, 250);
        Point p3 = new Point(250, 40);
        Line l1 = new Line(p1, p2);
        Line l2 = new Line(p2, p3);
        Line l3 = new Line(p3, p1);

        Line[] lines = { l1, l2, l3 };

        int rowCount = 4;
        int[] minCol = new int[rowCount];
        int[] maxCol = new int[rowCount];
        for (int i = 0; i < rowCount; i++)
        {
            minCol[i] = int.MaxValue;
            maxCol[i] = int.MinValue;
        }

        for (int i = 0; i < lines.Length; i++)
            lines[i].ExpandRanges(minCol, maxCol);

        for (int i = 0; i < rowCount; i++)
            Console.WriteLine("Row {0}:  {1} - {2}", i, minCol[i], maxCol[i]);
    }
}

输出:

Row 0:  2 - 2
Row 1:  1 - 3
Row 2:  1 - 3
Row 3:  2147483647 - -2147483648

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.