如何在 QML 中旋转 3D 立方体?

3
我正在使用Qt Creator和QtQuick开发一个应用程序。该应用程序的目的是通过手指的帮助在三维空间内旋转一个立方体。
我成功地实现了与手指移动相关的操作,但是在立方体与手指之间的旋转上完全卡住了。
我尝试创建一个方向向量,根据当前点和前一个点以及法线向量来确定它。我对立方体进行了沿x和y轴的欧拉旋转,但是到某个点后,立方体旋转不如预期。
这是我的主页QML代码:main.qml
import QtQuick 2.15
import QtQuick.Window 2.14
import QtQuick3D 1.15
import Qt3D.Input 2.0
import QtQuick.Controls 2.1
import Qt3D.Extras 2.15

Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("MouseArea Demo")

        MouseArea{
            property int previousX: -1
            property int previousY: -1

            anchors.fill : parent

            onPressed: {

                previousX = mouseX;
                previousY = mouseY;
            }

            onPositionChanged: {

                let direction = Qt.vector2d(mouseX - previousX, mouseY - previousY).normalized();
                let normal = Qt.vector2d(direction.y, direction.x).normalized();
                console.log("direction : " + direction);
                console.log("normal : " + normal);
                cube.eulerRotation.x += normal.x * 3;
                cube.eulerRotation.y += normal.y * 3;



                previousX = mouseX;
                previousY = mouseY
            }

            onReleased: {

                previousX = -1;
                previousY = -1;

            }
        }

    View3D {
        id: view
        anchors.fill: parent
        camera: camera
        renderMode: View3D.Overlay


        PerspectiveCamera {
            id: camera
            position: Qt.vector3d(0, 200, 300)
            eulerRotation.x: -30

        }

        DirectionalLight {
            eulerRotation.x: -30
        }

        Model {

            id: cube
            visible: true
            position: Qt.vector3d(0, 0, 0)
            source: "#Cube"
            materials: [ DefaultMaterial {
                    diffuseMap: Texture {
                        id: texture
                        source: "../build-colorpicker2d-Desktop_Qt_5_15_2_GCC_64bit-Debug/res.png"
                    }
                }
            ]                       
        }
    }

}

这里有一个关于我两天前跟你提到的y轴旋转问题的视频。

此致敬礼。

enter image description here


1
你有几个选项:使用四元数旋转 - Node.rotation 是我更喜欢的选择,还有Node.eulerRotation,但你必须记住旋转顺序很重要。另一个重要的事情是原点。在我的应用程序中,我手动计算角度,然后使用rotationposition放置形状,即使用偏移量放置形状。 - folibis
1
你是否觉得使用QtQuick3D很重要?因为你可以使用Qt3D类代替QtQuick3D,这样你想要的东西就变得非常容易了。 - Parisa.H.R
1
立方体根据 cube.eulerRotation 进行旋转。问题在于,经过几次点击后,立方体可能会颠倒,这意味着 Y 轴将被反转。而且,拖动鼠标上下会对立方体产生完全相反的效果,这是一个不容易用 QtQuick3D 解决的问题,因此我赞同 Parisa 的答案。 - karlphillip
谢谢你们的回答,但我在3D方面是新手,在大学里从未接触过。你们能给我一个folibis的例子吗?Parisa和karlphillip,我需要在QtQuick的qml中完成它,这是我的实习任务。 - Etienne PERIANA
好的,我为您写一个 QML 的例子,但是我使用 Qt3d 类,我使用 Scene3D 和 Entity 代替 View3D 和 Model。 - Parisa.H.R
QtQuick和QtQuick3d之间有区别。请查看此网站。在我发布的示例中,我使用qml,这是一个QtQuick项目,但我使用了Qt 3D类,这些类比QtQuick3d类更加稳定,请参考此处,希望我能表达清楚。 - Parisa.H.R
2个回答

2

首先,您应该在.pro文件中添加以下内容:

QT += qml quick 3dcore 3dinput 3dquick 3dlogic  3dquickextras  3dextras

