当前位置:   article > 正文

Qt Quick 3D学习:鼠标拾取物体_mapto3dscene

mapto3dscene

(注意,开源版的 Qt Quick 3D 是狗都不用的 GPL 协议)

Qt Creator 中有一个 picking 的示例,用于演示 View3D 中物体的拾取:

在示例基础上,我又加了一个简单的拖动效果,如图所示:

 

在使用 OpenGL 实现拾取的时候,我们可以用射线法。Qt Quick 3D 中封装了拾取操作,通过 View3D 的 pick 函数,可以取到 View3D 视口某个点下离屏幕最近的那个物体 Model。

PickResult pick(float x, float y)

该函数返回一个 PickResult 对象,通过其 objectHit 属性可以判断是否拾取到了物体。

  1. View3D {
  2. MouseArea {
  3. id: mouse_area
  4. anchors.fill: parent
  5. onPressed: {
  6. //获取点在View上的屏幕坐标
  7. pick_screen.text = "(" + mouse.x + ", " + mouse.y + ")"
  8. //pick取与该点射线路径相交的离最近的Model的信息,返回PickResult对象
  9. var result = control.pick(mouse.x, mouse.y)
  10. //判断objectHit是否有效,就可以只知道是否拾取到了物体
  11. if (result.objectHit) {
  12. } else {
  13. }
  14. }
  15. }
  16. }

Qt5.15 中,返回的 PickResult 类型只有简单的几个属性:

  1. //拾取原点与物体之间的距离,用视口坐标拾取则拾取原点就是观察点Camera的位置
  2. distance : float
  3. //拾取选中的Model对象
  4. objectHit : Model
  5. //pick点在场景中的位置,(可能相当于射线与物体表面的焦点坐标)
  6. //This property holds the scene position of the hit.
  7. scenePosition : vector3d
  8. //pick点的UV位置
  9. //This property holds the UV position of the hit.
  10. uvPosition : vector2d

在 Qt6.3 中,View3D 增加了 pickAll 函数拾取该点下所有的物体,同时 PickResult 类型也增加了一些属性:

  1. //局部空间中被选中的面的法线
  2. //This property holds the normal of the face that was hit in local coordinate space.
  3. normal : vector3d
  4. //This property holds the scene position of the hit in local coordinate space.
  5. position : vector3d
  6. //This property holds the normal of the face that was hit in scene coordinate space.
  7. sceneNormal : vector3d

拾取到物体之后,就可以对这个 Model 节点进行操作了。本文 Demo 中我实现了一个简单的拖动操作,主要流程是:

1.pick 选中时保存 Model 位置和 pick 位置的视口屏幕坐标差值(可以通过 View3D 的 mapFrom3DScene 函数将场景坐标转换为视口坐标,使用该函数需要先给 View3D 设置 camera )

2.在鼠标移动的过程中,通过差值还原物体 Model 的相对位置,使用 View3D 的 mapTo3DScene 函数转换得到 Model 新的场景坐标。

(但是这个逻辑没有考虑透视投影时,移动场景坐标 xy,视口坐标 xy 并不是等比变化的)

主要代码

