Пересечение лучей с трехмерными квадратами в XNA?

Итак, я успешно создал луч, который представляет мышь, не проецируемую в мир, и теперь мне нужно проверить, может ли этот луч пересекаться с четырехугольным объектом, вот код, который я использую для получения луча:

public Ray GetMouseRay()
    {
        Vector2 mousePosition = new Vector2(cursor.getX(), cursor.getY());

        Vector3 nearPoint = new Vector3(mousePosition, 0);
        Vector3 farPoint = new Vector3(mousePosition, 1);

        nearPoint = viewport.Unproject(nearPoint, projectionMatrix, viewMatrix, Matrix.Identity);
        farPoint = viewport.Unproject(farPoint, projectionMatrix, viewMatrix, Matrix.Identity);

        Vector3 direction = farPoint - nearPoint;
        direction.Normalize();

        return new Ray(nearPoint, direction);
    }

Точно так же это моя четырехугольная структура, которую я использую для рисования «кубического мира» в трехмерном пространстве. общедоступная структура Quad {public Vector3 Origin; общедоступный Vector3 UpperLeft; общедоступный Vector3 LowerLeft; общедоступный Vector3 UpperRight; общедоступный Vector3 LowerRight; общедоступный Vector3 Normal; общедоступный Vector3 Up; общедоступный Vector3 Left;

    public VertexPositionNormalTexture[] Vertices;
      public int[] Indexes;
    public short[] Indexes;


    public Quad( Vector3 origin, Vector3 normal, Vector3 up, 
        float width, float height )
    {
        Vertices = new VertexPositionNormalTexture[4];
        Indexes = new short[6];
        Origin = origin;
        Normal = normal;
        Up = up;

        // Calculate the quad corners
        Left = Vector3.Cross( normal, Up );
        Vector3 uppercenter = (Up * height / 2) + origin;
        UpperLeft = uppercenter + (Left * width / 2);
        UpperRight = uppercenter - (Left * width / 2);
        LowerLeft = UpperLeft - (Up * height);
        LowerRight = UpperRight - (Up * height);

        FillVertices();
    }

    private void FillVertices()
    {
        // Fill in texture coordinates to display full texture
        // on quad
        Vector2 textureUpperLeft = new Vector2( 0.0f, 0.0f );
        Vector2 textureUpperRight = new Vector2( 1.0f, 0.0f );
        Vector2 textureLowerLeft = new Vector2( 0.0f, 1.0f );
        Vector2 textureLowerRight = new Vector2( 1.0f, 1.0f );

        // Provide a normal for each vertex
        for (int i = 0; i < Vertices.Length; i++)
        {
            Vertices[i].Normal = Normal;
        }

        // Set the position and texture coordinate for each
        // vertex
        Vertices[0].Position = LowerLeft;
        Vertices[0].TextureCoordinate = textureLowerLeft;
        Vertices[1].Position = UpperLeft;
        Vertices[1].TextureCoordinate = textureUpperLeft;
        Vertices[2].Position = LowerRight;
        Vertices[2].TextureCoordinate = textureLowerRight;
        Vertices[3].Position = UpperRight;
        Vertices[3].TextureCoordinate = textureUpperRight;

        // Set the index buffer for each vertex, using
        // clockwise winding
        Indexes[0] = 0;
        Indexes[1] = 1;
        Indexes[2] = 2;
        Indexes[3] = 2;
        Indexes[4] = 1;
        Indexes[5] = 3;
    }
}

Я обнаружил, что у класса лучей есть метод crosscts (), который принимает структуру plane в качестве параметра, а структура плоскости принимает нормаль и расстояние от начала координат в конструкторе, но мои плоскости имеют положение и нормальный, а не просто расстояние от источника, поэтому я не могу их преобразовать. Как я могу определить, пересекает ли мой луч мою четырехугольную структуру?

Изменить: я понимаю, что не могу использовать структуру плоскости, так как она не имеет конечного размера и не включает углы, как мой четырехугольник. Теперь мне нужно найти способ определить, пересекает ли этот луч созданный мною четырехугольник ...

