netlib.narod.ru< Назад | Оглавление | Далее >

Пересечение луча и ландшафта

Чтобы обнаружить случаи, когда объект в сцене пересекается с частью ландшафта, вам необходимо создать какие-нибудь методы проверки столкновений. Одна из полезных проверок столкновений — между лучом и ландшафтом. Например, если объект перемещается в сцене, вы можете отследить луч, идущий в направлении перемещения объекта, и получить расстояние между ним и ландшафтом.

Чтобы проверить пересечение луча и ландшафта вы выполняете проверку столкновений между лучом и картой высот ландшафта, вместо того, чтобы проверять пересечение луча и сетки ландшафта (множества треугольников). Проверка столкновений делится на две части. В первой части вы выполняете линейный поиск по направлению луча, пока не найдете одну точку вне (над) и другую внутри (под) ландшафта. Затем вы выполняете бинарный поиск между этими двумя точками, чтобы найти точное местоположение точки столкновения с ландшафтом. На рис. 10.13 показан процесс линейного поиска, где найдены ближайшие точки вне и внутри ландшафта.


Рис. 10.13. Линейный поиск используется для обнаружения одной точки вне ландшафта, и другой внутри него

Рис. 10.13. Линейный поиск используется для обнаружения одной точки вне ландшафта, и другой внутри него


Для выполнения линейного поиска воспользуйтесь следующим кодом:

// Хорошей величиной шага является половина
// размера блока blockScale
Vector3 rayStep = ray.Direction * blockScale * 0.5f;
Vector3 rayStartPosition = ray.Position;

// Линейный поиск - цикл, пока не найдены точки
// вне и внутри ландшафта
Vector3 lastRayPosition = ray.Position;
ray.Position += rayStep;
float height = GetHeight(ray.Position);

while (ray.Position.Y > height && height >= 0)
{
    lastRayPosition = ray.Position;
    ray.Position += rayStep;
    height = GetHeight(ray.Position);
}

После линейного поиска переменная lastRayPosition хранит местоположение вне ландшафта, а переменная ray хранит местоположение внутри ландшафта. Теперь вам необходимо выполнить бинарный поиск между этими двумя точками, чтобы найти точку, ближайшую к поверхности ландшафта. Вы выполняете поиск с фиксированным количеством шагов, и 32 шага достаточно, чтобы обеспечить приемлемый уровень точности. Код для бинарного поиска таков:

Vector3 startPosition = lastRayPosition;
Vector3 endPosition = ray.Position;

// Бинарный поиск с 32 шагами.
// Пробуем найти точное место пересечения
for (int i = 0; i < 32; i++)
{
    // Проход бинарного поиска
    Vector3 middlePoint = (startPosition + endPosition) * 0.5f;

    if (middlePoint.Y < height)
        endPosition = middlePoint;
    else
        startPosition = middlePoint;
}

Vector3 collisionPoint = (startPosition + endPosition) * 0.5f;

Теперь создадим метод Intersect, для проверки пересечения луча и ландшафта. Метод Intersect возвращает расстояние между начальной точкой луча и точкой пересечения с ландшафтом, а если пересечение с ландшафтом отсутствует, метод возвратит null. Ниже показан код метода Intersect класса Terrain:

public float? Intersects(Ray ray)
{
    float? collisionDistance = null;
    Vector3 rayStep = ray.Direction * blockScale * 0.5f;
    Vector3 rayStartPosition = ray.Position;

    // Линейный поиск - цикл, пока не найдены точки
    // вне и внутри ландшафта
    Vector3 lastRayPosition = ray.Position;
    ray.Position += rayStep;
    float height = GetHeight(ray.Position);

    while (ray.Position.Y > height && height >= 0)
    {
        lastRayPosition = ray.Position;
        ray.Position += rayStep;
        height = GetHeight(ray.Position);
    }

    // Если луч пересекается с ландшафтом
    if (height >= 0)
    {
        Vector3 startPosition = lastRayPosition;
        Vector3 endPosition = ray.Position;

        // Бинарный поиск. Ищем точное место пересечения 
        for (int i = 0; i < 32; i++)
        {
            // Проход бинарного поиска
            Vector3 middlePoint = (startPosition + endPosition) * 0.5f;

            if (middlePoint.Y < height)
                endPosition = middlePoint;
            else
                startPosition = middlePoint;
        }

        Vector3 collisionPoint = (startPosition + endPosition) * 0.5f;
        collisionDistance = Vector3.Distance(rayStartPosition, collisionPoint);
    }

    return collisionDistance;
}

netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz