Учитывая нормаль поверхности, найдите вращение для 3D плоскости - PullRequest
17 голосов
/ 19 января 2010

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

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

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

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

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

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

Есть идеи, как создать правильное вращение?

Ответы [ 2 ]

14 голосов
/ 20 января 2010

Несколько полезных вещей о вращениях:

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

Итак, проблема в том, чтобы найти любой набор из трех ортонормированных векторов и расположить их как

| 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 принимает массивы в главном порядке столбцов (а не в главном порядке строк C), поэтому вам может понадобиться транспонировать.

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

Редактировать: я что-то забыл :)

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

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

2 голосов
/ 19 января 2010

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

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];
    }
}

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

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