您当前的位置: 首页 >  ui

龚建波

暂无认证

  • 3浏览

    0关注

    312博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

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

龚建波 发布时间:2022-06-11 23:24:00 ,浏览量:3

(注意,开源版的 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
        }
    }
}

参考

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

关注
打赏
1655829268
查看更多评论
立即登录/注册

微信扫码登录

0.0524s