具有负方向的快速体素遍历算法

时间:2014-12-10 20:33:00

标签: xna raytracing

我试图实现快速体素遍历算法并根据this answer计算T和M(T是tDelta,M是tMax)。如果方向矢量V的两个分量是正的,则一切都很好。但如果其中至少有一个是消极的,那就错了。

绿点开始,红色结束。一切似乎都正确。 S.X > 0, S.Y > 0

现在从更大到更少的位置。 S.X < 0, S.Y < 0

遍历方法:

private IEnumerable<Vector2> GetCrossedCells(Vector2 pPoint1, Vector2 pPoint2)
{
    Vector2 V = pPoint2 - pPoint1; // direction & distance vector
    if (V != Vector2.Zero)
    {
        Vector2 U = Vector2.Normalize(V); // direction unit vector
        Vector2 S = new Vector2(Math.Sign(U.X), Math.Sign(U.Y)); // sign vector
        Vector2 P = pPoint1; // position
        Vector2 G = new Vector2((int) Math.Floor(P.X / CELL_SIZE), (int) Math.Floor(P.Y / CELL_SIZE)); // grid coord
        Vector2 T = new Vector2(Math.Abs(CELL_SIZE / U.X), Math.Abs(CELL_SIZE / U.Y));
        Vector2 M = new Vector2(
            Single.IsInfinity(T.X) ? Single.PositiveInfinity : T.X * (1.0f - (P.X / CELL_SIZE) % 1),
            Single.IsInfinity(T.Y) ? Single.PositiveInfinity : T.Y * (1.0f - (P.Y / CELL_SIZE) % 1));

        Vector2 D = Vector2.Zero;
        bool isCanMoveByX = S.X != 0;
        bool isCanMoveByY = S.Y != 0;

        while (isCanMoveByX || isCanMoveByY)
        {
            yield return G;

            D = new Vector2(
                S.X > 0 ? (float) (Math.Floor(P.X / CELL_SIZE) + 1) * CELL_SIZE - P.X :
                S.X < 0 ? (float) (Math.Ceiling(P.X / CELL_SIZE) - 1) * CELL_SIZE - P.X :
                0,
                S.Y > 0 ? (float) (Math.Floor(P.Y / CELL_SIZE) + 1) * CELL_SIZE - P.Y :
                S.Y < 0 ? (float) (Math.Ceiling(P.Y / CELL_SIZE) - 1) * CELL_SIZE - P.Y :
                0);

            if (Math.Abs(V.X) <= Math.Abs(D.X))
            {
                D.X = V.X;
                isCanMoveByX = false;
            }

            if (Math.Abs(V.Y) <= Math.Abs(D.Y))
            {
                D.Y = V.Y;
                isCanMoveByY = false;
            }

            if (M.X <= M.Y)
            {
                M.X += T.X;
                G.X += S.X;
                if (isCanMoveByY)
                {
                    D.Y = U.Y / U.X * D.X; // U.X / U.Y = D.X / D.Y => U.X * D.Y = U.Y * D.X
                }
            }
            else
            {
                M.Y += T.Y;
                G.Y += S.Y;
                if (isCanMoveByX)
                {
                    D.X = U.X / U.Y * D.Y;
                }
            }

            V -= D;
            P += D;
        }
    }
}

在调试中我可以看到例如M.Y&gt;如果S.X&lt; X,则X应该相反。 0或S.Y&lt; 0

请告诉我,我的代码对于负面指示有什么用处?

1 个答案:

答案 0 :(得分:2)

所以,我解决了。 我使代码更清晰,问题就消失了。

private IEnumerable<Vector2> GetCrossedCells(Vector2 pPoint1, Vector2 pPoint2)
{
    if (pPoint1 != pPoint2)
    {
        Vector2 V = (pPoint2 - pPoint1) / CELL_SIZE; // direction & distance vector
        Vector2 U = Vector2.Normalize(V); // direction unit vector
        Vector2 S = new Vector2(Math.Sign(U.X), Math.Sign(U.Y)); // sign vector
        Vector2 P = pPoint1 / CELL_SIZE; // position in grid coord system
        Vector2 G = new Vector2((int) Math.Floor(P.X), (int) Math.Floor(P.Y)); // grid coord
        Vector2 T = new Vector2(Math.Abs(CELL_SIZE / U.X), Math.Abs(CELL_SIZE / U.Y));
        Vector2 D = new Vector2(
            S.X > 0 ? 1 - P.X % 1 : S.X < 0 ? P.X % 1 : 0,
            S.Y > 0 ? 1 - P.Y % 1 : S.Y < 0 ? P.Y % 1 : 0);
        Vector2 M = new Vector2(
            Single.IsInfinity(T.X) || S.X == 0 ? Single.PositiveInfinity : T.X * D.X,
            Single.IsInfinity(T.Y) || S.Y == 0 ? Single.PositiveInfinity : T.Y * D.Y);

        bool isCanMoveByX = S.X != 0;
        bool isCanMoveByY = S.Y != 0;

        while (isCanMoveByX || isCanMoveByY)
        {
            yield return G;

            D = new Vector2(
                S.X > 0 ? (float) Math.Floor(P.X) + 1 - P.X :
                S.X < 0 ? (float) Math.Ceiling(P.X) - 1 - P.X :
                0,
                S.Y > 0 ? (float) Math.Floor(P.Y) + 1 - P.Y :
                S.Y < 0 ? (float) Math.Ceiling(P.Y) - 1 - P.Y :
                0);

            if (Math.Abs(V.X) <= Math.Abs(D.X))
            {
                D.X = V.X;
                isCanMoveByX = false;
            }

            if (Math.Abs(V.Y) <= Math.Abs(D.Y))
            {
                D.Y = V.Y;
                isCanMoveByY = false;
            }

            if (M.X <= M.Y)
            {
                M.X += T.X;
                G.X += S.X;
                if (isCanMoveByY)
                {
                    D.Y = U.Y / U.X * D.X; // U.X / U.Y = D.X / D.Y => U.X * D.Y = U.Y * D.X
                }
            }
            else
            {
                M.Y += T.Y;
                G.Y += S.Y;
                if (isCanMoveByX)
                {
                    D.X = U.X / U.Y * D.Y;
                }
            }

            V -= D;
            P += D;
        }
    }
}

<强>更新

我开始删除GRID_CELL上的冗余分区,然后注意M计算中的错误。 在回答问题时使用Frac()函数,我提供了一个链接。我计算它(1 - P%1),但这是S&gt;的情况。如果S <0,则应该是(P%1)。 0,Inf为S = 0。

更新2

还应该有

Vector2 D = new Vector2(
    S.X > 0 ? (float) Math.Floor(P.X) + 1 - P.X :
    S.X < 0 ? (float) Math.Ceiling(P.X) - 1 - P.X :
    0,
    S.Y > 0 ? (float) Math.Floor(P.Y) + 1 - P.Y :
    S.Y < 0 ? (float) Math.Ceiling(P.Y) - 1 - P.Y :
    0);

而不是

Vector2 D = new Vector2(
    S.X > 0 ? 1 - P.X % 1 : S.X < 0 ? P.X % 1 : 0,
    S.Y > 0 ? 1 - P.Y % 1 : S.Y < 0 ? P.Y % 1 : 0);

因为在S