Qt3D : Scene2D를 창과 같은 크기 (픽셀 단위)로 조정하는 방법은 무엇입니까?

Nov 18 2020

Qt Quick 2 요소와 Qt 3D 개체를 사용하여 QML에서 몇 개의 개체를 성공적으로 그리는 800x600 창으로 C ++ 응용 프로그램을 만들었습니다.

QML 코드는 Rectangle내부에 Qt Quick 2 요소를 사용하여 두 개의 녹색 / 노란색 직사각형을 그립니다 Scene2D. 그런 다음 2D 장면이 렌더링을 위해 3D 큐브의 표면 중 하나로 블릿 처리되고 3D 세계에 표시됩니다. 마지막으로 SphereMeshQt 3D 의 파란색 이 위의 스크린 샷과 같이 중앙에 렌더링됩니다.

3D 큐브 (2D UI가 렌더링되는 위치)의 크기를 조정하여 창과 동일한 크기를 갖도록 시도했지만 프로그래밍 방식으로 수행 할 방법을 찾을 수 없습니다.

그렇다면 문제는 3D 큐브의 크기를 조정하거나 크기를 조정하여 창과 같은 크기로 자동 조정되도록하는 방법입니다.

큐브가 창과 동일한 양의 픽셀을 가질 수있는 솔루션을 찾고 있습니다. 예를 들어, 800x600 창에서 800x600 녹색 직사각형을보고 싶습니다.

내가 시도한 것은 다음과 같습니다 . 3D 세계의 중심과 camZ의 거리 인의 값을 손으로 조정할 수 Camera있으며 약간의 눈으로 볼 수 있지만 정확한 해결책은 아닙니다. 나중에 창을 다른 것으로 변경하는 경우 새로운 값이 무엇인지 알아 내기 위해 다시 많은 테스트를 camZ해야합니다.

어떤 아이디어?

main.cpp :

#include <QGuiApplication>
#include <QQmlContext>

#include <Qt3DQuickExtras/qt3dquickwindow.h>
#include <Qt3DQuick/QQmlAspectEngine>


int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);

    Qt3DExtras::Quick::Qt3DQuickWindow view;
    view.setSource(QUrl("qrc:/main.qml"));
    auto rootContext = view.engine()->qmlEngine()->rootContext();
    rootContext->setContextProperty("_window", &view);
    view.resize(800, 600);
    view.show();

    return app.exec();
}

main.qml :

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

import QtQuick 2.0
import QtQuick.Scene2D 2.9
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2

