Пересечения лучей

В CesiumJS луч (ray) рассматривается как математическая сущность, описываемая начальной точкой и направлением. Он используется для определения пересечений с объектами сцены: поверхностью земного эллипсоида, тайлами 3D Tiles, примитивами, сущностями и пользовательской геометрией.

Луч задаётся структурой, содержащей:

  • origin — точка начала (Cesium.Cartesian3)
  • direction — нормализованный вектор направления
const ray = new Cesium.Ray(
    camera.positionWC,
    camera.directionWC
);

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


Формирование луча из камеры

На практике чаще всего луч формируется из координат пикселя экрана через камеру сцены.

const ray = viewer.camera.getPickRay(screenPosition);

Или более низкоуровневый вариант:

const ray = Cesium.Ray.fromCamera(camera, screenPosition);

Где screenPosition — координаты в системе canvas (x, y).

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


Пересечение луча с эллипсоидом Земли

Cesium использует математическую модель WGS84 эллипсоида. Для пересечений с ним применяется:

const intersection = Cesium.IntersectionTests.rayEllipsoid(
    ray,
    Cesium.Ellipsoid.WGS84
);

Возвращаемое значение — параметр t, который подставляется в уравнение луча:

[ P(t) = origin + t direction]

const point = Cesium.Ray.getPoint(ray, intersection);

Ключевая особенность: пересечение может отсутствовать, если луч направлен в космос или параллелен поверхности.


Работа с поверхностью Globe

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

const cartesian = viewer.scene.globe.pick(ray, viewer.scene);

Поведение метода:

  • учитывает рельеф (terrain provider)
  • возвращает первую точку пересечения
  • работает только с земной поверхностью

Если требуется проверка наличия пересечения без извлечения точки:

const isHit = Cesium.defined(
    viewer.scene.globe.pick(ray, viewer.scene)
);

Пересечения с 3D Tiles

Для сцен с 3D Tiles используется:

const result = viewer.scene.pickFromRay(ray);

или расширенный вариант:

const result = viewer.scene.drillPickFromRay(ray);

Отличия методов:

  • pickFromRay — возвращает первый найденный объект
  • drillPickFromRay — возвращает список всех пересечённых объектов вдоль луча

Структура результата pickFromRay

Результат может содержать:

  • object — объект сцены (tileset, entity, primitive)
  • position — Cartesian3 координата пересечения
  • primitive — низкоуровневый графический объект
  • id — связанный entity
if (result) {
    console.log(result.position);
    console.log(result.object);
}

Пересечения с примитивами

Для геометрических примитивов Cesium использует унифицированный механизм picking через сцены:

const picked = viewer.scene.pick(screenPosition);

Однако при работе с лучами применяется:

const result = viewer.scene.pickFromRay(ray);

Особенность: не все примитивы поддерживают точное геометрическое пересечение; некоторые используют bounding volumes.


Математические пересечения через IntersectionTests

Cesium включает набор низкоуровневых утилит для аналитической геометрии.

Пересечение луча и плоскости

const plane = Cesium.Plane.fromPointNormal(
    planePoint,
    planeNormal
);

const intersection = Cesium.IntersectionTests.rayPlane(ray, plane);

Результат — параметр t вдоль луча.


Пересечение луча и треугольника

const result = Cesium.IntersectionTests.rayTriangle(
    ray,
    vertexA,
    vertexB,
    vertexC,
    true, // cull backface
    intersectionResult
);

Применение:

  • кастомные меши
  • анализ геометрии
  • физические симуляции

Пересечение луча и сферы/эллипсоида

const t = Cesium.IntersectionTests.raySphere(ray, sphere);

или

const t = Cesium.IntersectionTests.rayEllipsoid(ray, ellipsoid);

Приоритеты пересечений в сцене

При работе через Scene методы Cesium использует внутреннюю систему приоритетов:

  1. 3D Tiles (наиболее детализированные объекты)
  2. Primitives
  3. Entities
  4. Terrain (globe)
  5. Imagery layers (не всегда участвуют в ray picking)

Drill-пересечения вдоль луча

Метод:

const results = viewer.scene.drillPickFromRay(ray);

возвращает массив пересечений, отсортированный по глубине.

Типичная структура:

[
  { object, position, primitive },
  { object, position, primitive },
  ...
]

Особенность: порядок соответствует увеличению расстояния от камеры.


Практика: выбор объекта под курсором

Классическая схема построения луча из координаты мыши:

const ray = viewer.camera.getPickRay(movement.position);
const result = viewer.scene.pickFromRay(ray);

if (result && result.position) {
    const cartesian = result.position;
}

Ограничения и особенности точности

Пересечения зависят от нескольких факторов:

  • уровень детализации 3D Tiles
  • включён ли terrain exaggeration
  • выбранный depth buffer
  • наличие batching у примитивов
  • тип материалов (opaque/transparent)

Важное наблюдение: прозрачные объекты могут быть пропущены при стандартном pickFromRay из-за оптимизаций глубины.


Лучи и координатные системы

Cesium активно использует несколько систем координат:

  • Cartesian3 (основная внутренняя)
  • Cartographic (широта, долгота, высота)
  • Window coordinates (экран)

Переходы:

const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
const cartesianBack = Cesium.Cartesian3.fromRadians(
    cartographic.longitude,
    cartographic.latitude,
    cartographic.height
);

Луч всегда оперирует Cartesian3, поэтому преобразования являются обязательной частью цепочки вычислений.


Обработка отсутствия пересечения

Любой метод пересечения может вернуть undefined.

Типовые сценарии:

  • луч направлен вверх в пустое пространство
  • объект находится за пределами камеры
  • геометрия отфильтрована системой LOD
  • пиксель вне canvas
const hit = viewer.scene.globe.pick(ray, viewer.scene);

if (!Cesium.defined(hit)) {
    // пересечения нет
}

Глубинные особенности ray casting в Cesium

Система пересечений в Cesium строится на комбинации:

  • аналитической геометрии (IntersectionTests)
  • GPU picking (color picking / depth picking)
  • bounding volume hierarchies (BVH)
  • tile streaming системы 3D Tiles

Следствие: результат пересечения не всегда является чисто математическим, часто он оптимизирован под производительность.


Использование лучей в кастомных алгоритмах

Луч применяется не только для выбора объектов, но и для:

  • построения линий видимости (line of sight)
  • анализа перекрытия объектов
  • расчёта теней в пользовательских шейдерах
  • симуляции трассировки

Пример проверки видимости:

const ray = new Cesium.Ray(origin, direction);
const blocked = viewer.scene.pickFromRay(ray);

const visible = !Cesium.defined(blocked);

Комбинирование terrain и 3D Tiles пересечений

Для комплексного анализа сцены часто комбинируются методы:

const terrainHit = viewer.scene.globe.pick(ray, viewer.scene);
const objectHit = viewer.scene.pickFromRay(ray);

const closest = Cesium.Cartesian3.distance(
    camera.positionWC,
    terrainHit || objectHit.position
);

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