Ограничения Trimesh при рейкасте

Cannon.js предоставляет возможности работы с физикой в браузере на JavaScript, включая сложные формы коллизий, такие как Trimesh. Trimesh — это сетка треугольников, используемая для представления произвольных 3D-объектов. Несмотря на гибкость, она накладывает ряд ограничений, особенно при использовании рейкаста (raycast).


Природа Trimesh и особенности рейкаста

Trimesh состоит из массивов вершин и индексов треугольников. Каждое столкновение или пересечение вычисляется через проверку луча против всех треугольников сетки. В Cannon.js процесс рейкаста через Trimesh существенно отличается от рейкаста через примитивы (сферы, коробки, капсулы):

  • Отсутствие внутренней оптимизации: Примитивы имеют аналитические формулы пересечения с лучом, что позволяет быстро вычислять точку столкновения. Для Trimesh каждое пересечение требует проверки всех треугольников или хотя бы использования BVH (Bounding Volume Hierarchy), которая в Cannon.js не реализована по умолчанию.
  • Высокая вычислительная нагрузка: При большом количестве треугольников рейкаст может замедлять работу сцены. Проверка каждого треугольника — O(n) операция, где n — количество треугольников.

Точность и проблемы с пропуском столкновений

Trimesh при рейкасте может демонстрировать неточности, особенно на тонких или очень сложных формах:

  1. Пропуск тонких треугольников: Луч может проходить через треугольники с малой толщиной, не фиксируя столкновение из-за ограниченной точности чисел с плавающей запятой.
  2. Внутренние столкновения не учитываются: Trimesh рассматривается как поверхность, а не объем. Рейкаст за пределами треугольника внутри объекта может не сработать.
  3. Дупликаты вершин и плоские треугольники создают ложные пересечения, особенно при динамических трансформациях сетки.

Ограничения по трансформации

  • Динамический масштаб: Изменение масштаба Trimesh после создания объекта не пересчитывает внутренние данные для рейкаста. Луч может пересекать старые координаты треугольников.
  • Повороты и позиционирование: Все трансформации должны быть учтены через матрицу Body или Shape, иначе рейкаст будет учитывать только локальные координаты треугольников.
  • Динамическая деформация: Cannon.js не поддерживает деформацию вершин Trimesh на лету для корректного рейкаста. Для динамических мешей требуется пересоздавать объект.

Ограничения при использовании с RaycastResult

RaycastResult хранит результаты пересечения: точку контакта, нормаль, дистанцию и тело. При Trimesh существуют специфические ограничения:

  • Нет поддержки нескольких пересечений на одной вершине: Если луч проходит через смежные треугольники в одной точке, фиксируется только первое пересечение.
  • Нормаль поверхности может быть некорректной: Для очень острых углов треугольников нормаль вычисляется на основании треугольника, что может не совпадать с визуальным направлением поверхности.
  • Неоптимизированный поиск ближайшего столкновения: Придётся вручную фильтровать результаты рейкаста, если цель — найти ближайшую точку.

Рекомендации по работе с Trimesh и рейкастом

  1. Использовать упрощённые коллизии: Для интерактивных объектов предпочтительнее заменить сложные меши на комбинацию Box, Sphere или ConvexPolyhedron.
  2. Минимизировать количество треугольников: Чем меньше треугольников, тем выше производительность и стабильность рейкаста.
  3. Проверять нормали и вершины: Тщательная очистка меша от дубликатов и плоских треугольников снижает вероятность пропусков.
  4. Предварительно рассчитывать столкновения: Для статических мешей можно создать вспомогательную структуру для быстрого поиска треугольников, пересекаемых лучом.
  5. Избегать динамического изменения вершин: Любая модификация меша требует пересоздания Trimesh для корректного рейкаста.

Примеры ограничений на практике

const shape = new CANNON.Trimesh(vertices, indices);
const body = new CANNON.Body({ mass: 0 });
body.addShape(shape);
world.addBody(body);

const ray = new CANNON.Ray(from, to);
const result = new CANNON.RaycastResult();

// Проверка пересечения
ray.intersectBody(body, result);

console.log(result.hasHit); // true или false
console.log(result.hitPointWorld); // точка пересечения, может быть неточной
  • В сценах с плотной геометрией иногда result.hasHit остаётся false, даже если визуально луч проходит через объект.
  • На очень тонких плоскостях луч может пересекать треугольники несколько раз, но фиксируется только первый контакт.

Эти ограничения делают работу с Trimesh при рейкасте в Cannon.js подходящей только для статических или низкополигональных объектов, где высокая точность не критична.


Хотя Trimesh обеспечивает гибкость для произвольных форм, понимание этих ограничений критично для разработки эффективных физических симуляций и точных коллизий при использовании рейкастов.