При расчёте фрустума сразу же пересчитываются не только восемь угловых точек, но и ограничивающие плоскости. Точки — это очевидно — нужны для построения меша фрустума. Текстурные координаты фиксированные, как и массив граней (формируется из индексов точек) — в процессе работы они не изменяются (грани — всегда, а вот текстурные координаты никто не мешает анимировать, это легко сделать). Классы точки (Vector3, Vector2) и плоскости (Plane) уже готовы, писать ничего не нужно (юнититим уже всё сделала). Эти классы содержат весь необходимый функционал. Класса же для фрустума в Юнити нет. Есть класс камеры. По большому счёту, можно использовать его, но, на мой взгляд, это очень неудобно: в нём слишком много лишнего, да и заточен он всё-таки под другое.
Расчёт фрустума тривиален и может выполняться несколькими различными способами.
Есть относительно простой через матрицы. Делается это так: рассчитываются матрица вида (нужны положение наблюдателя и направление координатных осей) и матрица проекций (нужны Z-координаты плоскостей отсечения — ближней и дальней, угол поля зрения и отношение ширины вьюпорта к его высоте). Расчёт не сложен. Далее матрица проекций умножается на матрицу вида (именно в таком порядке) и получается матрица отсечения, из которой уже можно получить формирующие фрустум плоскости в виде нормали и расстояния до начала координат (как известно, плоскость можно задать несколькими способами — это один из). Плоскости необходимо нормализовать (а вот этого Юнити, к сожалению, не умеет, нужно писать собственную функцию). Чтобы затем построить меш фрустума, необходимо произвести дополнительные расчёты (определить точки пересечения плоскостей).
Я сначала пошёл этим путём, но по каким-то причинам (их я так и не определил точно), он у меня не заработал. Следует однако высказать кое-какие соображения (даже подозрения — вдруг кому-то пригодится). Дело в том, что есть определённые нюансы при расчёте как матриц, так и плоскостей для DirectX и OpenGL. Эти API используют по-разному ориентированные системы координат (DirectX — левостороннюю, OpenGL — правостороннюю). Не имея возможности определить, с какой графической подсистемой работает Юнити, невозможно корректно рассчитать матрицы и плоскости фрустума.
После ряда неудачных экспериментов я решил считать фрустум по-другому. Можно обойтись и без матриц, имея те же самые данные, но воспользоваться векторной, а не линейной алгеброй. В этом способе сначала рассчитываются угловые точки фрустума, а затем, если необходимо, формируются плоскости. С этим всё получилось, практически сразу заработало. Оценить способы по затратам производительности, к сожалению, возможности не было (профайлер, кажется, в про-версии только, да? У меня этот пункт меню недоступен).
Я всё время говорю о плоскостях, но должно быть, не всем ясно, для чего они нам. Точки-то понятно — без них меш не сделать. А зачем плоскости? А вот зачем: они позволяют очень быстро произвести отбраковку точек (определить взаимное положение). Вот, например, как будет выглядеть функция определения положения точки относительно фрустума:
- Код: Выделить всё
паблик инт тестПоинт(Вектор3 п)
{
флоат расстояние;
цикл для и от 0 до 6
{
расстояние = плейн[и].получитьРасстояниеДоТочки(п);
если расстояние > 0
{
вернуть результат «снаружи»;
}
иначе
{
если расстояние = 0
{
вернуть результат «на плоскости»;
}
}
}
вернуть результат «внутри»;
}
Также несложно рассчитывается положение, например, сферы относительно фрустума (по центру и радиусу) или бибокса (по четырём или восьми точкам). Я при расчёте бибокса для дополнительных расчётов ещё сохраняю информацию о том, какие именно точки лежат внутри или снаружи.