이 페이지에서

Qt Quick 3D - XR 3D 상호작용 예시

Qt Quick 3D XR을 사용하여 3D 오브젝트를 조작하는 방법을 보여줍니다.

우주 공간에 알록달록한 글자와 도형들이 떠 있는 3D 장면

이 예제는 씬 내에서 3D 오브젝트를 이동하고 크기를 조정하는 컴포넌트를 만드는 방법을 보여줍니다. 이 예제는 xr_input 예제와 동일한 레이 피킹(ray-picking) 전략을 사용합니다.

컨트롤러 입력

이 예제에서는 AimController 라는 재사용 가능한 컴포넌트를 생성합니다. 이 컴포넌트는 레이 피킹을 수행하여 모델을 선택하며, 선택된 모델을 사용자가 잡고 이동할 수 있도록 선택적으로 지원합니다.

AimController 사용자 정의 상호 작용을 구현할 수 있도록 신호를 정의합니다:

property Model hoveredObject: null
signal objectPressed(obj: Model, pos: vector3d, direction: vector3d)
signal moved(pos: vector3d, direction: vector3d)
signal released()
signal objectGrabbed(obj: Model)

가젯

우리는 ` XrGadget`라는 추상 컴포넌트를 정의하며, 이 컴포넌트는 ` handleControllerPress `와 ` handleControllerMove`라는 두 가지 함수를 가집니다. C++에서는 이러한 함수들이 가상 함수(virtual)로 정의되었을 것입니다. 하지만 이 예제는 완전히 QML로 구현되었으므로, 대신 하위 컴포넌트에서 처리할 수 있는 신호를 발산합니다.

예를 들어, ` TranslateGadget `는 ` onMoved ` 신호에 따라 제어 대상 객체를 가젯의 축을 따라 이동시킵니다:

onMoved: (pos, dir) => {
    let moveDirection = delta.normalized()
    let mapped_axis = controlledObject.mapDirectionToScene(axisDirection).normalized()
    let dot = mapped_axis.dotProduct(moveDirection)
    let offset = mapped_axis.times(delta.length() * dot)
    controlledObject.position = originalPos.plus(offset)
}

모든 것을 통합하기

GadgetBox 라는 컴포넌트를 정의합니다. 이 컴포넌트는 어떤 객체가 선택되었는지 추적하고, 선택된 객체 주위에 반투명 상자를 표시할 뿐만 아니라 객체 주위에 가젯도 표시합니다. 선택된 객체를 누르면 ` GadgetBox ` 신호가 발생하여 세 가지 유형의 가젯(이동, 회전, 크기 조정)을 순차적으로 전환합니다.

main.qml 에서는 AimController 의 신호에 반응하여 GadgetBox 에 있는 함수들을 호출합니다:

AimController {
    id: rightAim
    controller: XrController.RightController
    view: xrView
    enableVirtualMouse: true
    enableThumbstickMove: thumbCheckBox.checked

    onObjectPressed: (obj, pos, dir) => {
        gadgetBox.handlePress(obj, pos, dir)
    }
    onHoveredObjectChanged: {
        gadgetBox.handleHover(hoveredObject)
        hapticFeedback.handleHover(hoveredObject)
    }
    onMoved: (pos, dir) => {
        gadgetBox.handleMove(pos, dir)
    }
    onReleased: {
        gadgetBox.handleRelease()
    }
    onObjectGrabbed: (obj) => {
        if (!grabCheckBox.checked)
            return
        const gadget = obj as XrGadget
        if (!gadget)
            startGrab(obj)
        else if (gadget.grabbable)
            startGrab(gadget.controlledObject)
    }
    Model {
        source: "#Cylinder"
        scale: "0.05, 0.1, 0.05"
        z: 5
        eulerRotation.x: 90
        materials: PrincipledMaterial {
            baseColor: "black"
        }
    }
    xrCursor: cursor
}

또한 마우스가 올려진 객체나 가젯이 변경될 때 햅틱 피드백을 제공합니다:

XrHapticFeedback {
    id: hapticFeedback
    controller: XrHapticFeedback.RightController
    hapticEffect: XrSimpleHapticEffect {
        amplitude: 0.5
        duration: 10
        frequency: 200
    }
    property Model prevObj: null
    function handleHover(obj: Model) {
        if (obj && obj !== prevObj)
            start()
        prevObj = obj
    }
}

코드 예제 @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.