Search code examples
qtqmlqt3d

How to pick a color in a 3d colored cube in qml?


I am developping a 3D color picker cube in QML, I have developpe the cube textures but I don't know how to code the color picking.

I want when i click on a cube face to have the color on which I clicked, is it possible to do it in qml ?

Here is my code for the cube :

Entity {
    id: root

    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: cubeTransform.translation
    }

    CustomCameraController {
        id: mainCameraController
        camera: mainCamera
    }

    components: [

        DirectionalLight{
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(1, 0, 0)

        },

        DirectionalLight{
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(0, 1, 0)

        },

        DirectionalLight{
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(-1, 0, 0)

        },

        DirectionalLight{
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(0, -1, 0)

        },

        DirectionalLight{
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(0, 0, 1)

        },

        DirectionalLight{
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(0, 0, -1)

        },


        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: "black"
                                NoDraw {}
                            }

                            LayerFilter {
                                filterMode: LayerFilter.DiscardAnyMatchingLayers
                                layers: [topLayer]
                            }

                            LayerFilter {
                                filterMode: LayerFilter.AcceptAnyMatchingLayers
                                layers: [topLayer]

                                ClearBuffers {
                                    buffers: ClearBuffers.DepthBuffer
                                }
                            }
                        }
                    }

                }

            }
        },

        InputSettings {}
    ]

    Layer {
        id: topLayer
        recursive: true
    }

    Entity {
            id: cubeEntity


            Texture2D {
                id: cubeTexture

                TextureImage {
                    source: "qrc:/texture.png"
                }
            }

            Mesh {
                id: cubeMesh

                source: "qrc:/cube.obj"
            }

            Transform {
                id: cubeTransform
            }

            ObjectPicker {
                id: cubePicker

                onClicked: {

                    console.log("x : " + pick.localIntersection.normalized().x)
                    console.log("y : " + pick.localIntersection.normalized().y)
                    console.log("z : " + pick.localIntersection.normalized().z)

                }
            }

            NormalDiffuseMapMaterial{
                id: cubeMaterial
                diffuse: cubeTexture
                normal: cubeTexture
                specular: "black"
                shininess: 50
            }

            components: [cubeMesh, cubeTransform, cubeMaterial, cubePicker]

    }

}

I add, a picture of my actual cube if someone could help me, it will be great

enter image description here


