1

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

  • I up the topic, I need help please – Etienne PERIANA Aug 17 '21 at 06:11
  • The colors on the cube seem to depend on the x, y and z coordinate. So why don't you just use the clicked coordinates you are already retrieveing in your code example? – Florian Blume Aug 18 '21 at 12:51
  • Thank you for you answer. I don't use this clicked coordinates because sometimes they are negative and I don't understand this values – Etienne PERIANA Aug 18 '21 at 13:48
  • They are negative because your model has negative xyz coordinates, what's not to understand? The coordinates don't represent the colors but what location you clicked on the model. What you can do is render your model in a second pass to an offscreen texture without any lightning but with the texture. Then, when the user clicks, you get the rendering using a `QRenderCapture` and check the color. I implemented instanced picking via colors [here](https://github.com/florianblume/qt3d-instanced-picking) which should help you get started. – Florian Blume Aug 19 '21 at 06:49
  • Hi! I am not at all familiar with 3d and I have no idea how to use your example in my case as I use a pattern for my cube. Would it be possible to do an interview or a call to discuss it? – Etienne PERIANA Aug 19 '21 at 07:11
  • Sorry I don't have time for an interview. Look at my code and how I write out the 3D coordinates to the shader. Instead of the 3D coordinates, you would want to write the texture to the pixel values. This way, you can retrieve them using the render capture. – Florian Blume Aug 19 '21 at 07:51
  • Hi @FlorianBlume, I use your code and everything works good thank you so much for your help – Etienne PERIANA Aug 20 '21 at 06:33
  • I'm glad I could help. Could you post an answer to your question? In case someone else stumbles across it :) – Florian Blume Aug 20 '21 at 06:39
  • Yes I will do it :) – Etienne PERIANA Aug 20 '21 at 11:49
  • Sorry to disturb you again but did you know how to fix cube rotations with a one finger gesture ? – Etienne PERIANA Aug 20 '21 at 11:59

1 Answers1

1

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