ContactPoint: нормаль, глубина проникновения, точки контакта

В библиотеке Cannon.js система столкновений строится вокруг объекта контакта — ContactEquation. Каждый экземпляр такого объекта содержит параметры, описывающие конкретную точку соприкосновения двух тел. В рамках одного столкновения может существовать несколько контактных точек.

Контактная точка (ContactPoint) включает:

  • Нормаль контакта
  • Глубину проникновения
  • Точки контакта на каждом теле
  • Вспомогательные векторы для расчёта импульса

Эти данные используются решателем (solver) для вычисления импульса, который устраняет пересечение тел и формирует корректную реакцию.


Нормаль контакта

Определение

Нормаль контакта — это единичный вектор, направленный от первого тела ко второму и перпендикулярный поверхности столкновения.

В Cannon.js нормаль хранится в свойстве:

contact.ni

где:

  • ni — normal impulse direction
  • всегда нормализованный вектор (Vec3)
  • направление: от bi к bj

Геометрический смысл

Нормаль определяет:

  • направление реакции столкновения
  • направление импульса, устраняющего проникновение
  • базис для расчёта трения

Если два тела пересекаются, импульс будет прикладываться вдоль нормали в противоположном направлении проникновения.

Роль в вычислениях

Скалярная проекция относительной скорости на нормаль:

vn = (vj - vi) · ni

где:

  • vi, vj — скорости тел в точке контакта
  • ni — нормаль

Если vn < 0, тела движутся навстречу друг другу и требуется реакция.

Нормаль используется:

  • для расчёта нормального импульса
  • для вычисления силы трения (построение касательных векторов)
  • для стабилизации контакта

Глубина проникновения

Физический смысл

Глубина проникновения — это величина, на которую объекты пересеклись друг с другом.

В Cannon.js она не хранится напрямую как отдельное поле, а вычисляется через:

  • расстояние между контактными точками
  • параметр penetration внутри алгоритма narrowphase
  • величину g в уравнении контакта

В объекте ContactEquation используется выражение:

contact.penetrationVec

или косвенно через вычисление:

g = (xj + rj - xi - ri) · ni

где:

  • xi, xj — позиции центров масс
  • ri, rj — локальные радиус-векторы до точки контакта
  • ni — нормаль

Если g < 0, наблюдается проникновение.

Влияние глубины проникновения

Глубина влияет на:

  • величину корректирующего импульса
  • скорость устранения пересечения
  • устойчивость симуляции

В решателе применяется стабилизация Баумгарте:

bias = -β * penetration / dt

где:

  • β — коэффициент стабилизации
  • dt — шаг симуляции

Чем глубже проникновение, тем сильнее корректирующий импульс.


Точки контакта

Локальные координаты

Каждый ContactEquation хранит точки контакта относительно центров масс тел:

contact.ri
contact.rj

где:

  • ri — вектор от центра масс тела A до точки контакта
  • rj — аналогично для тела B
  • тип — Vec3

Это локальные смещения, уже переведённые в мировые координаты.

Вычисление мировой позиции контакта

Мировая точка контакта для тела A:

contactPointA = bodyA.position + ri

Для тела B:

contactPointB = bodyB.position + rj

Эти точки:

  • используются для расчёта момента силы
  • определяют плечо приложения импульса
  • влияют на вращательное движение

Связь точек контакта и вращения

Импульс прикладывается не в центр масс, а в точку контакта. Поэтому возникает вращательный эффект.

Крутящий момент вычисляется как:

τ = r × F

где:

  • r — вектор ri или rj
  • F — импульс вдоль нормали

Даже небольшой импульс может вызвать значительное вращение при большом плече.


Множественные контактные точки

При столкновении сложных форм (например, box–box) генерируется несколько контактных точек:

  • до 4 точек для параллельных граней
  • 1–2 точки для касания рёбер
  • 1 точка для касания вершины

Каждая точка создаёт отдельный ContactEquation.

Почему это важно

Одна точка контакта не обеспечивает устойчивость. Например:

  • если куб стоит на плоскости
  • при одной точке он будет балансировать
  • при нескольких — формируется устойчивая опора

