目的:实现鼠标控制正方形的平移、缩放、以及围绕自身某个点旋转。

要求:

  1. 坐标系固定在左下角
  2. 坐标系和正方形一起旋转,但不平移与缩放
  3. 鼠标左键平移正方形,右键旋转,滚轮缩放(放大与缩小)

步骤

  1. 编写绘制正方形与坐标系函数
  2. 在OpenGL窗口界面绘制
  3. 实现鼠标左键平移移动,右键旋转,滚轮缩放(放大与缩小)
  4. 设置正方形的旋转点,以及坐标系位置
  5. 将对应的鼠标事件应用到正方形与坐标系

实现:

  • 步骤1:编写绘制正方形与坐标系函数(比较简单,就不贴代码了)
	drawObjects();
	drawaxis();
  • 步骤2:在OpenGL窗口界面绘制
  • 步骤5:将对应的鼠标事件应用到正方形与坐标系

void paintGL();
绘制OpenGL窗口界面。在这里绘制图形,并对图形设置了不同的变换。

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    int renderMode;
    glGetIntegerv(GL_RENDER_MODE, &renderMode);

    if (renderMode != GL_SELECT) {
        // Matrix setting
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        const float aspect = static_cast<float>(width()) / height();
        gluPerspective(45.0, aspect, 1.0, 1000.0);
    }
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

    glPushMatrix();
    glMultMatrixf(m_transform.constData());//对drawObjects()的图形进行平移,旋转
    glScalef(zscale,zscale,zscale);//缩放
    drawObjects();
    glPopMatrix();

    glPushMatrix();
    glTranslatef(-4, -3, 0);
    glMultMatrixf(axis_transform.constData());//对drawaxis()的图形进行旋转
    drawaxis();
    glPopMatrix();
  • 步骤3:设置鼠标左键平移移动,右键旋转

mouseMoveEvent(QMouseEvent* ev);

    int dx = ev->x() - m_lastPos.x();
    int dy = ev->y() - m_lastPos.y();

    if(ev->buttons() & Qt::LeftButton)
    {
        translate(dx, dy);
    }
    else if(ev->buttons() & Qt::RightButton)
    {
        objects_rotate(dx, dy);
        axis_rotate(dx, dy);
    }
    m_lastPos = ev->pos();
    update();
  • 步骤3:正方形的平移移动
    translate(int dx, int dy);
	m_translation.setX(m_translation.x() + dx * 0.01);
    m_translation.setY(m_translation.y() - dy * 0.01);
    m_transform.setToIdentity();                       //将变换矩阵重置为单位矩阵
    m_transform.translate(m_translation);              //平移到正确的位置
    m_transform.rotate(QQuaternion::fromAxisAndAngle(1, 0, 0, m_rotation.x()/* + 1*/));//将旋转状态应用到平移中
    m_transform.rotate(QQuaternion::fromAxisAndAngle(0, 1, 0, m_rotation.y()/* + 1*/));
  • 步骤3:坐标系的旋转
    axis_rotate(int dx, int dy);
// 四元数: new_rotation = q * m_rotation * q逆          // q = xRot * yRot * zRot, 即 rotate()的参数
    axis_rotation.setX(axis_rotation.x() - dy);
    axis_rotation.setY(axis_rotation.y() - dx);
    QQuaternion xRot = QQuaternion::fromAxisAndAngle(1, 0, 0, axis_rotation.x());//绕(0,0,0)点与(1,0,0)点连线,即x轴,旋转角度m_rotation.x()
    QQuaternion yRot = QQuaternion::fromAxisAndAngle(0, 1, 0, axis_rotation.y());

    axis_transform.setToIdentity();
    axis_transform.rotate(xRot * yRot);
  • 步骤3:正方形的旋转
    objects_rotate(int dx, int dy);
	m_rotation.setX(m_rotation.x() - dy);
    m_rotation.setY(m_rotation.y() - dx);
    QQuaternion xRot = QQuaternion::fromAxisAndAngle(1, 0, 0, m_rotation.x());//绕(0,0,0)点与(1,0,0)点连线,即x轴,旋转角度m_rotation.x()
    QQuaternion yRot = QQuaternion::fromAxisAndAngle(0, 1, 0, m_rotation.y());
    m_transform.setToIdentity();
    m_transform.translate(m_translation); //将平移状态应用到旋转
    m_transform.rotate(xRot * yRot);//四元数相乘,表示两次旋转
  • 步骤3:正方形的滚轮缩放(放大与缩小)
    wheelEvent(QWheelEvent *ev);
	int delta = ev->delta();  // positive when wheel up, delta +/- 120 when wheel +/- 1
    if(zscale-delta*2.0*1e-3>0)
        zscale += delta*2.0*1e-3;
    update();
  • 步骤4:设置正方形的旋转点,以及坐标系位置
    qt opengl中想要绕着某个点旋转,主要思维是将图形平移到该点位置,进行旋转操作后,再对图形做逆平移
    //*******设置坐标轴的旋转中心********
    float centre_x, centre_y;
    centre_x = -4;
    centre_y = -3;
    axis_translation.setX(centre_x);
    axis_translation.setY(centre_y);
    //******设置坐标轴的旋转中心*********
    axis_transform.translate(axis_translation);//平移到centre点的位置
    
    axis_transform.rotate(xRot * yRot);//旋转
    
    axis_translation.setX(-centre_x);
    axis_translation.setY(-centre_y);
    axis_transform.translate(axis_translation);//逆平移

在最近研究这个问题的过程中,发现如果同时对图形做了平移与旋转,用上述方法难点在于对于旋转点的世界坐标的获取。目前只学习了将屏幕坐标转换为世界坐标,但是对于追踪某一点的世界坐标还有待学习(有懂的大佬希望可以教教我qwq)。然而眼前的问题还是要尽快解决的,后来发现了下面的方法。

	glTranslatef(1, 1, 0);

没错,就是平移!其实也就是将正方形想要围绕旋转的点平移到原点(0,0)的位置。
我的正方形边长为2,所以这个代码意思是将正方形向右上方平移(二维平面),结果就是正方形左下角到了原点的位置。也就是我的正方形设置是围绕左下角旋转,在一系列平移,缩放后,可以一直围绕左下角旋转。

效果展示

展示1
展示2

题外话

  1. 对于上图坐标系显示文字的绘制办法下次记录
  2. 鼠标点击,选择拾取glSelectBuffer整理之后再记录
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