Github(PickModel.qml): https://github.com/gongjianbo/HelloQtQuick3D

  1. import QtQuick 2.15
  2. import QtQuick.Controls 2.15
  3. import QtQuick3D 1.15
  4. import QtQuick3D.Helpers 1.15
  5. View3D {
  6. id: control
  7. //背景
  8. environment: SceneEnvironment {
  9. clearColor: "darkGreen"
  10. backgroundMode: SceneEnvironment.Color
  11. }
  12. //观察相机
  13. //View3D的mapTo/mapFrom坐标转换函数需要先设置camera属性
  14. camera: perspective_camera
  15. PerspectiveCamera {
  16. id: perspective_camera
  17. z: 300
  18. }
  19. //光照
  20. DirectionalLight {
  21. eulerRotation.y: 45
  22. }
  23. //立方体
  24. Model {
  25. id: cube_node
  26. objectName: "Cube"
  27. source: "#Cube"
  28. //使能pick
  29. pickable: true
  30. materials: DefaultMaterial {
  31. diffuseColor: mouse_area.pickNode == cube_node ? "cyan" : "yellow"
  32. }
  33. //立方体转动
  34. SequentialAnimation on eulerRotation {
  35. running: true
  36. loops: Animation.Infinite
  37. PropertyAnimation {
  38. duration: 10000
  39. from: Qt.vector3d(0, 0, 0)
  40. to: Qt.vector3d(360, 360, 360)
  41. }
  42. }
  43. }
  44. //锥体
  45. Model {
  46. id: cone_node
  47. objectName: "Cone"
  48. source: "#Cone"
  49. pickable: true
  50. x: 100
  51. z: 50
  52. materials: DefaultMaterial {
  53. diffuseColor: mouse_area.pickNode == cone_node ? "cyan" : "orange"
  54. }
  55. }
  56. //球体
  57. Model {
  58. id: sphere_node
  59. objectName: "Sphere"
  60. source: "#Sphere"
  61. pickable: true
  62. x: -100
  63. z: -50
  64. materials: DefaultMaterial {
  65. diffuseColor: mouse_area.pickNode == sphere_node ? "cyan" : "purple"
  66. }
  67. }
  68. //展示拾取对象的信息
  69. Row {
  70. x: 20
  71. y: 20
  72. spacing: 10
  73. Column {
  74. Label {
  75. color: "white"
  76. text: "Pick Node:"
  77. }
  78. Label {
  79. color: "white"
  80. text: "Screen Position:"
  81. }
  82. Label {
  83. color: "white"
  84. text: "Distance:"
  85. }
  86. Label {
  87. color: "white"
  88. text: "World Position:"
  89. }
  90. }
  91. Column {
  92. Label {
  93. id: pick_name
  94. color: "white"
  95. }
  96. Label {
  97. id: pick_screen
  98. color: "white"
  99. }
  100. Label {
  101. id: pick_distance
  102. color: "white"
  103. }
  104. Label {
  105. id: pick_word
  106. color: "white"
  107. }
  108. }
  109. }
  110. MouseArea {
  111. id: mouse_area
  112. anchors.fill: parent
  113. hoverEnabled: false
  114. property var pickNode: null
  115. //鼠标和物体xy的偏移
  116. property real xOffset: 0
  117. property real yOffset: 0
  118. property real zOffset: 0
  119. onPressed: {
  120. //获取点在View上的屏幕坐标
  121. pick_screen.text = "(" + mouse.x + ", " + mouse.y + ")"
  122. //pick取与该点射线路径相交的离最近的Model的信息,返回PickResult对象
  123. //因为该模块一直在迭代,新的版本可以从PickResult对象获取更多的信息
  124. //Qt6中还提供了pickAll获取与该射线相交的所有Model信息
  125. var result = control.pick(mouse.x, mouse.y)
  126. //目前只在点击时更新了pick物体的信息
  127. if (result.objectHit) {
  128. pickNode = result.objectHit
  129. pick_name.text = pickNode.objectName
  130. pick_distance.text = result.distance.toFixed(2)
  131. pick_word.text = "("
  132. + result.scenePosition.x.toFixed(2) + ", "
  133. + result.scenePosition.y.toFixed(2) + ", "
  134. + result.scenePosition.z.toFixed(2) + ")"
  135. //console.log('in',pick_screen.text)
  136. //console.log(result.scenePosition)
  137. var map_from = control.mapFrom3DScene(pickNode.scenePosition)
  138. //var map_to = control.mapTo3DScene(Qt.vector3d(mouse.x,mouse.y,map_from.z))
  139. //console.log(map_from)
  140. //console.log(map_to)
  141. xOffset = map_from.x - mouse.x
  142. yOffset = map_from.y - mouse.y
  143. zOffset = map_from.z
  144. } else {
  145. pickNode = null
  146. pick_name.text = "None"
  147. pick_distance.text = " "
  148. pick_word.text = " "
  149. }
  150. }
  151. onPositionChanged: {
  152. if(!mouse_area.containsMouse || !pickNode){
  153. return
  154. }
  155. var pos_temp = Qt.vector3d(mouse.x + xOffset, mouse.y + yOffset, zOffset);
  156. var map_to = control.mapTo3DScene(pos_temp)
  157. pickNode.x = map_to.x
  158. pickNode.y = map_to.y
  159. }
  160. }
  161. }

参考

Qt文档:https://doc.qt.io/qt-5/qml-qtquick3d-view3d.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/91686
推荐阅读
  

闽ICP备14008679号