首页 > 解决方案 > 反转父 qt3d 实体变换(不适用于 scale3D)

问题描述

由于比这个最小测试用例更复杂的原因,我需要childEntity另一个实体(parentEntity,青色框)的实体( ,洋红色框)子,但childEntity应该独立于parentEntity' 转换。

因此我添加了这个处理程序:

QtQuick.Connections {
    target: parentTransform
    onMatrixChanged: {
        // cancel parent's transform
        var m = parentTransform.matrix
        var i = m.inverted()
        childTransform.matrix = i

        // debug:
        console.log(parentTransform.matrix.times(i))
    }
}

这适用于消除父级的平移和旋转,但不适用于缩放。

当父级scale3D不是 [1,1,1] 并且还设置了旋转时,尽管时间childEntity的乘积给出了 4x4 标识,但它会出现扭曲。为什么?parentTransform.matrixchildTransform.matrix

截屏

最小测试用例:(加载到 a 中QQuickView

import QtQml 2.12 as QtQml
import QtQuick 2.12 as QtQuick
import QtQuick.Controls 2.12 as QtQuickControls
import QtQuick.Scene3D 2.0

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

Scene3D {
    function change_translation_and_rotation() {
        parentTransform.translation.x = 0.1
        parentTransform.translation.y = 0.5
        parentTransform.translation.z = 2
        parentTransform.rotationX = 30
        parentTransform.rotationY = 60
        parentTransform.rotationZ = 10
    }

    function change_rotation_and_scale() {
        parentTransform.rotationX = 30
        parentTransform.rotationY = 60
        parentTransform.rotationZ = 10
        parentTransform.scale3D.x = 0.1
        parentTransform.scale3D.y = 0.5
        parentTransform.scale3D.z = 2
    }

    function reset_transform() {
        parentTransform.translation.x = -0.5
        parentTransform.translation.y = 0
        parentTransform.translation.z = 0.5
        parentTransform.rotationX = 0
        parentTransform.rotationY = 0
        parentTransform.rotationZ = 0
        parentTransform.scale3D.x = 1
        parentTransform.scale3D.y = 1
        parentTransform.scale3D.z = 1
    }

    data: [
        QtQml.Connections {
            target: parentTransform
            onMatrixChanged: {
                // cancel parent's transform
                var m = parentTransform.matrix
                var i = m.inverted()
                childTransform.matrix = i

                // debug:
                console.log(parentTransform.matrix.times(i))
            }
        },

        QtQuick.Column {
            spacing: 5
            QtQuick.Repeater {
                id: buttons
                model: ["change_translation_and_rotation", "change_rotation_and_scale", "reset_transform"]
                delegate: QtQuickControls.Button {
                    text: modelData.replace(/_/g, ' ')
                    font.bold: focus
                    onClicked: {focus = true; scene3d[modelData]()}
                }
            }
        }
    ]

    id: scene3d
    anchors.fill: parent
    aspects: ["render", "logic", "input"]

    Entity {
        id: root
        components: [RenderSettings {activeFrameGraph: ForwardRenderer {camera: mainCamera}}, InputSettings {}]

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

        OrbitCameraController {
            camera: mainCamera
        }

        Entity {
            id: parentEntity
            components: [
                CuboidMesh {
                    xExtent: 1
                    yExtent: 1
                    zExtent: 1
                },
                PhongMaterial {
                    ambient: "#6cc"
                },
                Transform {
                    id: parentTransform
                    translation: Qt.vector3d(-0.5, 0, 0.5)
                }
            ]

            Entity {
                id: childEntity
                components: [
                    CuboidMesh {
                        xExtent: 0.5
                        yExtent: 0.5
                        zExtent: 0.5
                    },
                    PhongMaterial {
                        ambient: "#c6c"
                    },
                    Transform {
                        id: childTransform
                        translation: Qt.vector3d(-0.5, 0, 0.5)
                    }
                ]
            }
        }

        QtQuick.Component.onCompleted: reset_transform()
    }
}

标签: qtqmlqt-quickqt3d

解决方案


问题是 QTransform 节点没有将转换存储为一般的 4x4 矩阵。而是将矩阵分解为以固定顺序应用的 3 个变换:

  • S - 对角缩放矩阵
  • R - 旋转矩阵
  • T - 翻译

然后按 T * R * S * X 的顺序将其应用于点 X。

矩阵属性的文档描述了这个分解步骤https://doc.qt.io/qt-5/qt3dcore-qtransform.html#matrix-prop

因此,当父级的变换为 M = T * R * S 时,子级的逆变换将是 M^-1 = S^-1 * R^-1 * T^-1。在子 QTransform 上设置逆将尝试以相同的方式分解它:

M^-1 = T_i * R_i * S_i = S^-1 * R^-1 * T^-1

那是行不通的,因为特别是 S 和 R 不会像这样通勤。

您可以在设置childTransform.matrix后通过比较childTransform.matrixi的值在代码中对此进行测试。

我认为唯一的解决方案是在嵌套在子对象上方的实体上使用 3 个 QTransform,以实现逆序的正确顺序为 S^-1 * R^-1 * T^-1。

从父 QTransform 中的相应参数计算逆 S^-1, R^-1 T^-1 非常简单。


推荐阅读