Qt实现Trackball交互方式代码

来源:互联网 发布:淘宝开店培训班 编辑:程序博客网 时间:2024/05/21 21:37

毕设要用到Qt+OpenGL,生成三维模型后需要能够进行拖动旋转等用户交互,因此我实现了Trackball类型的交互代码。Trackball的原理见https://www.khronos.org/opengl/wiki/Object_Mouse_Trackball,在此只做简单介绍。
实现Trackball需要对交互时鼠标的起点和终点进行记录,根据鼠标移动的轨迹改变三维模型旋转矩阵的值,进而实现三维模型的旋转。
Trackball假设鼠标在一个三维球面上进行拖动,但是屏幕只是二维的,因此需要计算第三个维度z的坐标,计算方法很简单:z=r2(x2+y2)。考虑到鼠标有时会落在球面之外,那么z怎么计算呢,因此需要在这些区域构造一个曲面来代替球面,这个曲面是z=r2/2x2+y2。这个曲面与球面相切,为了保证交互的连贯性,并不是在球面之外才使用这个曲面求得z,而是在切线之外就使用这个曲面,切线投影在xy平面上是x2+y2=r2/2,z坐标计算最终的公式为:

z=r2(x2+y2)r2/2x2+y2x2+y2r2/2otherwise

因此计算z坐标的函数如下:

float GLWidget::z(float x, float y){    const float r = 1.0;    if ((x * x + y * y) <= (r * r / 2)) {        return qSqrt(r * r - (x * x + y * y));    } else {        return (r * r / 2 / qSqrt(x * x + y * y));    }}

为了计算方便,我们将屏幕的xy坐标都被映射到了[-1, 1]的范围之内,映射的方式在下面代码中不做解释,这样相当于屏幕上有一个半径为1的球。我们要使用鼠标移动前后的起点向量V1和终点向量V2来计算旋转轴和旋转角度,V1和V2都是单位化向量,经过单位化以后,V1和V2终点就都落在了单位球面上。那么如何计算旋转轴和旋转角度呢,公式如下:

N=V1×V2
θ=arccosV1V2
需要注意的是,这里的θ是弧度制,使用QMatrix4x4::rotate()函数进行计算时,需要将其转换成弧度制,否则拖动起来会非常慢。处理鼠标移动事件的函数如下:

void GLWidget::mouseMoveEvent(QMouseEvent *event){    float x1 = (float)m_lastPos.x() / (this->width() / 2) - 1.0;    float y1 = 1.0 - (float)m_lastPos.y() / (this->height() / 2);    float z1 = z(x1, y1);    float x2 = (float)(event->x()) / (this->width() / 2) - 1.0;    float y2 = 1.0 - (float)(event->y()) / (this->height() / 2);    float z2 = z(x2, y2);    float l1 = qSqrt(x1 * x1 + y1 * y1 + z1 * z1);    float l2 = qSqrt(x2 * x2 + y2 * y2 + z2 * z2);    if (event->buttons() & Qt::LeftButton) {        QVector3D v1(x1 / l1, y1 / l1, z1 / l1);        QVector3D v2(x2 / l2, y2 / l2, z2 / l2);        QVector3D n = QVector3D::crossProduct(v1, v2);        float angle = qAcos(QVector3D::dotProduct(v1, v2));        rotateBy(angle * 180 / PI, n);    }    m_lastPos = event->pos();}

m_lastPos类型为QPoint,记录上次事件的终点,也就是本次事件的起点。
rotateBy函数用来操作旋转矩阵,代码如下:

void GLWidget::rotateBy(float angle, QVector3D &vector){    QMatrix4x4 temp = m_rotate;    m_rotate.setToIdentity();    m_rotate.rotate(angle, vector);    m_rotate *= temp;    update();}

m_rotate类型为QMatrix4x4,用来记录模型的旋转矩阵。这里需要注意的是,由于矩阵乘法不遵守交换律,因此需要将最后产生的旋转量放在最左侧,最后与模型点阵相乘,因此有了temp变量。
由于是毕业设计,因此其余代码就不放上来了。

0 0