Solution

  • Here is the answer for the color picking using qml, opengl and c++:

    C++ classes

    pixelvaluereader.h

    #pragma once
    
    #include <QObject>
    #include <Qt3DRender/QRenderCaptureReply>
    
    class PixelValueReader : public QObject
    {
      Q_OBJECT
    public:
      explicit PixelValueReader(QObject *parent = nullptr);
    
    
      Q_INVOKABLE QColor getColor(Qt3DRender::QRenderCaptureReply *reply, int x, int y);
    
    signals:
    
      void newColor(QColor color);
    
    };
    

    pixelvaluereader.cpp

    #include "pixelvaluereader.h"
    #include <QDebug>
    
    
    
    
    PixelValueReader::PixelValueReader(QObject* parent)
        : QObject(parent)
    {
    
    }
    
    
    
    QColor PixelValueReader::getColor(Qt3DRender::QRenderCaptureReply* reply, int x, int y)
    {
    
        QRgb pixel = reply->image().pixel(x, y);
    
        int red = qRed(pixel);
        int blue = qBlue(pixel);
        int green = qGreen(pixel);
        int alpha = qAlpha(pixel);
    
        qDebug() << red * 0xFF000000 + green * 0xFF0000 + blue * 0xFF00 + alpha;
    
        if (red * 0xFF000000 + green * 0xFF0000 + blue * 0xFF00 + alpha > 0) {
          emit newColor(QColor(pixel));
          qDebug() << "color : " << QColor(pixel).name(); // here is the color of the pixel
    
        }
        // RGBA captures the ID but since we masked and right shifted the respective values in the shader
        // (e.g. (red & 0xFF000000) >> 24 for red) to prevent overflow in the color values we have to
        // undo the shift here again.
        return QColor(pixel);
    }
    

    main.cpp

    #include <Qt3DRender/QAbstractTextureImage>
    #include <Qt3DQuickExtras/qt3dquickwindow.h>
    #include <Qt3DQuick/QQmlAspectEngine>
    #include <QGuiApplication>
    #include <QQmlContext>
    #include <QQmlEngine>
    
    
    #include "pixelvaluereader.h"
    
    //Q_DECLARE_METATYPE(Qt3DRender::QAbstractTextureImage)
    
        int main(int argc, char* argv[])
        {
          QGuiApplication app(argc, argv);
        
          QVector<QVector3D> pos;
          pos << QVector3D(1, 1, 0);
          pos << QVector3D(-1, 2, 8);
          pos << QVector3D(1, 1, 7);
          pos << QVector3D(0, 0, 4);
          pos << QVector3D(1, 5, 1);
          pos << QVector3D(-3, 3, 0);
          pos << QVector3D(2, 2, -2);
        
        
          PixelValueReader *valueReader = new PixelValueReader();
          qmlRegisterType<PixelValueReader>("PixelValueReader", 1, 0, "PixelValueReader");
        
        
          Qt3DExtras::Quick::Qt3DQuickWindow view;
          view.setTitle("Instanced Rendering");
          view.resize(1600, 800);
          view.engine()->qmlEngine()->rootContext()->setContextProperty("_window", &view);
          view.engine()->qmlEngine()->rootContext()->setContextProperty("pixelValueReader", valueReader);
          view.setSource(QUrl("qrc:/main.qml"));
          view.show();
        
          return app.exec();
        }
    

    Opengl

    For opengl code you can find it in Florian Blume git project : here

    QML

    main.qml

    import QtQuick 2.1 as QQ2
    import Qt3D.Core 2.0
    import Qt3D.Render 2.10
    import Qt3D.Input 2.0
    import Qt3D.Extras 2.0
    
    Entity {
    
        components: [
            rendSettings,
            inputSettings,
            light1,
            light2,
            light3,
            light4,
            light5,
            light6
        ]
    
        DirectionalLight{
            id: light1
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(1, 0, 0)
    
        }
    
        DirectionalLight{
            id: light2
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(0, 1, 0)
    
        }
    
        DirectionalLight{
            id: light3
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(-1, 0, 0)
    
        }
    
        DirectionalLight{
            id: light4
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(0, -1, 0)
    
        }
    
        DirectionalLight{
            id: light5
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(0, 0, 1)
    
        }
    
        DirectionalLight{
            id: light6
            color: "#e0eef0"
            intensity: 0.4
            enabled: true
            worldDirection: Qt.vector3d(0, 0, -1)
    
        }
    
        InputSettings { id: inputSettings }
    
        RenderSettings {
            id: rendSettings
            activeFrameGraph: RenderSurfaceSelector {
                id: surfaceSelector
                Viewport {
                    normalizedRect: Qt.rect(0, 0, 1, 1)
                    CameraSelector {
                        camera: camera
                        ClearBuffers {
                            buffers: ClearBuffers.ColorDepthBuffer
                            clearColor: "transparent"
                            FrustumCulling {
                                DepthTest {
                                    depthFunction: DepthTest.LessOrEqual
    
                                    RenderPassFilter {
                                        matchAny: []
                                    }
                                    RenderPassFilter {
                                        matchAny: []
                                        RenderTargetSelector {
                                            target: rt
                                            TextureRenderTarget {
                                                id: rt
                                                width: surfaceSelector.surface ? surfaceSelector.surface.width : 512
                                                height: surfaceSelector.surface ? surfaceSelector.surface.height : 256
                                            }
                                            RenderCapture {
                                                id: renderCapture
                                            }
                                        }
                                    }
                                }
    
                            }
                        }
                    }
                }
            }
    
        }
    
        MouseDevice {
            id: mouseDevice
        }
    
        MouseHandler {
            sourceDevice: mouseDevice
    
            property var reply
            property bool isHold: false
            property var x
            property var y
    
            onReleased: {
                if (!isHold) {
                    x = mouse.x
                    y = mouse.y
                    doRenderCapture()
                }
    
                isHold = false;
            }
    
    
            onPositionChanged: {
                if (mouse.buttons === 1){
                    isHold = true
                }
    
            }
    
            function doRenderCapture() {
                reply = renderCapture.requestCapture()
                reply.completeChanged.connect(onRenderCaptureCompleted)
            }
    
            function onRenderCaptureCompleted() {
                var color = pixelValueReader.getColor(reply, x, y)
            }
    
        }
    
        Camera {
            id: camera
            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: cubeTransform.translation
        }
    
        CustomCameraController {camera: camera}
    
        Entity {
                id: cubeEntity
    
    
                Texture2D {
                    id: cubeTexture
    
                    TextureImage {
                        source: "qrc:/texture.png"
                    }
                }
    
                Mesh {
                    id: cubeMesh
    
                    source: "qrc:/cube.obj"
                }
    
                Transform {
                    id: cubeTransform
                }
    
                NormalDiffuseMapMaterial{
                    id: cubeMaterial
                    diffuse: cubeTexture
                    normal: cubeTexture
                    specular: "black"
                    shininess: 50
                }
    
                components: [cubeMesh, cubeTransform, cubeMaterial]
    
        }
    
    }
    

    TextureRenderTarget.qml

    import Qt3D.Core 2.0
    import Qt3D.Render 2.0
    
    RenderTarget {
        id: rt
    
        property real width: 512
        property real height: 512
    
        property alias colorTexture: colorTexture
        property variant depthTexture
    
        attachments : [
            RenderTargetOutput {
                attachmentPoint: RenderTargetOutput.Color0
                texture: Texture2D {
                    id: colorTexture
                    width: rt.width
                    height: rt.height
                    format: Texture.RGBA8_UNorm
                    minificationFilter: Texture.Linear
                    magnificationFilter: Texture.Linear
                }
            },
            RenderTargetOutput {
                attachmentPoint: RenderTargetOutput.Depth
                texture : Texture2D {
                    id: depthTexture
                    width: rt.width
                    height: rt.height
                    format: Texture.D32
                    minificationFilter: Texture.Linear
                    magnificationFilter: Texture.Linear
                    comparisonFunction: Texture.CompareLessEqual
                    comparisonMode: Texture.CompareRefToTexture
                }
            }
        ]
    }
    

    My CustomCameraController don't change