Entity
{
    id: sceneRoot
    property int w: _window.width
    property int h: _window.height
    property real camZ: 1000

    /* setup camera */

    Camera {
        id: mainCamera
        projectionType: CameraLens.PerspectiveProjection
        fieldOfView: 45
        aspectRatio: _window.width / _window.height
        nearPlane:   0.01
        farPlane: 1000000.0
        position:    Qt.vector3d( 0.0, 0.0, sceneRoot.camZ )
        viewCenter:  Qt.vector3d( 0.0, 0.0, 0.0 )
        upVector:    Qt.vector3d( 0.0, 1.0, 0.0 )
    }

    components: [
        RenderSettings {
            activeFrameGraph: ForwardRenderer {
                camera: mainCamera
                clearColor: "white"
            }
            pickingSettings.pickMethod: PickingSettings.TrianglePicking
        },

        InputSettings {}
    ]

    /* setup a 3D cube to be used as the 2D drawing surface for all Qt Quick 2 stuff */

    Entity {
        id: drawingSurface

        CuboidMesh {
            id: planeMesh
        }

        Transform {
            id: planeTransform
            translation: Qt.vector3d(0, 0, 0)
            scale3D: Qt.vector3d(sceneRoot.w, sceneRoot.h, 1)
        }

        TextureMaterial {
            id: planeMaterial
            texture: offscreenTexture  // created by qmlTexture below
        }

        // picked up by Scene2D’s "entities" property and used as a source for events
        ObjectPicker {
            id: planePicker
            hoverEnabled: false
            dragEnabled: false
        }

        components: [ planeMesh, planeMaterial, planeTransform, planePicker ]
    }

    /* setup Scene2D offscreen texture to be used as canvas by Qt Quick 2 */

    Scene2D {
        id: qmlTexture
        output: RenderTargetOutput {
            attachmentPoint: RenderTargetOutput.Color0
            texture: Texture2D {
                id: offscreenTexture
                width: sceneRoot.w
                height: sceneRoot.h
                format: Texture.RGBA8_UNorm
                generateMipMaps: true
                magnificationFilter: Texture.Linear
                minificationFilter: Texture.LinearMipMapLinear
                wrapMode {
                    x: WrapMode.ClampToEdge
                    y: WrapMode.ClampToEdge
                }
            }
        }

        mouseEnabled: false
        entities: [ drawingSurface ]

        /* Qt Quick 2 rendering */

        Rectangle {
            width: offscreenTexture.width
            height: offscreenTexture.height
            x: 0
            y: 0
            border.color: "red"
            color: "green"

            Component.onCompleted: {
                console.log("Outter rectangle size: " + width + "x" + height + " at " + x + "," + y);
            }

            Rectangle {
                id: innerRect
                height: parent.height*0.6
                width: height
                x: (parent.width/2) - (width/2)
                y: (parent.height/2) - (height/2)
                border.color: "red"
                color: "yellow"
                transform: Rotation { origin.x: innerRect.width/2; origin.y: innerRect.height/2; angle: 45}

                Component.onCompleted: {
                    console.log("Inner rectangle size: " + width + "x" + height + " at " + x + "," + y);
                }
            }
        }

    } // Scene2D

    /* add light source at the same place as the camera */

    Entity {
        PointLight {
            id: light
            color: "white"
            intensity: 1
            constantAttenuation: 1.0
            linearAttenuation: 0.0
        }

        Transform {
            id: lightTransform
            translation: Qt.vector3d(0.0, 0.0, sceneRoot.camZ)
        }

        components: [ light, lightTransform ]
    }

    /* display 3D object */

    Entity {
        SphereMesh {
            id: mesh
            radius: 130
        }

        PhongMaterial {
            id: material
            ambient: "blue"
        }

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

       components: [ mesh, material, transform ]
   }

} // sceneRoot

.pro 파일에 다음 모듈을 추가합니다.

QT += qml quick 3dquick 3dquickextras

답변

3 FlorianBlume Nov 20 2020 at 09:04

일반적으로 전체 화면을 덮는 텍스처를 원할 때 직교 투영 을 사용 합니다. 원근 투영 개체와 달리 카메라와의 거리에 관계없이 항상 화면에 동일한 크기로 나타납니다. 이러한 유형의 투영은 건물 등의 3D 계획을 시각화하거나 UI 요소를 3D로 렌더링하는 데 자주 사용됩니다.

아이디어는 이제 분기를 프레임 그래프해야한다는 것입니다.

  1. 배경 이미지를 그립니다.
  2. 모든 개체를 그립니다
                     RenderSurfaceSelector
                                |
                             Viewport
                                |
          -------------------------------------------
          |             |             |             |
     ClearBuffers  LayerFilter   ClearBuffers  LayerFilter
          |             |             |             |
        NoDraw    CameraSelector    NoDraw    CameraSelector

첫 번째 (왼쪽에서 오른쪽으로) 지우기 버퍼는 모든 버퍼를 지 웁니다. 첫 번째 레이어 필터는 배경 레이어를 필터링합니다 (배경 엔티티에 연결해야 함). 두 번째 지우기 버퍼는 깊이 만 지 웁니다 (객체가 확실히 그려 지도록). 두 번째 레이어 필터는 메인 레이어 (그리려는 모든 개체에 연결해야 함)를 필터링합니다.

그런 다음 배경 카메라를 만들고 투영 유형을 직교 투영으로 설정합니다.

