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"
}
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
QLayer
s and add one to each and two respectiveQLayerFilter
s 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.