我在qml中有三个类:main.qmlRootEntity.qmlSOrbitCameraController.qml

我有一个Scene3D,在其中可以放置所有的实体。我创建了一个单独的类并将其称为RootEntity。 这种方式的更重要的一点是我在我的类中使用的orbitController。这使得你可以通过鼠标旋转立方体,而为了做到这一点,你需要实体。因此,我使用Scene3D和Entity而不是view3d和models。

main.qml中:

import QtQuick 2.12
import QtQuick.Scene3D 2.12
import QtQuick.Window 2.12

import "."

Window {
    visible: true
    width: 640
    height: 480

    Scene3D
    {
        id : scene3d
        anchors.fill: parent
        focus: true
        aspects: ["render", "logic", "input"]
        hoverEnabled: true
        cameraAspectRatioMode: Scene3D.AutomaticAspectRatio


        antialiasing: true

        RootEntity
        {
            id:root
        }

    }

}

in RootEntity.qml:

import QtQuick 2.0

import Qt3D.Core 2.12
import Qt3D.Render 2.12
import Qt3D.Extras 2.12
import Qt3D.Input 2.12

import "."


Entity {
    id: root

    //create camera

    Camera {
        id: mainCamera
        projectionType: CameraLens.PerspectiveProjection
        fieldOfView: 45
        aspectRatio: 16/9
        nearPlane : 0.1
        farPlane : 1000.0
        position: Qt.vector3d(0.0, 4.49373, -3.78577)
        upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
        viewCenter: Qt.vector3d(0.0, 0.5, 0.0)
    }

    //use my class instead of OrbitCameraController

    SOrbitCameraController {
        id: mainCameraController
        camera: mainCamera
    }

    components: [
        RenderSettings {

            Viewport {
                normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)
                RenderSurfaceSelector {
                    CameraSelector {
                        id: cameraSelector
                        camera: mainCamera
                        FrustumCulling {
                            ClearBuffers {
                                buffers: ClearBuffers.AllBuffers
                                clearColor: "#444449"
                                NoDraw {}
                            }
                            LayerFilter {
                                filterMode: LayerFilter.DiscardAnyMatchingLayers
                                layers: [topLayer]
                            }
                            LayerFilter {
                                filterMode: LayerFilter.AcceptAnyMatchingLayers
                                layers: [topLayer]
                                ClearBuffers {
                                    buffers: ClearBuffers.DepthBuffer
                                }
                            }
                        }
                    }
                }
            }
        },
        InputSettings {}
        ,
        ScreenRayCaster
        {
            id:screenRayCaster
            onHitsChanged:
            {
                drawLineMesh(hits)

            }

        }
    ]

    Layer {
        id: topLayer
        recursive: true
    }


    Entity {
        id: cubeEntity
        components: [
            CuboidMesh
            {
                xExtent: 1
                yExtent: 1
                zExtent: 1


            }

            ,

            Transform {
                id: t
                translation: Qt.vector3d(0, 0, 0)

            }
            ,
            PhongMaterial
            {
                ambient: "red"
            }


        ]

    }

}

SOrbitCameraController.qml中:

import Qt3D.Core 2.0
import Qt3D.Render 2.0
import Qt3D.Input 2.0

