3D-вращение прямоугольной призмы

Я возился с обработкой, создавая некоторые 3D-объекты, и я создал рекурсивный метод, который, как я надеюсь, сгенерирует объект, похожий на палец, с большим количеством степеней свободы. Идея состоит в том, что каждый сегмент, отображаемый в виде прямоугольника, создает новый сегмент, так что его нижняя грань находится в том же месте, что и нижняя грань предыдущего сегмента (показано ниже).Segment Finger

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

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

void finger(float x, float y, float z, float rx, float ry, float rz, float r, 
float h){
  translate(x,-y,z);
  rotateX(rx);
  rotateY(ry);
  rotateZ(rz);
  translate(0,-h/2,0);
  box(r,h,r);
  translate(0,h/2,0);
  rotateZ(-rz);
  rotateY(-ry);
  rotateX(-rx);
  translate(-x,y,-z);
  if(r>10){
    finger(x+h*sin(rx)*sin(ry)*cos(rz)+h*cos(rx)*sin(rz),y-h*sin(rx)*sin(ry)*sin(rz)+  
    h*cos(rx)*cos(rz),z-h*sin(rx)*cos(ry),rx+deltaX,ry+deltaY,rz+deltaZ,r-4,h-5);
  }
}

[Редактировать: MCVE Ниже, включает мой код для перемещения в 3D-пространстве и инициализацию настройки/переменной] [Редактировать (2): Обновлено MCVE, изменить deltaX, deltaY, deltaZ для движения]

float deltaX,deltaY,deltaZ;
void setup(){
  deltaX=0;
  deltaY=0;
  deltaZ=0;
  fullScreen(P3D);
}
void draw(){
  noStroke();
  camera(-600, -400, -600, 0, -300, 0, 0, 1, 0);
  background(#51B6F5);
  directionalLight(255,255,255,0.5,1,0.5);
  directionalLight(255,255,255,-0.5,1,-0.5);
  box(400,10,400);
  tree(0,0,0,0,0,0,40,100);
}
void tree(float x, float y, float z, float rx, float ry, float rz, float r, float h){
  translate(x,-y,z);
  rotateX(rx);
  rotateY(ry);
  rotateZ(rz);
  translate(0,-h/2,0);
  box(r,h,r);
  translate(0,h/2,0);
  rotateZ(-rz);
  rotateY(-ry);
  rotateX(-rx);
  translate(-x,y,-z);
  if(r>10){
    tree(x+h*sin(rx)*sin(ry)*cos(rz)+h*cos(rx)*sin(rz),y-h*sin(rx)*sin(ry)*sin(rz)+h*cos(rx)*cos(rz),z-h*sin(rx)*cos(ry),rx+deltaX,ry+deltaY,rz+deltaZ,r-4,h-5);
  }
}

person cmxu    schedule 08.01.2016    source источник
comment
Можете ли вы опубликовать MCVE вместо только вашего метода finger()?   -  person Kevin Workman    schedule 08.01.2016
comment
Конечно, добавил. Он просто включает в себя мой код для движения в 3D-пространстве, прямоугольник, нарисованный в методе отрисовки, и инициализацию переменных.   -  person cmxu    schedule 08.01.2016
comment
Я не пытаюсь показаться неприятным, но это слишком много кода для MCVE. Можете ли вы опубликовать базовый скетч с простой функцией draw(), которая вызывает вашу функцию finger()? Вам не нужны какие-либо дополнительные элементы взаимодействия с пользователем. Когда я пытаюсь получить минимум из вашего примера, я получаю черный набросок. Просто жестко закодируйте переменные, необходимые для демонстрации проблемы, и оставьте все остальное.   -  person Kevin Workman    schedule 08.01.2016
comment
Извините, у меня нет большого опыта работы с MCVE, я думаю, что должно быть лучше?   -  person cmxu    schedule 08.01.2016


Ответы (1)


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

Но подумайте об этом так:

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

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

void tree2(float x, float y, float z, float rx, float ry, float rz, float r, float h){

  //assume current origin is at bottom of box

  //rotate around bottom
  rotateX(rx);
  rotateY(ry);
  rotateZ(rz);

  //move to center
  translate(0,-h/2,0);

  //draw the box
  box(r,h,r);

  //move origin to the top of the box- the bottom of the next box
  translate(0,-h/2,0);

  //draw the next box
  if(r>10){
    tree2(x+h*sin(rx)*sin(ry)*cos(rz)+h*cos(rx)*sin(rz),y-h*sin(rx)*sin(ry)*sin(rz)+h*cos(rx)*cos(rz),z-h*sin(rx)*cos(ry),rx+deltaX,ry+deltaY,rz+deltaZ,r-4,h-5);
  }
}

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

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

person Kevin Workman    schedule 08.01.2016
comment
Большое спасибо! Это выглядит почти так, как я хотел. Тем не менее, я изначально хотел, чтобы все углы были одинаковыми между сегментами, но, как вы можете видеть, если вы поворачиваете каждый раз и увеличиваете углы каждый раз, когда угол становится все больше и больше, поэтому у меня были обратные преобразования. Это, безусловно, большой шаг вперед, и если в какой-то момент я сделаю с ним что-то классное, я обязательно вернусь и поделюсь этим здесь! Спасибо еще раз! - person cmxu; 09.01.2016
comment
На самом деле, это легко исправить небольшую проблему, о которой я упоминал выше, просто удалив rx, ry, rz из рекурсивных вызовов. - person cmxu; 09.01.2016
comment
@Changming Да, это то, что я собирался сказать! Спасибо, что нашли время, чтобы сделать MCVE, было довольно весело поиграть. - person Kevin Workman; 09.01.2016