Camera {
        id: backgroundCamera
        projectionType: CameraLens.OrthographicProjection
        fieldOfView: 45
        aspectRatio: sceneRoot.w / sceneRoot.h
        left: - sceneRoot.w / 2
        right: sceneRoot.w / 2
        bottom: - sceneRoot.h / 2
        top: sceneRoot.h / 2
        nearPlane:   0.1
        farPlane:    1000.0
        position:    Qt.vector3d( 0.0, 0.0, 1.0 )
        viewCenter:  Qt.vector3d( 0.0, 0.0, 0.0 )
        upVector:    Qt.vector3d( 0.0, 1.0, 0.0 )
}

당신은 또한 선택할 수 -11좌측에 - 오른쪽 하단 - 상단 대신 sceneRoot.w하고 sceneRoot.h. 이 경우 질감이있는 평면의 크기를 (2, 2). 저는 사용자가 텍스처에 클릭 한 횟수를 그리고 싶었습니다. 그래서 화면 크기를 선택했습니다.

참고 :nearPlane 및에 대해 매우 높거나 낮은 값을 사용하지 마십시오 farPlane. Qt3D 문서 (어딘가에서 지금 찾을 수 없음)에서 원거리 평면이 100.000 이상으로 설정되면 부정확성이 발생한다고 말합니다. 또한 너무 작게 설정하면 같은 일이 발생합니다. 인터넷에서 읽을 수 있습니다. 이것은 3D 컴퓨터 그래픽의 일반적인 문제입니다.

다음은 전체 코드입니다.

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

import QtQuick 2.0
import QtQuick.Scene2D 2.9
import QtQuick.Controls 1.4
import QtQuick.Layouts 1.2

