Qt3D Different Material for different RenderPasses

324 Views Asked by At

I have a QML Scene3D with 2 viewports, left and right half of window. The scene contains two cameras (one per viewport) and a torus mesh. I want to use a viewport-specific material for the same torus, i.e. different QMaterials (QEffects, QShaderPrograms, ...) when rendering in the left and right viewport respectively.

// main.qml

Entity {
    id: rootEntity

    components: [
        MyFrameGraph {
            camLeft: cameraLeft
            camRight: cameraRight
        },
        InputSettings { }
    ]

    Camera {
        id: cameraLeft
        projectionType: CameraLens.PerspectiveProjection
        position: Qt.vector3d( 0.0, 0.0, -200.0 )
        upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
        viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
    }

    Camera {
        id: cameraRight
        projectionType: CameraLens.PerspectiveProjection
        position: Qt.vector3d( 0.0, 200.0, 0.0 )
        upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
        viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
    }

    TorusMesh {
        id: torusMesh
        radius: 20
        minorRadius: 5
    }

    Transform {
        id:torusTransform
    }

    Material{
        id: myTorusMaterial
        effect: MyEffect{}
    }

    Entity {
        id: torus
        components: [torusMesh,torusTransform,myTorusMaterial]
    }
}

My FrameGraph contains two branches for the two viewports, whereas the leaf nodes are RenderPassFilters.

//MyFrameGraph.qml

RenderSettings {
    id: root

    property Camera camLeft
    property Camera camRight

    activeFrameGraph: RenderSurfaceSelector {
        id: surfaceSelector

        Viewport {
            id: mainViewport
            normalizedRect: Qt.rect(0.0, 0.0, 1.0, 1.0)

            ClearBuffers {
                buffers: ClearBuffers.ColorDepthBuffer
                clearColor: "black"
            }

            Viewport {
                normalizedRect: Qt.rect(0.0, 0.0, 0.5, 1)
                CameraSelector {
                    camera: root.camLeft
                    RenderStateSet {
                        renderStates: [
                            DepthTest {depthFunction: DepthTest.Less}
                        ]
                        RenderPassFilter {
                            matchAny: [FilterKey { name: "pass"; value: "myMaterialLeft" }]
                        }
                    }
                }
            }
            Viewport {
                normalizedRect: Qt.rect(0.5, 0.0, 0.5, 1)
                CameraSelector {
                    camera: root.camRight
                    RenderStateSet {
                        renderStates: [
                            DepthTest {depthFunction: DepthTest.Less}
                        ]

                        RenderPassFilter {
                            matchAny: [FilterKey { name: "pass"; value: "myMaterialRight" }]
                        }
                    }
                }
            }
        }
    }
}

In my custom Material/Effects I use FilterKeys in the RenderPass object.

//MyEffect.qml

Effect {
    id: root

    property color maincolor: Qt.rgba(0.0, 1.0, 0.0, 1.0)

    ShaderProgram {
        id: myShaderLeft
        vertexShaderCode: loadSource("qrc:/shader/myShaderLeft.vert")
        fragmentShaderCode: loadSource("qrc:/shader/myShaderLeft.frag")
    }
    ShaderProgram {
        id: myShaderRight
        vertexShaderCode: loadSource("qrc:/shader/myShaderRight.vert")
        fragmentShaderCode: loadSource("qrc:/shader/myShaderRight.frag")
    }

    parameters: [
        Parameter {
             name: "maincolor"
             value: Qt.vector3d(root.maincolor.r, root.maincolor.g, root.maincolor.b)
         }
    ]

    techniques: [
        Technique {
            filterKeys: [FilterKey { name: "renderingStyle"; value: "forward" }]

            graphicsApiFilter {
                api: GraphicsApiFilter.OpenGL
                profile: GraphicsApiFilter.CoreProfile
                majorVersion: 3
                minorVersion: 1
            }
            RenderPass {
                id:renderPassLeft
                filterKeys: [ FilterKey { name: "pass"; value: "myMaterialLeft" } ]

                shaderProgram: myShaderLeft
                renderStates: [CullFace { mode: CullFace.NoCulling }]
            }
            RenderPass {
                id: renderPassRight
                filterKeys: [ FilterKey { name: "pass"; value: "myMaterialRight" } ]

                shaderProgram: myShaderRight
                renderStates: [CullFace { mode: CullFace.NoCulling }]
            }
            renderPasses:[renderPassLeft,renderPassRight]
        }
    ]
}

This works as expected, the object is rendered with myShaderLeft in the left viewport and with myShaderRight in the right viewport.

Now I want to use an default Qt-material (QMetalRoughMaterial) when rendering in the left viewport, and my custom material/effect when rendering in the right viewport. How can I achieve that?

An idea was to access the ShaderProgram of the MetalRoughMaterial through metalRougMaterial.effect.techniques[0].renderPasses[0].shaderProgram and use it one of the RenderPasses of my custom Material/Effect. However, metalRougMaterial.effect.techniques returns an undefined object.

MetalRoughMaterial {
    id: metalRoughMaterial
    baseColor: "red"
}
1

There are 1 best solutions below

0
On

From the source code, it looks like effect is not a property that you can access on the material.

I'm not sure if this will work but I guess you could subclass the material in C++ and make the effect accessible. This way, you could modify it in QML, once you've registered it as a new QML type and used this QML type instead of the normal material one.

Another option that I can think of would be to create the object twice, once of the right viewport and once for the left. Then create two QLayers and add one to each and two respective QLayerFilters for each framegraph branch, to which you also add one layer each.

I gues computationally this comes down to the same load but you'd have to think about how setting position etc. works (I think you can add the same QTransform to both, but I'm not sure).

Last but not least you could probably recreate the whole material in QML completely (with your filter key) but that's probably a lot of work.