Comments 17
Для перехода из (x, y, z) к (x', y', z'), где z' = d
нужна не матрица поворота, а матрица преобразования координат, которая составлена из ортов x, y, z, заданных в координатах (x', y', z'). Которую, в свою очередь, можно найти обратив матрицу, составленную из ортов x', y', z', заданных в x, y, z.

Возможно, я сноб, но мне почему-то казалось, что это очевидно. Матрица поворота здесь не нужна.
Это будут те же самые арифметические действия, только «в профиль». Вам вычисление компонентов векторного произведения AzBx — AxBz не напоминает вычисление детерминанта по минору?

Тут вопрос не в том какие это будут действия, а в том как к ним идти

Вы как учитель школьный — толкаете единственно верный ход мыслей?

P.S. Есть разные инструменты, подходы и интерпретации. Собственно, разный тип мышления выбирает тот интсрумент, который ему понятен.
Ну, если человек не прогуливал на первом курсе линейную алгебру, то он знает оба пути, и легко видит, что названный мной — более прямой.
Пытаясь расшифровать ваше послание, надо вот такую матрицу вычислить?
    | x.x’ y.x’ z.x’ | -1
M = | x.y’ y.y’ z.y’ |
    | x.z’ y.z’ z.z’ |
Которую, в свою очередь, можно найти обратив матрицу

В большинстве случаев (когда нет масштабирования и оси ортогональны) достаточно транспонировать, что ещё дешевле.
Насколько устойчива схема, когда вектора d и z почти противоположны? Ведь c будет стремиться к -1, а k, соответственно, к бесконечности.

Кстати, схема расчёта поворота ещё сильнее упрощается в плане подготовки преобразований на CPU перед отдачей в GPU-конвейер и размещения в памяти, если вычислять не матрицу поворота, а кватернион:

vec4 quaternionByTwoVectors(vec3 d, vec3 z) {
    const vec3 vec = cross(d, z);
    const float c = dot(d, z);
    const float k = sqrt((1 + c) * 2);

    return vec4(c.x / k, c.y / k, c.z / k, k / 2.0f);
}


ну и дальше в вертексном шейдере, при трансформации просто перемножить этот кватернион с координатой каждой вершины (что чуть дороже, чем матричное умножение)

vec3 rotateVectorByQuaternion(vec4 q, vec3 v) {
  return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
}


P.S.: может, кто подизассемблировать итоговый код, сколько mul/add операций выходит в rotateVectorByQuaternion против перемножения mat3x3 на vec3?
И в итоге вместо понятной rotationAxisAngle с понятными любому аргументами мы имеем rotationAxisCosSin с косинусами и синусами вместо аргументов. Да, практически любому ясно как это работает. Но читабельность кода превращается в г…
Поэтому если кусок кода не критичной по скорости — читабельность в приоритете, оставляем ось и угол.
Если кусок кода критичен по скорости — реализуем всю математику внутри функции не разбивая её на кучу других.
А вообще: преждевременная оптимизация — зло. Именно поэтому ни в одном игровом движке мат либа не содержит таких извращений, а дает простые и понятные методы.
А почему если есть косинус не вычислить синус используя основное тригонометрическое тождество? Или корень более тяжелая операция чем векторное произведение с последующей нормализацией?

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

4 операции умножения в быстром алгоритме вычисления обратного корня (1/sqrt(x)): en.wikipedia.org/wiki/Fast_inverse_square_root

Но пишут, что sse или avx инструкции (rsqrtss и vrsqrtss соответственно) работают быстрее (за 3-5 тактов)

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


Вот как раз при нормализации можно и обратный использовать, ежели точность устраивает (а она может и не устроить).

Как минимум, точность убивается в ноль. Допустим, у нас есть угол в районе 0, скажем, 10^-4. Его косинус находится где-то в районе 1, квадрат соответственно тоже. Машинная точность float в районе 1 где-то 10^-8. вычитаем из 1 квадрат косинуса, погрешность остается не меньше 10^-8. берем корень и наша погрешность внезапно становится 10^-4, что соразмеримо с синусом угла, который хотим измерить. В итоге функция измерения синуса через косинус имеет явное квантование в районе 0, что часто неприменимо. А если считать на 16битных числах, то там вообще погрешность 10^-2, что уже вообще использовать нельзя почти ни для чего
Only those users with full accounts are able to leave comments. Log in, please.