【问题标题】:How to rotate object around local axis in OpenGL?如何在OpenGL中围绕局部轴旋转对象?
【发布时间】:2009-11-04 01:27:45
【问题描述】:

我正在做一个正在进行的项目,我想对齐链条的链接,使其遵循贝塞尔曲线的轮廓。我目前正在执行以下步骤。

  1. 绘制曲线。
  2. 使用显示列表创建链中的一个链接。
  3. 使用 FOR 循环重复调用一个函数,该函数计算曲线上两点之间的角度,返回角度和链接应围绕其旋转的轴。
  4. 旋转角度“a”并平移到新位置,将链接放置在新位置。

编辑:我还应该说两个半环的中心必须位于贝塞尔曲线上。 另外我知道我用来绘制环面的方法很繁琐,我稍后会使用 TRIANGLE_FAN 或 QUAD_STRIP 以更有效的方式绘制环面。

虽然乍一看这个逻辑看起来可以正确渲染链条,但最终结果并不是我想象的那样。这是链条的样子。

我读到您必须在旋转之前将对象转换为原点?我可以直接调用 glTranslate(0,0,0) 然后按照上面的步骤 4 进行操作吗?

我已经包含了我迄今为止所做的相关代码,如果有任何建议可以让我的代码正常工作,我将不胜感激。

/* this function calculates the angle between two vectors oldPoint and new point contain the x,y,z coordinates of the two points,axisOfRot is used to return the x,y,z coordinates of the rotation axis*/
double getAxisAngle(pointType oldPoint[],
pointType newPoint[],pointType axisOfRot[]){

float tmpPoint[3];   

float normA = 0.0,normB = 0.0,AB = 0.0,angle=0.0;
int i;

axisOfRot->x= oldPoint->y * newPoint->z - oldPoint->z * newPoint->y;
axisOfRot->y= oldPoint->z * newPoint->x - oldPoint->x * newPoint->z;
axisOfRot->z= oldPoint->x * newPoint->y - oldPoint->y * newPoint->x; 


normA=sqrt(oldPoint->x * oldPoint->x + oldPoint->y * oldPoint->y + oldPoint->z * 
oldPoint->z);
normB=sqrt(newPoint->x * newPoint->x + newPoint->y * newPoint->y + newPoint->z *    
newPoint->z);

tmpPoint[0] =  oldPoint->x * newPoint->x;
tmpPoint[1] =  oldPoint->y * newPoint->y;
tmpPoint[2] =  oldPoint->z * newPoint->z;   

for(i=0;i<=2;i++)
 AB+=tmpPoint[i];  

AB /= (normA * normB);  


    return angle = (180/PI)*acos(AB);  
 }

 /* this function calculates and returns the next point on the curve give the 4 initial points for the curve, t is the tension of the curve */
    void bezierInterpolation(float t,pointType cPoints[],
    pointType newPoint[]){

newPoint->x = pow(1 - t, 3) * cPoints[0].x +3 * pow(1 - t , 2) * t * cPoints[1].x + 3 
* pow(1 - t, 1) * pow(t, 2) * cPoints[2].x + pow(t, 3) * cPoints[3].x;

newPoint->y = pow(1 - t, 3) * cPoints[0].y +3 * pow(1 - t , 2) * t * cPoints[1].y + 3 
* pow(1 - t, 1) * pow(t, 2) * cPoints[2].y + pow(t, 3) * cPoints[3].y;

newPoint->z = pow(1 - t, 3) * cPoints[0].z +3 * pow(1 - t , 2) * t * cPoints[1].z + 3 
* pow(1 - t, 1) * pow(t, 2) * cPoints[2].z + pow(t, 3) * cPoints[3].z;

}

/* the two lists below are used to create a single link in a chain, I realize that creating a half torus using cylinders is a bad idea, I will use GL_STRIP or TRIANGLE_FAN once I get the alignment right 
*/
torusList=glGenLists(1);   

glNewList(torusList,GL_COMPILE);
for (i=0; i<=180; i++)
{
  degInRad = i*DEG2RAD;
  glPushMatrix();
  glTranslatef(cos(degInRad)*radius,sin(degInRad)*radius,0);
  glRotated(90,1,0,0);
  gluCylinder(quadric,Diameter/2,Diameter/2,Height/5,10,10);
  glPopMatrix();      
}

 glEndList();

    /*! create a list for the link , 2 half torus and 2 columns */

 linkList = glGenLists(1);

 glNewList(linkList, GL_COMPILE); 
 glPushMatrix();
 glCallList(torusList); 
 glRotatef(90,1,0,0);
 glTranslatef(radius,0,0);
 gluCylinder(quadric, Diameter/2, Diameter/2, Height,10,10); 
 glTranslatef(-(radius*2),0,0);
 gluCylinder(quadric, Diameter/2, Diameter/2, Height,10,10); 
 glTranslatef(radius,0, Height);
 glRotatef(90,1,0,0);
 glCallList(torusList); 
 glPopMatrix();
 glEndList();

最后是创建链中三个链接的代码

t=0.031; 
bezierInterpolation(t,cPoints,newPoint);  
a=getAxisAngle(oldPoint,newPoint,axisOfRot);
 glTranslatef(newPoint->x,newPoint->y,newPoint->z); 
   glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);     
    glCallList(DLid);
   glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
 glTranslatef(-newPoint->x,-newPoint->y,-newPoint->z);  

