Высота наклона в точке (3D-столкновение)?

Я стараюсь изо всех сил, чтобы описать это:

У меня есть трехмерная наклонная поверхность, и я хочу знать ее высоту (Z) в любой заданной точке внутри нее. У меня есть 3D-позиции вершин поверхности и 2D-позиция точки. Как узнать, на какой высоте он столкнется, находясь на поверхности?

Я использую GDScript, создавая 3D/2.5D Doom-подобный движок в 2D-движке Godot. Плоские полы и стены работают идеально, но наклоны? Я пробовал все, что мог придумать в течение нескольких месяцев, и ничего не получалось.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот что бы вы сделали:

  • Мы определяем линию, которая является вертикальной и проходит через вашу 2D-точку.

    Допустим, наша 2D-точка равна position. Затем:

    • Точка на линии position дополняется 0 компонентом высоты. Я назову это v0:

      var v0 = Vector3(position.x, position.y, 0.0)
      
    • И направление линии вертикальное, поэтому ноль на обоих компонентах 2D и 1 на компоненте высоты. Я назову это dir:

      var dir = Vector3(0.0, 0.0, 1.0) 
      
  • Мы определяем плоскость тремя точками, которые составляют ваш трехмерный треугольник.

    Допустим, наши 3D-точки — это v1, v2 и v3.

    • По определению они все на плоскости.

    • Мы можем найти нормаль плоскости, используя векторное произведение, например:

      var normal := (v2 - v1).cross(v3 - v1)
      

      Для этого варианта использования нам все равно, если мы перевернули нормаль.

      Ах, но будет удобно убедиться, что он имеет единичную длину:

      var normal := (v2 - v1).cross(v3 - v1).normalized()
      
  • Находим пересечение линии и плоскости. Это наше решение.

    Хотим найти точку на плоскости, я назову ее r. Это такая точка, что вектор, идущий от одной из точек плоскости к ней, перпендикулярен нормали к плоскости. Это означает, что (r - v1).dot(normal) == 0.

    Обратите внимание, что я использую ==, который является оператором сравнения на равенство. Я использую это, потому что это уравнение, а не задание.

    Точка r также должна принадлежать прямой. Мы можем использовать параметрическую форму линии, которая выглядит следующим образом: r = v0 + dir * t. И нам нужно сначала найти, каким должен быть параметр t, чтобы мы могли вычислить r.

    Поэтому мы заменяем r здесь:

    (r - v1).dot(normal) == 0
    

    Что дает нам это:

    ((v0 + dir * t) - v1).dot(normal) == 0
    

    Переставить:

    (dir * t + v0 - v1).dot(normal) == 0
    

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

    (dir * t).dot(normal) + (v0 - v1).dot(normal) == 0
    

    Поскольку t является скаляром, мы можем вынести его из скалярного произведения:

    t * dir.dot(normal) + (v0 - v1).dot(normal) == 0
    

    Вычтите (v0 - v1).dot(normal) с обеих сторон:

    t * dir.dot(normal) == - (v0 - v1).dot(normal)
    

    Мы можем переместить этот отрицательный знак внутри скалярного произведения:

    t * dir.dot(normal) == (v1 - v0).dot(normal)
    

    И разделите обе части на dir.dot(normal):

    t == (v1 - v0).dot(normal) / dir.dot(normal)
    

    Таким образом, наша точка r:

    var r := v0 + dir * (v1 - v0).dot(normal) / dir.dot(normal)
    

    Затем вы можете прочитать компонент высоты r, который является вашим ответом.

    Кстати, у Годо есть почти такой метод. Но он пересекает луч, а не прямую: intersect_ray класса Plane.

Простите меня, но мне очень трудно это понять. v0 — позиция игрока, v1, v2 и v3 — вершины, все в порядке. "Точка на прямой дополнена 0 по компоненте высоты. Я назову ее v0."; «И направление линии вертикальное, поэтому ноль на обоих компонентах 2D и 1 на компоненте высоты. Я назову это направление». Что это значит? Как определяется директор? Затем идет r, сначала он равен нулю, потом нет... мне просто использовать последний? Я действительно не был готов к такому сложному ответу! Что я понял + ошибка: i.imgur.com/Hf7cUsV.png

weg 01.02.2023 13:10

@weg Я изменил ответ.

Theraot 01.02.2023 18:43

Большое спасибо! Вот как это выглядит у меня, правильно ли это?: i.imgur.com/9Uh27tW.png Вроде как по возрастанию и по убыванию работает, но конечный результат может быть либо правильным, либо рандомно заниженным. Похоже, что это напрямую связано с порядком расположения вершин. (0,100,50 - 0,0,0 - 100,100,100) работает отлично, (0,0,0 - 100,100,100 - 100,0,50) например -1400 ед.

weg 01.02.2023 22:37

@weg Я попробовал эти значения с помощью (50, 50, 0), и у меня это работает. Изменение порядка перевернет нормаль плоскости, но решение должно быть таким же. Однако, увидев ваш код, я заметил кое-что, что я упустил из виду: использование var d = v1.dot(normal) может дать вам отрицательный результат, но var d = v1.project(normal).length() всегда будет положительным. поэтому используйте вместо этого var d = v1.dot(normal), я исправляю это в ответе. Обновлено: я решил удалить d из ответа, чтобы избежать путаницы.

Theraot 01.02.2023 22:58

Какое-то время это было отклонение примерно на 20-40 единиц, положительное или отрицательное, но в процессе добавления решения (я собирался получить новую высоту в самой нижней точке и вычесть ее из нового роста игрока) он решил работать из ниоткуда отлично! Не знаю почему. В любом случае, я добавил кредит для вас в моем проекте! Большое вам спасибо за вашу помощь. Ваше решение невероятно лаконично и изысканно. Вы должны быть прекрасным кодером! Итоговый код для интересующихся: i.imgur.com/fJCloUR.png

weg 02.02.2023 11:50

Другие вопросы по теме