2013-12-15 13 views
3

Проблема в том, что у меня есть две точки в трехмерном пространстве, где y + вверх, x + справа, а z + - к вам. Я хочу ориентировать цилиндр между ними, который является длиной расстояния между обеими точками, так что оба его центра касаются двух точек. Я получил цилиндр, чтобы перевести его на место в центре двух точек, и мне нужна помощь, которая подходит с матрицей поворота для применения к цилиндру, так что она ориентирована правильно. Моя матрица преобразования для всей вещи выглядит следующим образом:Необходимая матрица вращения для 3D-преобразования opengl

перевод (центральная точка) * rotateX (около X градусов) * rotateZ (некоторые Z градусов)

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

Вот то, что я до сих пор для этого:

mat4 getTransformation(vec3 point, vec3 parent) 
{ 
    float deltaX = point.x - parent.x; 
    float deltaY = point.y - parent.y; 
    float deltaZ = point.z - parent.z; 

    float yRotation = atan2f(deltaZ, deltaX) * (180.0/M_PI); 
    float xRotation = atan2f(deltaZ, deltaY) * (180.0/M_PI); 
    float zRotation = atan2f(deltaX, deltaY) * (-180.0/M_PI); 
    if(point.y < parent.y) 
    { 
     zRotation = atan2f(deltaX, deltaY) * (180.0/M_PI); 
    } 

    vec3 center = vec3((point.x + parent.x)/2.0, (point.y + parent.y)/2.0, (point.z + parent.z)/2.0); 
    mat4 translation = Translate(center); 
    return translation * RotateX(xRotation) * RotateZ(zRotation) * Scale(radius, 1, radius) * Scale(0.1, 0.1, 0.1); 
} 

Я попробовал решение при вниз, но это не похоже на работу на всех

mat4 getTransformation(vec3 parent, vec3 point) 
{ 
    // moves base of cylinder to origin and gives it unit scaling 
    mat4 scaleFactor = Translate(0, 0.5, 0) * Scale(radius/2.0, 1/2.0, radius/2.0) * cylinderModel; 

    float length = sqrtf(pow((point.x - parent.x), 2) + pow((point.y - parent.y), 2) + pow((point.z - parent.z), 2)); 
    vec3 direction = normalize(point - parent); 
    float pitch = acos(direction.y); 
    float yaw = atan2(direction.z, direction.x); 

    return Translate(parent) * Scale(length, length, length) * RotateX(pitch) * RotateY(yaw) * scaleFactor; 
} 

После выполнения кода Я получаю это:

Любая черная точка - это точка с родителем, являющимся точкой, которая породила его (тот, который был перед ним). Я хочу, чтобы ветви соответствовали в точки. В основном я пытаюсь реализовать алгоритм пространственной колонизации для генерации случайного дерева. Я получил большую часть этого, но я хочу сопоставить ветви, чтобы он выглядел хорошо. Я могу использовать GL_LINES только для создания общего соединения, но если я получу эту работу, он будет выглядеть намного красивее. Алгоритм объясняется here.

Вот образ того, что я пытаюсь сделать (простите мои навыки рисования)

+0

Обратите внимание, что отсутствует одна степень свободы: рулон цилиндра вокруг своей оси.Увидев вашу абстрактную формулу, я думаю, вам все равно, какой угол это будет. (Это просто прекрасно, я просто хотел сообщить вам, что есть другие возможности ориентировать цилиндр между двумя точками.) – leemes

+0

Можете ли вы дать мне несколько намеков на другие возможности? Я пытаюсь правильно ориентировать это с 11:00 CMT lol – boddie

+0

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

ответ

2

Ну, есть произвольное число матриц вращения, удовлетворяющие ваши ограничения. Но любой будет делать. Вместо того, чтобы пытаться определить конкретное вращение, мы просто собираемся записать матрицу напрямую. Скажем, ваш цилиндр, когда никакое преобразование не применяется, имеет свою ось вдоль оси Z. Таким образом, вам нужно преобразовать локальное пространство Z в направлении между этими двумя точками. То есть z_t = normalize(p_1 - p_2), где normalize(a) = a/length(a).

Теперь нам нужно всего лишь создать трехмерную координатную базу. Начнем с произвольного вектора, не параллельного z_t. Скажем, один из (1,0,0) или (0,1,0) или (0,0,1); используйте скалярный продукт · (также называемый внутренним или точечным произведением) с z_t и используйте вектор, для которого абсолютное значение является наименьшим, назовем этот вектор u. В псевдокоде:

# Start with (1,0,0) 
mindotabs = abs(z_t · (1,0,0)) 
minvec = (1,0,0) 
for u_ in (0,1,0), (0,0,1): 
    dotabs = z_t · u_ 
    if dotabs < mindotabs: 
     mindotabs = dotabs 
     minvec = u_ 

u = minvec_ 

Тогда вы ортогонолизации, что вектор, что дает локальное преобразование у y_t = normalize(u - z_t · u).

Наконец создать й преобразование, беря декартово произведению x_t = z_t × y_t

Чтобы переместить цилиндр на место, где вы объедините это с матрицей соответствия перевода.

Матрицы преобразования фактически являются просто осями пространства, из которого вы «исходите», записанными, как если бы видели из другого пространства.Таким образом, результирующая матрица, которая является матрицей вращения, которую вы ищете, - это просто векторы x_t, y_t и z_t бок о бок в качестве матрицы. OpenGL использует так называемые однородные матрицы, поэтому вам нужно отнести его к форме 4 × 4 с помощью 0,0,0,1 нижней строки и самой правой колонки.

Что вы можете загрузить затем в OpenGL; если использовать фиксированный functio с помощью glMultMatrix для применения вращения, или если вы используете шейдер для умножения на матрицу, вы, в конце концов, переходите к glUniform.

+0

Я немного смущен тем, что у находится в параграфе 2. Я также не понимаю, что вы подразумеваете под «вектором, для которого абсолютное значение является наименьшим». Какие векторы мы имеем в виду здесь? – boddie

+0

@ user3000845: Я добавил несколько псевдокодов. – datenwolf

0

Начнем с единичной длины цилиндра, который имеет один из его концов, которые я называю C1, в начале координат (обратите внимание, что изображение указывает на то, что ваш цилиндр имеет свой центр в нуле, но вы можете легко преобразовать, что с чего я начинаю). Другой конец, который я называю C2, затем находится в (0,1,0).

Я хотел бы назвать свои две точки в мировых координатах P1 и P2, и мы хотим, чтобы найти C1 на P1 и C2 к P2.

Перевести на английский язык с помощью P1, который успешно находит C1 по адресу P1.

Затем масштабируйте цилиндр distance(P1, P2), так как первоначально он имел длину 1.

Оставшееся вращение может быть вычислено с использованием сферических координат. Если вы не знакомы с этим типом системы координат: это как GPS-координаты: два угла; один вокруг оси полюса (в вашем случае ось Y в мире), которую мы обычно называем рывком, а другой - шаг угол (в вашем случае ось X в пространстве модели). Эти два угла могут быть вычислены путем преобразования P2-P1 (то есть локального смещения P2 по отношению к P1) в сферические координаты. Во-первых повернуть объект с углом наклона вокруг X, затем рыскания вокруг Y.

Нечто подобное будет делать это (псевдо-код):

Matrix getTransformation(Point P1, Point P2) { 
    float length = distance(P1, P2); 
    Point direction = normalize(P2 - P1); 
    float pitch = acos(direction.y); 
    float yaw = atan2(direction.z, direction.x); 

    return translate(P1) * scaleY(length) * rotateX(pitch) * rotateY(yaw); 
} 
+0

Я пробовал ваше решение, и мой код для него выше, но он, похоже, не имел эффекта. – boddie

+0

В обновленном коде используйте 'Масштаб (радиус, длина, радиус)' вместо 'Масштаб (длина, длина, длина)'. Что ты видишь? Я думаю, ничего, из чего вы видите, что не так. Затем попробуйте сыграть с заказом. И постарайтесь сделать один шаг за другим. Сначала попробуйте расположить цилиндр в 'parent', затем масштабируйте его по длине (и если это не удается, вероятно, из-за неправильного порядка матрицы) и так далее. Это так легко, чтобы порядок был неправильным! – leemes

+0

Я опубликовал вид того, что я пытаюсь сделать выше, если это помогает – boddie

0

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

Вызов целевого вектора (один между двумя точками) B. Take -acos (B X/B Y), и это угол первого вращения.

Возьмите B снова, игнорировать компонент Х, и посмотреть на ее проекции в плоскости (Y, Z) плоскости. Возьмите acos (B Z/B Y), и это угол второго вращения.

Смежные вопросы