当前位置: 首页 > news >正文

OpenGL鼠标控制沿着指定轴旋转

文章目录

  • 鼠标控制沿着指定轴旋转
    • 方案一:根据两次鼠标坐标来得出旋转方向、角度
      • 步骤
      • 问题
    • 方案二:结合arkball算法实现用户友好旋转(推荐)
      • 步骤
      • 缺点

鼠标控制沿着指定轴旋转

有这么一个开发场景,选定指定轴,操作鼠标控制模型绕着着指定轴旋转。

代码仅供参考,可根据实际应用场景进行调整。

方案一:根据两次鼠标坐标来得出旋转方向、角度

void CCabinet3DScene::rotateByAxis(int axis, QMouseEvent *event)
{QVector3D offset = getMouseMoveOffset(event);m_lastPos = event->pos();SPoint3D sRotate = m_pData->GetRotate();QVector3D delta = QVector3D(sRotate.x, sRotate.y, sRotate.z);float rotate = 0.0f;bool bMove = true;// 根据axis参数约束移动旋转switch(axis){case enX:{if (offset.x() >= 0){rotate = (delta.x() + 1 > 360) ? (delta.x() + 1 - 360) : (delta.x() + 1);delta.setX(rotate);}else{rotate = (delta.x() - 1 < -360) ? (delta.x() - 1 + 360) : (delta.x() - 1);delta.setX(rotate);}}break;case enY:{if (offset.y() >= 0){rotate = (delta.y() + 1 > 360) ? (delta.y() + 1 - 360) : (delta.y() + 1);delta.setY(rotate);}else{rotate = (delta.y() - 1 < -360) ? (delta.y() - 1 + 360) : (delta.y() - 1);delta.setY(rotate);}}break;case enZ:{if (offset.z() >= 0){rotate = (delta.z() + 1 > 360) ? (delta.z() + 1 - 360) : (delta.z() + 1);delta.setZ(rotate);}else{rotate = (delta.z() - 1 < -360) ? (delta.z() - 1 + 360) : (delta.z() - 1);delta.setZ(rotate);}}break;default :bMove = false;break;}if (!bMove)return;if (m_screens.contains(m_curID))m_screens[m_curID]->setRotate(delta, true);update();
}QVector3D CCabinet3DScene::getMouseMoveOffset(QMouseEvent *event)
{makeCurrent();GLint viewport[4];GLdouble modelview[16], projection[16];glGetIntegerv(GL_VIEWPORT, viewport);glGetDoublev(GL_MODELVIEW_MATRIX, modelview);glGetDoublev(GL_PROJECTION_MATRIX, projection);float lastZ = 0.0f;int lastWinX = m_lastPos.x();int lastWinY = viewport[3] - m_lastPos.y();glReadPixels(lastWinX, lastWinY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &lastZ);double lastWorldX, lastWorldY, lastWorldZ;gluUnProject(lastWinX, lastWinY, lastZ, modelview, projection, viewport, &lastWorldX, &lastWorldY, &lastWorldZ);float currZ = 0.0f;int currWinX = event->pos().x();int currWinY = viewport[3] - event->pos().y();glReadPixels(currWinX, currWinY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &currZ);double currWorldX, currWorldY, currWorldZ;gluUnProject(currWinX, currWinY, currZ, modelview, projection, viewport, &currWorldX, &currWorldY, &currWorldZ);QVector3D delta = QVector3D(currWorldX - lastWorldX, currWorldY - lastWorldY, currWorldZ - lastWorldZ);doneCurrent();return delta * devicePixelRatioF();
}

步骤

  • 根据鼠标curPos和lastPos世界坐标的差值计算delta;
  • 根据选定的旋转轴,取出delta对应的分量;
  • 根据其值设置旋转方向,固定旋转为1。

问题

  • 用户体验不友好:只根据一个目标分量判断方向,当用户操作鼠标在其他分量上变化大时,呈现的效果不好…
  • 鼠标做画圈操作,会出现模型来回摆动的现象。
  • 逆时针、顺时针操作呈现的效果不是相反的。

方案二:结合arkball算法实现用户友好旋转(推荐)