Спасибо за чтение, я понимаю, что этот вопрос немного длинноват, и заранее спасибо.


person Glen654    schedule 24.10.2012    source источник


Ответы (3)


Я не совсем уверен, что они подразумевают под «расстоянием по нормали от начала координат», но я предполагаю, что это просто означает расстояние от начала координат. Вы можете получить это из свойства length в Vector3.

Если это не сработает, есть также конструктор для plane, который принимает три точки на плоскости:

public Plane (
     Vector3 point1,
     Vector3 point2,
     Vector3 point3
)

Вы можете использовать это с любыми 3 точками на плоскости, такими как углы вашего квадрата, чтобы создать плоскость.

person Tyler Venables    schedule 24.10.2012
comment
Расстояние по вектору нормали к плоскости означает, что если бы плоскость была в той же ориентации, но преобразована так, что она также содержала точку (0,0,0), то как далеко ей нужно было бы переместиться, чтобы достичь своего фактического положения. Или это может быть отрицательное значение этого значения, я всегда смешивал последний бит :) - person Seth Battin; 24.10.2012
comment
Хорошо, но разве это не самое близкое расстояние от исходной точки до плоскости? Мне это кажется плохо сформулированным. - person Tyler Venables; 24.10.2012
comment
Да, но он основан на идее, что плоскость может быть определена исключительно по этому нормальному единичному вектору и расстоянию. - person Seth Battin; 24.10.2012

Поскольку вы используете кубики, вы можете использовать метод Ray.Intersects(Plane). Вам просто нужно еще несколько самолетов.

Как только вы определите первую точку пересечения, вы можете построить больше плоскостей для каждой перпендикулярной грани вашего куба. Затем вы можете использовать Plane.Distance(Vector3) (я почти уверен, что это или что-то в этом роде существует), чтобы убедиться, что ваша точка находится между каждой парой перпендикулярных плоскостей. Если между ними нет, то перекресток можно не обращать внимания.

person Seth Battin    schedule 24.10.2012

Не совсем прямой ответ на ваш вопрос, но, возможно, вы сочтете это актуальным.

Рассматривали ли вы создание объекта BoundingBox и использование пересечения (луч)? http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.boundingbox.intersects.aspx

Изменить 1: Так я обычно это делаю. В зависимости от вашей структуры данных, ее также может быть проще оптимизировать (например, если вы создадите ограничительные рамки большего размера).

Изменить 2: Согласно предложению Нико: я не хочу публиковать соответствующий код (потому что это всего пара страниц, чтобы контекст имел смысл) из образцов, но я могу указать вам на те части, которые вы ' будет интересно.

  1. Загрузите образец с http://xbox.create.msdn.com/en-US/education/catalog/sample/picking_triangle
  2. Внизу файла Cursor.cs содержится то, что вы уже знаете, вычисление луча.
  3. Перейдите в файл Game.cs. Там вы найдете UpdatePicking (), который вызывает RayIntersectsModel (...), который вызывает RayIntersectsTriangle (...)

Преобразование Луча - это эта часть:

Matrix inverseTransform = Matrix.Invert( modelTransform );

ray.Position = Vector3.Transform( ray.Position, inverseTransform );
ray.Direction = Vector3.TransformNormal( ray.Direction, inverseTransform );

То, насколько глубоко вы хотите зайти (насколько точно вы выбираете), конечно, соответствует вашим потребностям, но вот и все.

person FanerYedermann    schedule 24.10.2012
comment
К сожалению, BoundingBox выровнен по оси, что означает, что он не вращается, как его Quad. - person neeKo; 24.10.2012
comment
... что действительно очень легко обойти, просто преобразовав луч. Если вам интересно, все это продемонстрировано в этих двух примерах: xbox .create.msdn.com / en-US / education / catalog / sample / и xbox.create.msdn.com/en-US/education/catalog/sample/picking - person FanerYedermann; 24.10.2012
comment
Эй, это здорово - если бы вы могли отредактировать ответ, включив в него рабочий образец, он, скорее всего, был бы принят! - person neeKo; 24.10.2012