Решатель обрабатывает все контакты итеративно.


Жизненный цикл ContactPoint

  1. Broadphase — определение потенциальных пар тел
  2. Narrowphase — вычисление точек контакта
  3. Создание ContactEquation
  4. Добавление в список уравнений мира
  5. Решение системой ограничений
  6. Очистка перед следующим шагом

Контакт существует только в рамках одного шага симуляции.


Взаимодействие с трением

После создания нормального контакта создаются уравнения трения:

  • 2 касательных вектора
  • 2 уравнения трения на каждую точку

Касательные вычисляются как ортогональные нормали:

t1 ⟂ ni
t2 = ni × t1

Таким образом, одна точка контакта создаёт:

  • 1 нормальное уравнение
  • 2 уравнения трения

Доступ к контактам через события

В Cannon.js можно получить данные контакта через событие:

body.addEventListener("collide", function(event) {
    const contact = event.contact;
});

Доступные данные:

  • contact.ni
  • contact.ri
  • contact.rj
  • contact.bi
  • contact.bj

Это позволяет:

  • определять направление удара
  • рассчитывать силу столкновения
  • создавать эффекты (звук, частицы)

Числовая устойчивость и особенности

Проблема дрожания (jitter)

При малой глубине проникновения возможны:

  • колебания
  • микроподпрыгивания
  • нестабильность стопки объектов

Причины:

  • недостаточное число итераций solver
  • слишком большой dt
  • неточная нормаль

Решения

  • увеличение world.solver.iterations
  • уменьшение шага времени
  • корректная настройка contactEquationStiffness
  • использование contactEquationRelaxation

Оптимизация количества контактов

Избыточные контактные точки увеличивают нагрузку на решатель.

Методы оптимизации:

  • reduction contact points
  • объединение близких точек
  • ограничение максимального числа контактов

В Cannon.js часть этой логики реализована в narrowphase.


Визуализация ContactPoint

Для отладки полезно визуализировать:

  • нормаль (как стрелку)
  • точки contactPointA и contactPointB
  • глубину проникновения

Обычно используется связка с Three.js:

  • создаётся ArrowHelper
  • точка отображается маленькой сферой
  • глубина — линией между точками

Это позволяет анализировать:

  • корректность нормалей
  • стабильность контактов
  • поведение при сложных столкновениях

Влияние формы на ContactPoint

Тип формы влияет на способ вычисления контактов:

Пара форм Метод вычисления
Sphere–Sphere аналитическая формула
Sphere–Plane проекция центра
Box–Box SAT (Separating Axis Theorem)
Convex–Convex SAT + clipping

Для convex форм используется алгоритм клиппинга граней, генерирующий множество контактных точек.


Внутренняя структура ContactEquation

Основные поля:

  • bi, bj — тела
  • ni — нормаль
  • ri, rj — радиус-векторы
  • restitution — коэффициент упругости
  • enabled — активность контакта

ContactEquation наследуется от базового Equation, который содержит:

  • minForce
  • maxForce
  • параметры stiffness/relaxation

Расчёт импульса

Общая формула импульса:

λ = -(Jv + bias) / (JM⁻¹Jᵀ + ε)

где:

  • J — якобиан (включает нормаль и r × n)
  • M — матрица масс
  • ε — регуляризация
  • bias — коррекция проникновения

ContactPoint формирует строки якобиана на основе:

  • нормали
  • радиус-векторов

Связь с restituion (упругостью)

Если задан коэффициент упругости:

contact.restitution

то учитывается относительная скорость вдоль нормали:

vn_after = -restitution * vn_before

Это влияет только на компонент вдоль ni.


Практические выводы для разработки

ContactPoint является ключевым элементом системы столкновений:

  • нормаль определяет направление реакции
  • глубина задаёт величину коррекции
  • точки контакта создают вращательный эффект
  • несколько точек обеспечивают устойчивость

Понимание структуры ContactEquation позволяет:

  • управлять стабильностью симуляции
  • анализировать ошибки столкновений
  • реализовывать продвинутые механики (удары, скольжение, липкость, нестандартные материалы)

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