void CCabinet3DScene::rotateByAxis(int axis, QMouseEvent *event)
{QVector3D va = mapToArcball(m_lastPos);QVector3D vb = mapToArcball(event->pos());// 计算方向和角度float angle = acos(qBound(-1.0f, QVector3D::dotProduct(va, vb), 1.0f));float cross = va.x() * vb.y() - va.y() * vb.x();float sign = cross >= 0 ? -1.0f : 1.0f;float constrainedAngle = sign * angle * 180.0f / M_PI; ; // 转为角度m_lastPos = event->pos();SPoint3D sRotate = m_pData->GetRotate();QVector3D delta = QVector3D(sRotate.x, sRotate.y, sRotate.z);float rotate = 0.0f;bool bMove = true;// 根据axis参数约束移动旋转switch(axis){case enX:{rotate = delta.x() + constrainedAngle;if(rotate >= 360 || rotate <= -360)rotate = 0;delta.setX(rotate);}break;case enY:{rotate = delta.y() + constrainedAngle;if(rotate >= 360 || rotate <= -360)rotate = 0;delta.setY(rotate);}break;case enZ:{rotate = delta.z() + constrainedAngle;if(rotate >= 360 || rotate <= -360)rotate = 0;delta.setZ(rotate);}break;default :bMove = false;break;}if (!bMove)return;if (m_screens.contains(m_curID))m_screens[m_curID]->setRotate(delta, true);update();
}QVector3D CCabinet3DScene::mapToArcball(QPointF pos)
{float x = (2.0f * pos.x() - width()) / width();float y = (height() - 2.0f * pos.y()) / height(); // y反转float z2 = 1.0f - x * x - y * y;float z = z2 > 0.0f ? sqrt(z2) : 0.0f;return QVector3D(x, y, z).normalized();
}

步骤

  • 计算鼠标位置映射到球体的向量va、vb;
  • 根据两个向量计算出旋转角度,也可以为固定值;
  • 根据向量前两个维度计算旋转方向。

缺点

  • 依赖中心点的选取,这里直接选取屏幕中心,如果鼠标画圈没有包含中心点的话,也会出现用户不友好的现象;
http://www.lryc.cn/news/595171.html

相关文章:

  • STM32 开发的鼠标:技术详解与实现指南
  • 数据结构堆的实现(C语言)
  • Selenium 处理表单、弹窗与文件上传:从基础到实战
  • Ubuntu 22.04 安装 Jdk 8和 Tomcat (安装包形式)
  • Ubuntu 22 集群部署 Apache Doris 3.0.3 笔记
  • 前端图像视频实时检测
  • GitHub+Git新手使用说明
  • Flutter中 Provider 的基础用法超详细讲解(一)
  • 数据库和数据仓库的区别
  • [Python]函数调用链中局部变量的内存影响:通过memory_profiler分析
  • 全新开发范式:uni-app X助力全平台原生应用
  • Type-C接口台式显示器:LDR6021引领新潮流
  • JAVA+AI教程-第三天
  • 将 RustFS 用作 GitLab 对象存储后端
  • 从 Hi3861 平台到 WS63 星闪平台的程序移植全解析
  • 部署zabbox企业级分布式监控
  • 后训练(Post-training)语言模型
  • 2025最新版IntelliJ IDEA Ultimate for Mac专业版安装使用指南
  • How does Misinformation Affect Large Language ModelBehaviors and Preferences?
  • Flink框架:keyBy实现按键逻辑分区
  • makefile-- 其他函数
  • 低代码平台买saas好还是私有化好
  • 【HTTP缓存机制深度解析:从ETag到实践策略】
  • Zabbix 企业级分布式监控部署
  • C++学习<2>--引用、函数、内存分区
  • 【2025】Vscode Python venv虚拟环境显示“激活终端”成功但是在终端中“并没有激活成功”,pip安装还是会安装到全局环境中的解决方法;
  • 第十八节:第七部分:java高级:注解的应用场景:模拟junit框架
  • nextjs+react接口会请求两次?
  • 元宇宙与DAO自治:去中心化治理的数字文明实践
  • 【设计模式C#】简单工厂模式(用于简化获取对象实例化的复杂性)