赞
踩
(注意,开源版的 Qt Quick 3D 是狗都不用的 GPL 协议)
Qt Creator 中有一个 picking 的示例,用于演示 View3D 中物体的拾取:
在示例基础上,我又加了一个简单的拖动效果,如图所示:
在使用 OpenGL 实现拾取的时候,我们可以用射线法。Qt Quick 3D 中封装了拾取操作,通过 View3D 的 pick 函数,可以取到 View3D 视口某个点下离屏幕最近的那个物体 Model。
PickResult pick(float x, float y)
该函数返回一个 PickResult 对象,通过其 objectHit 属性可以判断是否拾取到了物体。
- View3D {
- MouseArea {
- id: mouse_area
- anchors.fill: parent
- onPressed: {
- //获取点在View上的屏幕坐标
- pick_screen.text = "(" + mouse.x + ", " + mouse.y + ")"
- //pick取与该点射线路径相交的离最近的Model的信息,返回PickResult对象
- var result = control.pick(mouse.x, mouse.y)
- //判断objectHit是否有效,就可以只知道是否拾取到了物体
- if (result.objectHit) {
-
- } else {
-
- }
- }
- }
- }
在 Qt5.15 中,返回的 PickResult 类型只有简单的几个属性:
- //拾取原点与物体之间的距离,用视口坐标拾取则拾取原点就是观察点Camera的位置
- distance : float
-
- //拾取选中的Model对象
- objectHit : Model
-
- //pick点在场景中的位置,(可能相当于射线与物体表面的焦点坐标)
- //This property holds the scene position of the hit.
- scenePosition : vector3d
-
- //pick点的UV位置
- //This property holds the UV position of the hit.
- uvPosition : vector2d
在 Qt6.3 中,View3D 增加了 pickAll 函数拾取该点下所有的物体,同时 PickResult 类型也增加了一些属性:
- //局部空间中被选中的面的法线
- //This property holds the normal of the face that was hit in local coordinate space.
- normal : vector3d
-
- //This property holds the scene position of the hit in local coordinate space.
- position : vector3d
-
- //This property holds the normal of the face that was hit in scene coordinate space.
- 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
- import QtQuick 2.15
- import QtQuick.Controls 2.15
- import QtQuick3D 1.15
- import QtQuick3D.Helpers 1.15
-
- View3D {
- id: control
-
- //背景
- environment: SceneEnvironment {
- clearColor: "darkGreen"
- backgroundMode: SceneEnvironment.Color
- }
-
- //观察相机
- //View3D的mapTo/mapFrom坐标转换函数需要先设置camera属性
- camera: perspective_camera
- PerspectiveCamera {
- id: perspective_camera
- z: 300
- }
-
- //光照
- DirectionalLight {
- eulerRotation.y: 45
- }
-
- //立方体
- Model {
- id: cube_node
- objectName: "Cube"
- source: "#Cube"
- //使能pick
- pickable: true
- materials: DefaultMaterial {
- diffuseColor: mouse_area.pickNode == cube_node ? "cyan" : "yellow"
- }
- //立方体转动
- SequentialAnimation on eulerRotation {
- running: true
- loops: Animation.Infinite
- PropertyAnimation {
- duration: 10000
- from: Qt.vector3d(0, 0, 0)
- to: Qt.vector3d(360, 360, 360)
- }
- }
- }
-
- //锥体
- Model {
- id: cone_node
- objectName: "Cone"
- source: "#Cone"
- pickable: true
- x: 100
- z: 50
- materials: DefaultMaterial {
- diffuseColor: mouse_area.pickNode == cone_node ? "cyan" : "orange"
- }
- }
-
- //球体
- Model {
- id: sphere_node
- objectName: "Sphere"
- source: "#Sphere"
- pickable: true
- x: -100
- z: -50
- materials: DefaultMaterial {
- diffuseColor: mouse_area.pickNode == sphere_node ? "cyan" : "purple"
- }
- }
-
- //展示拾取对象的信息
- Row {
- x: 20
- y: 20
- spacing: 10
- Column {
- Label {
- color: "white"
- text: "Pick Node:"
- }
- Label {
- color: "white"
- text: "Screen Position:"
- }
- Label {
- color: "white"
- text: "Distance:"
- }
- Label {
- color: "white"
- text: "World Position:"
- }
- }
- Column {
- Label {
- id: pick_name
- color: "white"
- }
- Label {
- id: pick_screen
- color: "white"
- }
- Label {
- id: pick_distance
- color: "white"
- }
- Label {
- id: pick_word
- color: "white"
- }
- }
- }
-
- MouseArea {
- id: mouse_area
- anchors.fill: parent
- hoverEnabled: false
- property var pickNode: null
- //鼠标和物体xy的偏移
- property real xOffset: 0
- property real yOffset: 0
- property real zOffset: 0
-
- onPressed: {
- //获取点在View上的屏幕坐标
- pick_screen.text = "(" + mouse.x + ", " + mouse.y + ")"
- //pick取与该点射线路径相交的离最近的Model的信息,返回PickResult对象
- //因为该模块一直在迭代,新的版本可以从PickResult对象获取更多的信息
- //Qt6中还提供了pickAll获取与该射线相交的所有Model信息
- var result = control.pick(mouse.x, mouse.y)
- //目前只在点击时更新了pick物体的信息
- if (result.objectHit) {
- pickNode = result.objectHit
- pick_name.text = pickNode.objectName
- pick_distance.text = result.distance.toFixed(2)
- pick_word.text = "("
- + result.scenePosition.x.toFixed(2) + ", "
- + result.scenePosition.y.toFixed(2) + ", "
- + result.scenePosition.z.toFixed(2) + ")"
- //console.log('in',pick_screen.text)
- //console.log(result.scenePosition)
- var map_from = control.mapFrom3DScene(pickNode.scenePosition)
- //var map_to = control.mapTo3DScene(Qt.vector3d(mouse.x,mouse.y,map_from.z))
- //console.log(map_from)
- //console.log(map_to)
- xOffset = map_from.x - mouse.x
- yOffset = map_from.y - mouse.y
- zOffset = map_from.z
- } else {
- pickNode = null
- pick_name.text = "None"
- pick_distance.text = " "
- pick_word.text = " "
- }
- }
- onPositionChanged: {
- if(!mouse_area.containsMouse || !pickNode){
- return
- }
- var pos_temp = Qt.vector3d(mouse.x + xOffset, mouse.y + yOffset, zOffset);
- var map_to = control.mapTo3DScene(pos_temp)
- pickNode.x = map_to.x
- pickNode.y = map_to.y
- }
- }
- }
参考
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。