Entity{
    id: root
    property Camera camera;
    property real dt: 0.001
    property real linearSpeed: 1
    property real lookSpeed: 500
    property real zoomLimit: 0.16

    MouseDevice {
        id: mouseDevice
        sensitivity: 0.001 // Make it more smooth
    }

    MouseHandler {
        id: mh
        readonly property vector3d upVect: Qt.vector3d(0, 1, 0)
        property point lastPos;
        property real pan;
        property real tilt;
        sourceDevice: mouseDevice

        onPanChanged: root.camera.panAboutViewCenter(pan, upVect);
        onTiltChanged: root.camera.tiltAboutViewCenter(tilt);

        onPressed: {
            lastPos = Qt.point(mouse.x, mouse.y);
        }
        onPositionChanged: {
            // You can change the button as you like for rotation or translation
            if (mouse.buttons === 1){ // Left button for rotation
                pan = -(mouse.x - lastPos.x) * dt * lookSpeed;
                tilt = (mouse.y - lastPos.y) * dt * lookSpeed;
            } else if (mouse.buttons === 2) { // Right button for translate
                var rx = -(mouse.x - lastPos.x) * dt * linearSpeed;
                var ry = (mouse.y - lastPos.y) * dt * linearSpeed;
                camera.translate(Qt.vector3d(rx, ry, 0))
            } else if (mouse.buttons === 3) { // Left & Right button for zoom
                ry = (mouse.y - lastPos.y) * dt * linearSpeed
                zoom(ry)
            }

            lastPos = Qt.point(mouse.x, mouse.y)
        }
        onWheel: {
            zoom(wheel.angleDelta.y * dt * linearSpeed)
        }

        function zoom(ry) {
            if (ry > 0 && zoomDistance(camera.position, camera.viewCenter) < zoomLimit) {
                return
            }

            camera.translate(Qt.vector3d(0, 0, ry), Camera.DontTranslateViewCenter)
        }

        function zoomDistance(posFirst, posSecond) {
            return posSecond.minus(posFirst).length()
        }
    }
}

最后是我的 main.cpp 文件:

#include <QGuiApplication>
#include <QQmlApplicationEngine>

int  main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication        app(argc, argv);
    QQmlApplicationEngine  engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
    {
        return -1;
    }

    return app.exec();
}

输出结果 在此输入图像描述


嗨,Parisa,非常感谢您的示例,但我发现:当我将立方体向下旋转以完全翻转它时,左右手指的移动是相反的。我能修复它吗? - Etienne PERIANA
我在上一条回答中提到了我发现的旋转问题的错误。 - Etienne PERIANA
@EtiennePERIANA,我会给你展示你需要的内容,但你所说的又是另外一个问题。这是一个通过鼠标旋转立方体的例子。 - Parisa.H.R
是的,但你说过你会检查我上次给你展示的内容。 - Etienne PERIANA
我尝试在检查相机y位置后进行旋转,它可以工作,但有时旋转效果不好。 - Etienne PERIANA
显示剩余5条评论

1

Node有一个rotate()方法。诀窍是在Node.SceneSpace中进行旋转。这将确保在您选择的任何轴上正确地组合旋转,并且旋转将应用于最新方向的顶部。我使用MouseArea来捕获X或Y中的2D移动。在3D中,我们将其解释为分别在Y轴或X轴上的旋转。以下是一个工作示例:

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick3D
Page {
    background: Rectangle { color: "#848895" }
    Node {
        id: standAloneScene
        DirectionalLight { ambientColor: Qt.rgba(1.0, 1.0, 1.0, 1.0) }
        Node {
            id: node
            Model {
                id: model
                source: "#Cube"
                materials: [
                    DefaultMaterial { diffuseColor: Qt.rgba(0.053, 0.130, 0.219, 0.75) }
                ]
            }
        }
        OrthographicCamera {
            id: cameraOrthographicFront
            y: 500; z: 1000
            lookAtNode: node
        }
    }
    View3D {
        anchors.fill: parent
        importScene: standAloneScene
        camera: cameraOrthographicFront
    }
    MouseArea {
        anchors.fill:parent
        property real pressedX
        property real pressedY
        onMouseXChanged: Qt.callLater(update)
        onMouseYChanged: Qt.callLater(update)
        onPressed: {
            [pressedX,pressedY] = [mouseX,mouseY];
        }
        function update() {
            let [dx,dy] = [mouseX - pressedX,mouseY - pressedY];
            [pressedX,pressedY] = [mouseX,mouseY];
            node.rotate(dx, Qt.vector3d(0, 1, 0), Node.SceneSpace);
            node.rotate(dy, Qt.vector3d(1, 0, 0), Node.SceneSpace);
        }
    }
}

你可以在线尝试!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接