如何根据物体的方向旋转一个物体

5

关于WebGL的类似问题: 绕世界轴旋转物体

我需要以用户拖动的方式旋转对象,但问题在于glRotatef只是旋转对象而没有考虑其方向。对于WebGL,解决方案是使用四元数,但我想OpenGL中没有四元数。

现在我实现旋转的方法如下:

// rotation 2D GLfloat C-vector, posX, posY GLfloat's
void mouse(int button, int state, int x, int y) {
    if(button== GLUT_LEFT_BUTTON) {
        if(state== GLUT_DOWN) 
        {
            posX=x;
            posY= y;
            onEvent= true;
        }
        else if(state== GLUT_UP) 
        {
            GLfloat dx= x-posX;
            GLfloat dy= y-posY;
            rotation[0]= -dy/5;
            rotation[1]= dx/5;
            onEvent= false;
            glutPostRedisplay();
        }
    }
}

然后,在显示函数中处理旋转:

glPushMatrix();
glRotatef(rotation[0],1,0,0);
glRotatef(rotation[1],0,1,0);
// Draw the object
glPopMatrix();

这种方法有点有效,但是像我说的,如果用户可以通过拖动对象来旋转它,那就更好了。例如,如果物体围绕X轴旋转90度,则当用户水平拖动鼠标使其围绕Y轴旋转时,它会以相反的方向旋转。我需要一个想法,怎样才能实现这个功能?

编辑

我尝试使用glMultMatrixf,但是物体不能正确地旋转:它被缩放而不是旋转,这是我在鼠标函数中编辑的代码:

// Global variables:
// GLfloat xRotationMatrix[4][4];
// GLfloat yRotationMatrix[4][4];
else if(state== GLUT_UP && onEvent)
{
    GLfloat dx= (x-posX)/(180.0*5)*2.0*M_PI;
    GLfloat dy= (y-posY)/(180.0*5)*2.0*M_PI;
    // Computing rotations
    double cosX= cos(dx);
    double sinX= sin(dy);
    double cosY= cos(dy);
    double sinY= sin(dy);
    // x axis rotation
    xRotationMatrix[1][1]+= cosY;
    xRotationMatrix[1][2]+=-sinY;
    xRotationMatrix[2][2]+= sinY;
    xRotationMatrix[2][2]+= cosY;
    // y axis rotation
    yRotationMatrix[0][0]+= cosX;
    yRotationMatrix[0][2]+= sinX;
    yRotationMatrix[2][0]+= -sinX;
    yRotationMatrix[2][2]+= cosX;

    onEvent= false;
    glutPostRedisplay();
}

在显示函数中:
glPushMatrix();
glMultMatrixf((const GLfloat*)xRotationMatrix);
glMultMatrixf((const GLfloat*)yRotationMatrix);
glutSolidTeapot(10);
glPopMatrix();

这是未旋转的茶壶:

enter image description here

如果我把鼠标水平拖动来围绕y轴旋转茶壶,而不是旋转,我得到的是这个结果:

enter image description here


你试过 rotation[1]= -dx/5; 吗? - Ashalynd
2
你不能仅存储两个浮点数来完成这个操作。你需要使用四元数或旋转矩阵来处理。你可以定义一个rotation[4][4],将其初始化为单位矩阵,并在你的mouse函数中预乘以正确的旋转矩阵。然后在你的draw方法中,你调用glMultMatrixf(rotation)而不是glRotate相关的内容。 - sbabbi
@sbabbi 我尝试过这种方法,但是物体没有旋转,反而被缩放和变形了。你能看一下我的编辑吗? - Ramy Al Zuhouri
1个回答

1
首先,一些代数知识。假设v是一个向量,M是当前的模型视图矩阵,R是与glRotate命令相关联的矩阵。那么,如果使用glRotate,你得到的结果是: M * R * v 这意味着你是绕物体轴旋转。你想绕世界轴旋转,也就是: R * M * v 看到区别了吗?不幸的是,GL没有MatrixPreMult函数。
在现代OpenGL中,我们不再使用矩阵堆栈,事实上,在使用着色器时,我们手动将变换矩阵传递给GL程序。大多数人会编写/使用外部向量代数库(例如Eigen)。
一种可能的(未经测试的)解决方法只使用旧的弃用的GL工具,可能是这样的:
void rotate(float dx, float dy)
{
     //assuming that GL_MATRIX_MODE is GL_MODELVIEW
     float oldMatrix[4][4];
     glGetFloatv(GL_MODELVIEW_MATRIX,oldMatrix);
     glLoadIdentity();
     glRotatef(-dy,1,0,0);
     glRotatef(dx,0,1,0);
     glMultMatrixf(oldMatrix);
}

将此代码放入您的mouse函数中,而不是绘制例程中。 您可以通过在GL矩阵堆栈中保留视图矩阵,然后每次绘制对象时推送/弹出来使用此技巧。我不建议在大型项目中使用这样的东西。
请注意,如果您反转上述代码中的两个glRotate调用的顺序,则可能会得到略微不同的结果,特别是如果dx和dy不是很小的话。以下代码可能会稍微好一些:
float angle = sqrt(dx*dx+dy*dy)*scalingFactor;
glRotate(angle,-dy,dx,0);

如果我在顶点着色器中进行旋转会怎样?我该如何进行旋转? - Ramy Al Zuhouri
@RamyAlZuhouri 这个问题的答案相当复杂,取决于GLSL版本(以及你是使用默认的gl_ModelviewMatrix uniform还是自定义的)。最终,您需要将完整的模型视图矩阵传递给着色器,因此必须在C++端手动计算它。我建议您寻找关于GL着色器和矩阵变换的教程。 - sbabbi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接