Qt实现Trackball交互方式代码

27 篇文章 4 订阅

毕设要用到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变量。
由于是毕业设计,因此其余代码就不放上来了。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值