oldPoint[0]=newPoint[0];
bezierInterpolation(t+=GAP,cPoints,newPoint);  
a=getAxisAngle(oldPoint,newPoint,axisOfRot);
 glTranslatef(newPoint->x,newPoint->y,newPoint->z); 
  glRotatef(90,0,1,0);
   glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);     
    glCallList(DLid);
   glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
  glRotatef(90,0,1,0);  
 glTranslatef(-newPoint->x,-newPoint->y,-newPoint->z);  

 oldPoint[0]=newPoint[0];
 bezierInterpolation(t+=GAP,cPoints,newPoint);    
  a=getAxisAngle(oldPoint,newPoint,axisOfRot); 
   glTranslatef(newPoint->x,newPoint->y,newPoint->z);       
    glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
     glCallList(DLid);
    glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
   glTranslatef(-newPoint->x,-newPoint->y,newPoint->z); 

【问题讨论】:

    标签: opengl


    【解决方案1】:

    需要注意的一点是 glTranslate 函数建立在以前的翻译之上。 IE。一个 glTranslatef(0.0,0.0,0.0);不会去原点,它只会将“笔”移动到任何地方。幸运的是,“笔”从原点开始。如果你翻译成 1.0,1.0,1.0 然后试试 glTranslatef(0.0,0.0,0.0);你仍然会在 1.0,1.0,1.0 绘图;

    此外,您似乎掌握了 openGL 后乘矩阵的事实。为此,您在平局后正确地“撤消”了矩阵操作。我只看到一个地方你可能会离开这里,那就是在这个声明中:

    glRotatef(90,0,1,0);
       glRotatef(a,axisOfRot->x,axisOfRot->y,axisOfRot->z);     
        glCallList(DLid);
       glRotatef(-a,axisOfRot->x,axisOfRot->y,axisOfRot->z); 
      glRotatef(90,0,1,0);  
    

    在这里您正确地撤消了第二次旋转,但第一次您似乎围绕 y 轴旋转得更多。最后一个 glRotatef 需要读取 glRotatef(-90,0,1,0);如果您想撤消该轮换。

    【讨论】:

      【解决方案2】:

      我查看了您的代码并假设执行 bezierInterp 和轴角度的代码是正确的。根据代码,我有以下建议:

      1. 创建单个链接的方式看起来非常昂贵。当您使用 gluCylinder 180 次时。这将为一个小链接生成很多顶点。您可以创建单个环面并应用比例,使其看起来像一个链接!

      2. 每当您进行任何矩阵运算时,最好先设置模式。这在进行推送和弹出之前很重要。在您的显示列表中,您可以在没有设置任何模式的情况下推送和弹出,也没有在调用者中设置。这不是好的做法,会导致很多错误/问题。您可以从调用列表中删除推送和弹出,并在其中仅保留几何图形。

      3. 您听说过建议在旋转之前将原点平移作为平移 * 旋转! = 旋转 * 平移。因此,您编写渲染循环的方式是:

        // Set matrix mode
        glMatrixMode(GL_MODELVIEW);
        
        for(number of links) {
        
        glLoadIdentity();     // makes model view matrix identity - default location`
        glTranslatef(x,y,z);  // Translate to a point on beizer curve
        glRotatef(..);        // Rotate link
        glCallList(link);     // can be simple torus, only geometry centered at origin
        }
        

      以上代码呈现在指定位置重复的链接。阅读 OpenGL Red book 的 chapter 3 - 示例 3.6(行星系统)示例,了解如何正确地将每个链接放置在不同的位置。

      【讨论】:

      • 我的程序已经包含在 main() 函数中设置的 MODELVIEW 语句,正如我在该显示列表的评论中指出的那样“我意识到使用圆柱体创建半圆环是一个坏主意,一旦对齐正确,我将使用 GL_STRIP 或 TRIANGLE_FAN”。仅使用环面不会使链条变得逼真,这就是为什么选择使用两个半环面和两个圆柱体来创建链条的原因。为了在这篇文章中简单起见,我已经简化了程序,去掉了 for 循环和大部分 main() 函数。感谢您的帮助。
      • 那么您找到正确的方法来进行翻译和旋转工作了吗?
      • 不,我尝试使用 glTranslatef(0,0,0); 将初始链接移动到原点;然后调用 glRotate();然后是 glTranslate() 到原始位置,然后渲染链接,但我仍然有两个半环的中心不在曲线上的问题。
      • 看起来您从 OpenGL 的角度正确设置了代码,但您的值可能不正确。当您创建初始链接时,我会假设它以原点本身为中心!那时你不应该做任何翻译。渲染通过 glCallList 创建的链接并确保它位于您想要的中心。完成后,您始终可以旋转并平移到循环中的所需位置,如前所述。希望你能把它修好!
      • Ketan,关于在原点创建的显示列表是正确的,我手动输入每个链接的旋转角度(从角度 = 3 开始并不断增加 5)这将环圈居中正确地在曲线上。我认为我的 getAxisAngle() 是错误的。目前正在尝试调试。
      猜你喜欢
      • 1970-01-01
      • 2021-01-05
      • 2018-05-20
      • 2014-11-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-22
      • 1970-01-01
      相关资源
      最近更新 更多