Entity
{
    id: sceneRoot
    property int w: _window.width
    property int h: _window.height
    property real camZ: 1000

    components: [
        RenderSettings {
            activeFrameGraph:  RenderSurfaceSelector {
                id: surfaceSelector

                Viewport {
                    id: mainViewport
                    normalizedRect: Qt.rect(0, 0, 1, 1)

                    ClearBuffers {
                        buffers: ClearBuffers.ColorDepthBuffer
                        clearColor: Qt.rgba(0.6, 0.6, 0.6, 1.0)

                        NoDraw {
                            // Prevent drawing here, we only want to clear the buffers
                        }
                    }

                    LayerFilter {
                        id: backgroundLayerFilter

                        layers: [backgroundLayer]

                        CameraSelector {
                            id: backgroundCameraSelector
                            camera: backgroundCamera
                        }
                    }

                    ClearBuffers {
                        buffers: ClearBuffers.DepthBuffer

                        NoDraw {
                            // Prevent drawing here, we only want to clear the buffers
                        }
                    }

                    LayerFilter {
                        id: mainLayerFilter

                        layers: [mainLayer]

                        CameraSelector {
                            id: mainCameraSelector
                            camera: mainCamera
                        }
                    }
                }
            }
            pickingSettings.pickMethod: PickingSettings.TrianglePicking
        },

        InputSettings {}
    ]

    Camera {
        id: mainCamera
        projectionType: CameraLens.PerspectiveProjection
        fieldOfView: 45
        aspectRatio: _window.width / _window.height
        nearPlane:   0.1
        farPlane:    1000.0
        position:    Qt.vector3d( 0.0, 0.0, camZ )
        viewCenter:  Qt.vector3d( 0.0, 0.0, 0.0 )
        upVector:    Qt.vector3d( 0.0, 1.0, 0.0 )
    }

    /* setup camera */

    Camera {
        id: backgroundCamera
        projectionType: CameraLens.OrthographicProjection
        fieldOfView: 45
        aspectRatio: sceneRoot.w / sceneRoot.h
        left: - sceneRoot.w / 2
        right: sceneRoot.w / 2
        bottom: - sceneRoot.h / 2
        top: sceneRoot.h / 2
        nearPlane:   0.1
        farPlane:    1000.0
        position:    Qt.vector3d( 0.0, 0.0, 1.0 )
        viewCenter:  Qt.vector3d( 0.0, 0.0, 0.0 )
        upVector:    Qt.vector3d( 0.0, 1.0, 0.0 )
    }

    /* setup a 3D cube to be used as the 2D drawing surface for all Qt Quick 2 stuff */

    Entity {
        id: drawingSurface

        PlaneMesh {
            id: planeMesh
            width: sceneRoot.w
            height: sceneRoot.h
        }

        Transform {
            id: planeTransform
            translation: Qt.vector3d(0, 0, 0)
            rotationX: 90
        }

        TextureMaterial {
            id: planeMaterial
            texture: offscreenTexture  // created by qmlTexture below
        }

        Layer {
            id: backgroundLayer
        }

        // picked up by Scene2D’s "entities" property and used as a source for events
        ObjectPicker {
            id: planePicker
            hoverEnabled: false
            dragEnabled: false
        }

        components: [ planeMesh, planeMaterial, planeTransform, planePicker, backgroundLayer ]
    }

    /* setup Scene2D offscreen texture to be used as canvas by Qt Quick 2 */

    Scene2D {
        id: qmlTexture
        output: RenderTargetOutput {
            attachmentPoint: RenderTargetOutput.Color0
            texture: Texture2D {
                id: offscreenTexture
                width: sceneRoot.w
                height: sceneRoot.h
                format: Texture.RGBA8_UNorm
                generateMipMaps: true
                magnificationFilter: Texture.Linear
                minificationFilter: Texture.LinearMipMapLinear
                wrapMode {
                    x: WrapMode.ClampToEdge
                    y: WrapMode.ClampToEdge
                }
            }
        }

        mouseEnabled: false
        entities: [ drawingSurface ]

        /* Qt Quick 2 rendering */

        Rectangle {
            width: offscreenTexture.width
            height: offscreenTexture.height
            x: 0
            y: 0
            border.color: "red"
            color: "green"

            Component.onCompleted: {
                console.log("Outter rectangle size: " + width + "x" + height + " at " + x + "," + y);
            }

            Rectangle {
                id: innerRect
                height: parent.height*0.6
                width: height
                x: (parent.width/2) - (width/2)
                y: (parent.height/2) - (height/2)
                border.color: "red"
                color: "yellow"
                transform: Rotation { origin.x: innerRect.width/2; origin.y: innerRect.height/2; angle: 45}

                Component.onCompleted: {
                    console.log("Inner rectangle size: " + width + "x" + height + " at " + x + "," + y);
                }
            }
        }

    } // Scene2D

    /* add light source at the same place as the camera */

    Layer {
        id: mainLayer
    }

    Entity {
        PointLight {
            id: light
            color: "white"
            intensity: 1
            constantAttenuation: 1.0
            linearAttenuation: 0.0
        }

        Transform {
            id: lightTransform
            translation: Qt.vector3d(0.0, 0.0, sceneRoot.camZ)
        }

        components: [ light, lightTransform, mainLayer ]
    }

    /* display 3D object */

    Entity {
        SphereMesh {
            id: mesh
            radius: 130
        }

        PhongMaterial {
            id: material
            ambient: "blue"
        }

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

       components: [ mesh, material, transform, mainLayer ]
   }

} // sceneRoot

결과 스크린 샷 :

그건 그렇고 : 당신의 코드는 오프 스크린 표면에 그리기 때문에 버그가있는 결과를 생성합니다. 오프 스크린 렌더링 프레임 그래프를 만들고 실제 그 안에 물건을 그리는 것이 좋습니다. 체크 아웃 이 아주 좋은 정보를 GitHub의의의 repo 및 내 C ++ Qt3D 렌더러 오프 스크린 구현을 .

참고로 원근 투영을 사용하여 동일한 결과를 확실히 얻을 수 있습니다. 인터넷에서 투시 투영에 대해 읽을 수 있습니다 (예 : 여기) . 기본적으로, 픽셀 좌표 (평면이 화면에 나타나기를 원하는 위치)를 알고 평면의 3D 점을 해결하는 선형 equestions 시스템이 있습니다. 하지만 복잡해질 수 있습니다. 게시 한 솔루션이 사용하기 더 쉽다고 확신합니다.)