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

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.xdnf.cn/news/15886.html

相关文章:

  • JSX(JavaScript XML)‌简介
  • wordle game(猜词游戏)小demo【react + ts】
  • 删除 XML 格式中双引号内的空格
  • 前后端分离项目进阶1---后端
  • Apache IoTDB(2):时序数据库 IoTDB 集群安装部署的技术优势与适用场景分析
  • Electron 主进程与渲染进程之间交互方式
  • 跑腿小程序|基于微信小程序的跑腿平台小程序设计与实现(源码+数据库+文档)
  • kotlin和Jetpack Compose对于Android系统来说是什么关系?
  • 【HTTP缓存机制深度解析:从ETag到实践策略】
  • c语言 进阶 动态内存管理
  • 客流分析核心算法 trajectory_event_analyzer数据结构
  • 深入解析Hadoop YARN:三层调度模型与资源管理机制
  • 单表查询-counter的使用
  • Centos卷挂载失败系统无法启动
  • c++ duiLib 使用xml文件编写界面布局
  • Protein FID:AI蛋白质结构生成模型评估新指标
  • axios二次封装-单个、特定的实例的拦截器、所有实例的拦截器。
  • Apache基础配置
  • C语言:深入理解指针(2)
  • 《汇编语言:基于X86处理器》第8章 复习题和练习,编程练习
  • Spring Cloud Gateway高危隐患
  • MySQL—表设计和聚合函数以及正则表达式
  • 2024年全国青少年信息素养大赛Scratch算法创意实践挑战赛 小高组 初赛 真题
  • Python适配器模式详解:让不兼容的接口协同工作
  • 【LeetCode数据结构】单链表的应用——环形链表问题详解
  • 详解Mysql索引合并
  • LeetCode 3202.找出有效子序列的最大长度 II:取模性质(动态规划)
  • lvs调度算法(10种)
  • TCL --- 列表_part1
  • 基于FPGA实现ARINC818