Подтвердить что ты не робот

Для нормальной поверхности найдите вращение для 3D-плоскости

Итак, у меня есть 3D-плоскость, описанная двумя векторами:

P: точка, лежащая на плоскости N: нормаль поверхности для плоскости

И у меня очень большой плоский квадрат Polygon, который я хочу отобразить, чтобы представить этот Plane. Я могу легко перевести многоугольник в заданную точку, но тогда мне нужно найти правильное вращение, чтобы применить нормальную поверхность на самом деле как нормальную поверхность.

Я попробовал метод, упомянутый еще где-то:

1) Возьмем любой параллельный вектор (V) в нормаль (N) и возьмем поперечное произведение (W1)
2) Возьмем теперь перекрестное произведение (W1) и (N) (W2), а это вектор (V '), который лежит на плоскости

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

Любые идеи о том, как сгенерировать правильное вращение?

4b9b3361

Ответ 1

Некоторые полезные сведения о поворотах:

  • Любые три ортонормированные векторы, расположенные в виде строк, определяют преобразование в новый базис (вращение в этот базис).
  • Транспонирование любого вращения является его обратным.
  • Таким образом, любые три ортонормированных вектора, расположенные в виде столбцов, определяют поворот из некоторого базиса в ваш "мировой" опорный кадр.

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

| x1 x2 x3  0 |
| y1 y2 y3  0 |
| z1 z2 z3  0 |
|  0  0  0  1 |

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

Мы можем, очевидно, использовать ваш нормальный как (x1, y1, z1), но проблема в том, что система имеет бесконечное множество решений для остальных двух векторов (хотя знание одного из них дает вам другое, как перекрестное произведение). Следующий код должен дать устойчивый вектор, перпендикулярный (x1, y1, z1):

float normal[3] = { ... };

int imin = 0;
for(int i=0; i<3; ++i)
    if(std::abs(normal[i]) < std::abs(normal[imin]))
        imin = i;

float v2[3] = {0,0,0};
float dt    = normal[imin];

v2[imin] = 1;
for(int i=0;i<3;i++)
    v2[i] -= dt*normal[i];

Это в основном использует ортогонализацию Грэма-Шмидта с размерностью, которая уже наиболее ортогональна нормальному вектору. v3 можно затем получить, взяв поперечное произведение normal и v2.

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

Боюсь, что я не тестировал выше, я просто набил его из некоторого кода, который я написал некоторое время назад, и адаптировал его к вашей проблеме! Надеюсь, я не забыл никаких подробностей.

Изменить: я что-то забыл:)

Вышеприведенная матрица предполагает, что ваш нормаль к многоугольнику находится вдоль оси x, и у меня есть скрытое подозрение, которого не будет, все, что вам нужно сделать, это поместить "нормальный" вектор в правильный столбец вращения матрицы и v2/v3 в двух других столбцах. Итак, если нормаль к вашему многоугольнику находится вдоль оси z, то нормаль идет в третьем столбце, а v2/v3 - в первых двух столбцах.

Извините, если это вызывает путаницу.

Ответ 2

Не знаете, какой метод вы используете для рендеринга, но заимствуете из OpenSceneGraph matrix:

void Matrix_implementation::makeLookAt(const Vec3d& eye,const Vec3d& center,const Vec3d& up)
{
    Vec3d f(center-eye);
    f.normalize();
    Vec3d s(f^up);
    s.normalize();
    Vec3d u(s^f);
    u.normalize();

    set(
        s[0],     u[0],     -f[0],     0.0,
        s[1],     u[1],     -f[1],     0.0,
        s[2],     u[2],     -f[2],     0.0,
        0.0,     0.0,     0.0,      1.0);

    preMultTranslate(-eye);
}

inline void Matrixd::preMultTranslate( const Vec3d& v )
{
    for (unsigned i = 0; i < 3; ++i)
    {
        double tmp = v[i];
        if (tmp == 0)
            continue;
        _mat[3][0] += tmp*_mat[i][0];
        _mat[3][1] += tmp*_mat[i][1];
        _mat[3][2] += tmp*_mat[i][2];
        _mat[3][3] += tmp*_mat[i][3];
    }
}

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