矩形棱柱的 3D 旋转

3D-Rotation of a Rectangular Prism

我正在处理创建一些 3D 对象的过程,我创建了一个递归方法,希望生成具有更多自由度的手指状对象。这个想法是,显示为一个框的每个段都会生成一个新段,以便它的底面与前一个段的底部位于相同的位置(如下所示)。

问题是,当我尝试旋转多个轴时(即将 deltaX 和 deltaZ 都设置为 0.3),我的算法失败了,我得到了一些奇怪的东西(如下所示)。

我使用了旋转矩阵来尝试计算新线段的基点应基于旧线段的位置,它仅适用于 1 个旋转轴但在多个旋转轴上失败(数学在 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 中移动的代码 space,以及 setup/variable 初始化] [编辑(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);
  }
}

我不太确定你 绘制每个方框后所做的旋转和平移。它们导致您的转换不是真正的 "stack"。我可以再盯着他们看一个小时,想出 为什么 他们会导致这种行为,但我不擅长 3D 东西。

但是这样想:

在每次调用 tree() 结束时,您希望原点位于刚刚绘制的框的 top(您绘制的框的底部即将绘制),并且您希望旋转到 "stack"。

如果你这样做,那么你只需要做几件事 - 首先你会做你的旋转(因为原点已经在底部),然后你会平移到中心绘制你的盒子,然后你会翻译到你的盒子的顶部,这是你想要下一个盒子的底部的地方。向您展示代码可能更容易:

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

该代码似乎可以满足您的需求 - 它更像是一个 "snake",每个部分都从上一节结束的地方开始。

顺便说一句,这是一个有趣的小玩具,我很好奇你最终会用它做什么!