Rendering distance in Qt Quick 3D
-
Hi
I realise that most of the discussion here is about Qt3D, but i have a question about Qt Quick 3D. Even in basic example, the max rendering distance from the camera seems to be fixed.
E.g. positioning a sphere at the origin looks like this
But when you zoom out one step further, the sphere disappears:
I see there is a clipFar property on the PerspectiveCamera that i am using, but it doesn't seem to change this behaviour. Is there any way to get it to keep rendering a larger distance away?
Thanks in advance.
Minimal QML for this is as below:
import QtQuick import QtQuick3D import QtQuick3D.Helpers Item { anchors.fill: parent Node { id: sceneRoot Model { source: "#Sphere" materials: [ PrincipledMaterial { baseColor: "red" } ] scale: Qt.vector3d(10.0, 20.0, 20.0) position: Qt.vector3d(-100.0, 200.0, 0.0) eulerRotation.y: 30 } } View3D { anchors.fill: parent importScene: sceneRoot environment: ExtendedSceneEnvironment { backgroundMode: SceneEnvironment.SkyBox lightProbe: Texture { textureData: ProceduralSkyTextureData{} } InfiniteGrid { gridInterval: 100 } } Node { id: originNode eulerRotation.x: -60 PerspectiveCamera { id: cameraNode z: 400 clipFar: 100000.0 } } OrbitCameraController { origin: originNode camera: cameraNode } } }
-
@bluezoo Hi
This seems like a bug of OrbitController. I replaced in your code moving the camera with the mouse whell and changing clipFar to different distances, it works.
import QtQuick import QtQuick3D import QtQuick3D.Helpers Window { width: 640 height: 480 visible: true title: qsTr("Hello World") View3D { anchors.fill: parent camera: cameraNode environment: ExtendedSceneEnvironment { backgroundMode: SceneEnvironment.SkyBox lightProbe: Texture { textureData: ProceduralSkyTextureData{} } InfiniteGrid { gridInterval: 100 } } Node { id: originNode //eulerRotation.x: -60 PerspectiveCamera { id: cameraNode y: 100 z: 3000 clipNear: 1 clipFar: 20000 onPositionChanged: console.log("camera position: ", position) } Model { source: "#Sphere" materials: [ PrincipledMaterial { baseColor: "red" } ] scale: Qt.vector3d(10.0, 20.0, 20.0) //position: Qt.vector3d(-100.0, 200.0, 0.0) //eulerRotation.y: 30 } }//originNode /*OrbitCameraController { anchors.fill: parent origin: originNode camera: cameraNode }*/ }//View3D MouseArea { anchors.fill: parent onWheel: { console.log("angleDelta: ", wheel.angleDelta.y) if (wheel.angleDelta.y > 0) cameraNode.position.z += 100 else cameraNode.position.z -= 100 } } }
If you comment MouseArea and uncomment OrbitCameraController it will fail. Note that I adated your code slighty has it was crashing, also remove the euler rotations for simplicity. Please fill a bug report at https://bugreports.qt.io
As a workaround you could try to mimic OrbitCameraController with MouseArea and quaternions which is not very trivial, or try to scale down the all view, so that it will not go further than the 10000 distance units. -
Ah I see - thanks for working this out...this was going to be a showstopper for what I was trying to achieve, so this is much appreciated. I will work on a replacement for the OrbitCameraController, and file the bug report. Apologies for the non-working code - I excised it from a larger project and didn't get a chance to test it before I posted.
-
-
@bluezoo Hi again
Here is quick fix fully working, I just checked OrbitCameraController.qml code, indeed it has the clipNear and clipFar values hardcoded. Add a new qml file to your project, call it NewController or whatever, copy paste the OrbitCameraController.qml code and remove the connection that is causing the bug. Then just use the NewController component instead of OrbitCameraController.
NewController.qml:
// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only import QtQuick import QtQuick3D Item { id: root required property Node origin required property Camera camera property real xSpeed: 0.1 property real ySpeed: 0.1 property bool xInvert: false property bool yInvert: true property bool mouseEnabled: true property bool panEnabled: true readonly property bool inputsNeedProcessing: status.useMouse || status.isPanning implicitWidth: parent.width implicitHeight: parent.height /*Connections { target: root.camera function onZChanged() { // Adjust near/far values based on distance let distance = root.camera.z if (distance < 1) { root.camera.clipNear = 0.01 root.camera.clipFar = 100 if (camera.z === 0) { console.warn("camera z set to 0, setting it to near clip") root.camera.z = camera.clipNear } } else if (distance < 100) { root.camera.clipNear = 0.1 root.camera.clipFar = 1000 } else { root.camera.clipNear = 1 root.camera.clipFar = 10000 } } }*/ DragHandler { id: dragHandler target: null enabled: root.mouseEnabled acceptedModifiers: Qt.NoModifier onCentroidChanged: { root.mouseMoved(Qt.vector2d(centroid.position.x, centroid.position.y), false); } onActiveChanged: { if (active) root.mousePressed(Qt.vector2d(centroid.position.x, centroid.position.y)); else root.mouseReleased(Qt.vector2d(centroid.position.x, centroid.position.y)); } } DragHandler { id: ctrlDragHandler target: null enabled: root.mouseEnabled && root.panEnabled acceptedModifiers: Qt.ControlModifier onCentroidChanged: { root.panEvent(Qt.vector2d(centroid.position.x, centroid.position.y)); } onActiveChanged: { if (active) root.startPan(Qt.vector2d(centroid.position.x, centroid.position.y)); else root.endPan(); } } PinchHandler { id: pinchHandler target: null enabled: root.mouseEnabled property real distance: 0.0 onCentroidChanged: { root.panEvent(Qt.vector2d(centroid.position.x, centroid.position.y)) } onActiveChanged: { if (active) { root.startPan(Qt.vector2d(centroid.position.x, centroid.position.y)) distance = root.camera.z } else { root.endPan() distance = 0.0 } } onScaleChanged: { root.camera.z = distance * (1 / scale) } } TapHandler { onTapped: root.forceActiveFocus() // qmllint disable signal-handler-parameters } WheelHandler { id: wheelHandler orientation: Qt.Vertical target: null enabled: root.mouseEnabled acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad onWheel: event => { let delta = -event.angleDelta.y * 0.01; root.camera.z += root.camera.z * 0.1 * delta } } function mousePressed(newPos) { root.forceActiveFocus() status.currentPos = newPos status.lastPos = newPos status.useMouse = true; } function mouseReleased(newPos) { status.useMouse = false; } function mouseMoved(newPos: vector2d) { status.currentPos = newPos; } function startPan(pos: vector2d) { status.isPanning = true; status.currentPanPos = pos; status.lastPanPos = pos; } function endPan() { status.isPanning = false; } function panEvent(newPos: vector2d) { status.currentPanPos = newPos; } FrameAnimation { id: updateTimer running: root.inputsNeedProcessing onTriggered: status.processInput(frameTime * 100) } QtObject { id: status property bool useMouse: false property bool isPanning: false property vector2d lastPos: Qt.vector2d(0, 0) property vector2d lastPanPos: Qt.vector2d(0, 0) property vector2d currentPos: Qt.vector2d(0, 0) property vector2d currentPanPos: Qt.vector2d(0, 0) function negate(vector) { return Qt.vector3d(-vector.x, -vector.y, -vector.z) } function processInput(frameDelta) { if (useMouse) { // Get the delta var rotationVector = root.origin.eulerRotation; var delta = Qt.vector2d(lastPos.x - currentPos.x, lastPos.y - currentPos.y); // rotate x var rotateX = delta.x * xSpeed * frameDelta if (xInvert) rotateX = -rotateX; rotationVector.y += rotateX; // rotate y var rotateY = delta.y * -ySpeed * frameDelta if (yInvert) rotateY = -rotateY; rotationVector.x += rotateY; origin.setEulerRotation(rotationVector); lastPos = currentPos; } if (isPanning) { let delta = currentPanPos.minus(lastPanPos); delta.x = -delta.x delta.x = (delta.x / root.width) * camera.z * frameDelta delta.y = (delta.y / root.height) * camera.z * frameDelta let velocity = Qt.vector3d(0, 0, 0) // X Movement let xDirection = origin.right velocity = velocity.plus(Qt.vector3d(xDirection.x * delta.x, xDirection.y * delta.x, xDirection.z * delta.x)); // Y Movement let yDirection = origin.up velocity = velocity.plus(Qt.vector3d(yDirection.x * delta.y, yDirection.y * delta.y, yDirection.z * delta.y)); origin.position = origin.position.plus(velocity) lastPanPos = currentPanPos } } } }
In main.qml replace OrbitCameraController with NewController
NewController { anchors.fill: parent origin: originNode camera: cameraNode }
Edit: I created a bug report here https://bugreports.qt.io/browse/QTBUG-129253
Edit 2: I see you also created a bug report, thank you, I did search before I post the bug report but since I did not find it, I went ahead, sorry :)