3D — модель в HTML/CSS; Вычислите вращение треугольника Эйлера

#javascript #css #3d #geometry #rotation

Вопрос:

TLDR; Учитывая набор вершин треугольника и вектор нормали (все в единичном пространстве), как рассчитать углы поворота Эйлера X, Y, Z треугольника в мировом пространстве?

Я пытаюсь отобразить 3D — модель в формате HTML- с реальными HTML-тегами и CSS-преобразованиями. Я уже загрузил файл OBJ в экземпляр класса Javascript.

Модель триангулирована. Моя первая цель — просто отобразить треугольники в виде плоскостей (HTML-элементы прямоугольные) — позже я буду «вырезать» треугольные фигуры с помощью CSS-пути клипа.

Я действительно изо всех сил пытаюсь понять и правильно повернуть треугольники модели.

Я думал, что матрица вращения может мне помочь, но мой единственный опыт работы с ними заключается в том, что у меня уже есть вектор вращения, и мне нужно преобразовать его и отправить в WebGL. На этот раз нет WebGL (или учебных пособий), чтобы упростить задачу.

В следующем отрывке показано создание/»визуализация» лиц. Я использую нормальное лицо в качестве поворота, но я знаю, что это неправильно.

 for (const face of _obj.faces) {

  const vertices = face.vertices.map(_index => _obj.vertices[_index]);

  const center = [
    (vertices[0][0]   vertices[1][0]   vertices[2][0]) / 3,
    (vertices[0][1]   vertices[1][1]   vertices[2][1]) / 3,
    (vertices[0][2]   vertices[1][2]   vertices[2][2]) / 3
  ];

  // Each vertex has a normal but I am just picking the first vertex' normal
  // to use as the 'face normal'.

  const normals = face.normals.map(_index => _obj.normals[_index]);
  const normal = normals[0];

  // HTML element creation code goes here; reference is 'element'.

  // Set face position (unit space)

  element.style.setProperty('--posX', center[0]);
  element.style.setProperty('--posY', center[1]);
  element.style.setProperty('--posZ', center[2]);

  // Set face rotation, converting to degrees also.

  const rotation = [
    normal[0] * toDeg,
    normal[1] * toDeg,
    normal[2] * toDeg,
  ];

  element.style.setProperty('--rotX', rotation[0]);
  element.style.setProperty('--rotY', rotation[1]);
  element.style.setProperty('--rotZ', rotation[2]);
}
 

CSS сначала переводит грань на X,Y,Z, затем поворачивает ее на X,Y,Z в этом порядке.

Я думаю, что мне нужно «разложить» вращение моих треугольников на отдельные вращения по осям — то есть повернуть на X, затем на Y, затем на Z, чтобы получить правильное вращение в соответствии с гранью модели.

Я понимаю, что нормальный вектор дает мне ориентацию, но не вращение вокруг себя — мне нужно это рассчитать. Я думаю, что мне нужно определить вектор вдоль одной стороны треугольника и пересечь его с нормалью, но это то, в чем я не уверен.

Я часами рассматривал подобные вопросы на SO, но я недостаточно умен, чтобы понять или заставить их работать на меня.

Можно ли описать, какие шаги предпринять без уравнений Latex? Я хорошо разбираюсь в псевдокоде, но мне сильно не хватает математических навыков.

Полный код находится здесь: https://whoshotdk.co.uk/cssfps/ (просмотр исходного кода HTML) Функция построения сетки находится в строке 422.

Файл OBJ находится здесь: https://whoshotdk.co.uk/cssfps/data/model/test.obj Файл блендера находится здесь: https://whoshotdk.co.uk/cssfps/data/model/test.blend

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

Мир устроен так, что-X слева,- Y вверху,- Z на экране.

Спасибо!

Ответ №1:

Если у вас есть плоскость и вы хотите повернуть ее в том же направлении, что и некоторую нормаль, вам нужно определить углы между вектором нормали этой плоскости и вектором нормали, который вы хотите. Углы Эйлера между двумя 3D-векторами могут быть сложными, но в этом случае начальная нормаль к плоскости всегда должна быть одинаковой, поэтому я предположу, что нормаль к плоскости начинает указывать на положительное X, чтобы упростить математику.

Вы также, вероятно, захотите повернуть перед переводом, чтобы все было проще, так как вы будете вращаться вокруг начала системы координат.

Принимая Генеральной 3D вращение матрица (все три 3D вращение матрицы перемножать, вы можете найти его на странице Википедии) и нанесения его на вектор (1,0,0), то тогда вы можете получить уравнения для трех углов A, B и C, необходимых для поворота вектора на вектор (х,у,Z). Это приводит к:

x = cos(a)*cos(b)

y = sin(a)*cos(b)

z = -грех(b)

Затем переставьте эти уравнения, чтобы найти a, b и c, которые будут тремя нужными вам углами (три значения массива вращения соответственно).:

a = atan(y/x)

b = asin(-z)

c = 0

Итак, в вашем коде это будет выглядеть так:

 const rotation = [
    Math.atan2(normal[1], normal[0]) * toDeg,
    Math.asin(-normal[2]) * toDeg,
    0
];
 

Возможно, вам потребуется использовать другую матрицу вращения (если порядок поворотов не такой, как вы ожидали) или другой начальный вектор (хотя вы можете просто использовать этот метод, а затем выполнить дополнительный поворот на 90 градусов, если каждая плоскость фактически начинается в положительном направлении Y, например).