Qt Quick 3D - Principled Material Example

 // Copyright (C) 2023 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

 import QtQuick
 import QtQuick.Controls
 import QtQuick.Layouts
 import QtQuick3D

 // qmllint disable missing-property
 // Disabling missing-property because the targetMaterial property
 // will either be a PrincipaledMaterial or SpecularGlossyMaterial
 // but the shared properties are not part of the common base class
 ScrollView {
     id: rootView
     required property Material targetMaterial
     ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
     width: availableWidth
     property bool specularGlossyMode: false

     ColumnLayout {
         width: rootView.availableWidth

         MarkdownLabel {
             text: `# Refraction
 The properties in this section would probably be best described as advanced
 transparency. In the previous section on transparency we discussed alpha
 blending, which is about blending colors together using the alpha channel of
 the material's color. What makes the transparency effects in this section
 different is that the goal is to handle transparency in a way that more
 physically represents how light works. To achieve this blending requires that
 all content that is blended with needs to be rendered to a texture in separate
 pass. Using any properties on this page is as expensive as rendering all opaque
 content in the scene at least twice. Once to get the background items, and again
 including the items using the refractive transparency effects. The advantage of
 this approach though is that we are not limited in how we can blend, but comes
 with the caveat that only opaque items are visible through refracted objects.`
         }

         MarkdownLabel {
             text: `## Transmission
 Transmission refers to lights ability to transmit, or pass through a surface.  Not all
 light will penetrate a surface and some will still be reflected as a specular
 reflection. This ability to transmit light only concerns the surface of a material, and
 not its depth. Without the use of further properties in this section, a material that
 has transmission alone can be assumed to be infinitely thin.
 ### Transmission Factor
 The Transmission Factor property controls the percentage of light that is transmitted by
 a materials surface.  This value is a single value between 0.0 meaning no light is
 transmitted and 1.0 meaning that 100% of the light that penetrates the surface of the
 material is transmitted through.

 Note: If you adjust the Transmission Factor to 1.0 and you still can't see
 through the models, it could be that your material is metallic. Metallic materials
 cannot transmit light.`
         }
         Button {
             text: "Reset Metalness to 0.0"
             onClicked: rootView.targetMaterial.metalness = 0.0
         }

         RowLayout {
             Label {
                 text: "Transmission Factor (" + rootView.targetMaterial.transmissionFactor.toFixed(2) + ")"
                 Layout.fillWidth: true
             }
             Slider {
                 from: 0
                 to: 1
                 value: rootView.targetMaterial.transmissionFactor
                 onValueChanged: rootView.targetMaterial.transmissionFactor = value
             }
         }
         MarkdownLabel {
             text: `### Transmission Map
 Like most other single floating point value properties, the Transmission property
 also allows for the use of a single channel of a texture to map transmission values
 to a mesh. And like many other textures, the final value of transmission will be
 the multiplication of Transmission Factor and the value sampled from Transmission Map.
 So when using a Transmission Map, it typically makes sense to set the Transmission
 Factor to 1.0.
 `
         }
         Button {
             text: "Reset Transmission"
             onClicked: rootView.targetMaterial.transmissionFactor = 1.0
         }

         ComboBox {
             id: transmissionChannelComboBox
             textRole: "text"
             valueRole: "value"
             implicitContentWidthPolicy: ComboBox.WidestText
             onActivated: rootView.targetMaterial.transmissionChannel = currentValue
             Component.onCompleted: currentIndex = indexOfValue(rootView.targetMaterial.transmissionChannel)
             model: [
                 { value: PrincipledMaterial.R, text: "Red Channel"},
                 { value: PrincipledMaterial.G, text: "Green Channel"},
                 { value: PrincipledMaterial.B, text: "Blue Channel"},
                 { value: PrincipledMaterial.A, text: "Alpha Channel"}
             ]
         }
         TextureSourceControl {
             defaultTexture: "maps/noise.png"
             defaultClearColor: "black"
             onTargetTextureChanged: {
                 rootView.targetMaterial.transmissionMap = targetTexture
             }
         }

         VerticalSectionSeparator {}

         ColumnLayout {
             visible: !rootView.specularGlossyMode
             MarkdownLabel {
                 text: `## Index of Refraction (IOR)
 The Index of Refraction or refraction index refers to the physical property of
 how fast light passes through a material. This number then is used to determine
 how light is bent or refracted when it enters a material. Since this value is
 a physical value, it's possible to plug in the same values as real life
 materials as well. The default value that the PrincipledMaterial uses for all
 lighting calculations is 1.5, which is very close to window glass. Below are
 several other materials' IOR values that will produce different results when
 used with a refractive material (especially ones with thickness).`
             }

             ComboBox {
                 id: iorChannelComboBox
                 textRole: "text"
                 valueRole: "value"
                 implicitContentWidthPolicy: ComboBox.WidestText
                 onActivated: rootView.targetMaterial.indexOfRefraction = currentValue
                 Component.onCompleted: currentIndex = 0
                 model: [
                     { value: 1.5, text: "Custom"},
                     { value: 1.4, text: "Acrylic glass"},
                     { value: 1.0, text: "Air"},
                     { value: 1.33, text: "Water"},
                     { value: 1.76, text: "Sapphire"},
                     { value: 2.42, text: "Diamond"}
                 ]
             }
             RowLayout {
                 Label {
                     text: "IOR (" + iorSlider.value.toFixed(2) + ")"
                     Layout.fillWidth: true
                 }
                 Slider {
                     id: iorSlider
                     from: 1.0
                     to: 3.0
                     value: rootView.targetMaterial.indexOfRefraction ?? 1.5
                     onValueChanged: {
                         if (iorChannelComboBox.currentValue != value)
                             iorChannelComboBox.currentIndex = 0;
                         rootView.targetMaterial.indexOfRefraction = value
                     }
                 }
             }

             VerticalSectionSeparator {}
         }

         MarkdownLabel {
             text: `## Thickness
 The Thickness properties are for giving refractive materials volume.  A
 transmissive material alone is considered to be infinitely thin so any
 Index of Refraction values will only affect the specular and fresnel
 effects of a material.  However when a transmissive material is given
 volume via the thickness properties, then light passing through the
 material is bent as it passes through.
 ### Thickness Factor
 The Thickness Factor property defines the thickness of the volume beneath
 the surface of the mesh.  Unlike other factors, the Thickness Factor
 Property is not clipped at 1.0, but rather refers to the distance in the
 coordinate space of the mesh itself.  When used in conjunction with the
 Thickness Map, the Thickness Factor would be the point of maximum thickness.
 `
         }

         RowLayout {
             Label {
                 text: "Thickness Factor (" + rootView.targetMaterial.thicknessFactor.toFixed(2) + ")"
                 Layout.fillWidth: true
             }
             Slider {
                 from: 0
                 to: 100.0
                 value: rootView.targetMaterial.thicknessFactor
                 onValueChanged: rootView.targetMaterial.thicknessFactor = value
             }
         }

         MarkdownLabel {
             text: `### Thickness Map
 The Thickness Map is a single channel (greyscale) texture that defines
 the thickness (or volume) of a mesh.  The values sampled from the
 Thickness Map are multiplied against the value of Thickness Factor to
 get the thickness of the mesh under the surface in the meshe's coordinate
 space.  Thickness Maps are baked in 3D content creation tools using ray
 tracers. The process of baking thickness is similar to the process for
 baking ambient occlusion, but the rays are cast in the opposite direction
 of the surface normal (into the mesh).  Darker values represent thin
 sections, and lighter values will be thicker.  Provided is a baked thickness
 map of the Monkey model. (The other models would have uniform thicknesses).`
         }

         ComboBox {
             id: thicknessChannelComboBox
             textRole: "text"
             valueRole: "value"
             implicitContentWidthPolicy: ComboBox.WidestText
             onActivated: rootView.targetMaterial.thicknessChannel = currentValue
             Component.onCompleted: currentIndex = indexOfValue(rootView.targetMaterial.thicknessChannel)
             model: [
                 { value: PrincipledMaterial.R, text: "Red Channel"},
                 { value: PrincipledMaterial.G, text: "Green Channel"},
                 { value: PrincipledMaterial.B, text: "Blue Channel"},
                 { value: PrincipledMaterial.A, text: "Alpha Channel"}
             ]
         }
         TextureSourceControl {
             defaultTexture: "maps/monkey_thickness.jpg"
             defaultClearColor: "black"
             onTargetTextureChanged: {
                 rootView.targetMaterial.thicknessMap = targetTexture
             }
         }

         VerticalSectionSeparator {}

         MarkdownLabel {
             text: `## Attenuation
 As light passes through a volume it will be subject to absorption and scattering.
 To simulate this interaction, two properties are provided for determining this
 attenuation.
 ### Attenuation Color
 The Attenuation Color property refers to the color that white light turns into
 due to the absorption when reaching the attenuation distance.
 `
         }
         RowLayout {
             Label {
                 text: "Red (" + rootView.targetMaterial.attenuationColor.r.toFixed(2) + ")"
                 Layout.fillWidth: true
             }
             Slider {
                 from: 0
                 to: 1
                 value: rootView.targetMaterial.attenuationColor.r
                 onValueChanged: rootView.targetMaterial.attenuationColor.r = value
             }
         }
         RowLayout {
             Label {
                 text: "Green  (" + rootView.targetMaterial.attenuationColor.g.toFixed(2) + ")"
                 Layout.fillWidth: true
             }

             Slider {
                 from: 0
                 to: 1
                 value: rootView.targetMaterial.attenuationColor.g
                 onValueChanged: rootView.targetMaterial.attenuationColor.g = value
             }
         }
         RowLayout {
             Label {
                 text: "Blue (" + rootView.targetMaterial.attenuationColor.b.toFixed(2) + ")"
                 Layout.fillWidth: true
             }

             Slider {
                 from: 0
                 to: 1
                 value: rootView.targetMaterial.attenuationColor.b
                 onValueChanged: rootView.targetMaterial.attenuationColor.b = value
             }
         }
         MarkdownLabel {
             text: `### Attenuation Distance
 Attenuation Distance defines material density, but does so by describing the
 average distance light must travel through the medium before interacting with a
 particle (absorption). In this case the distance is specified in world
 coordinate space (scene space). This distance can be any positive floating point
 value. This means the attenuation color will start to appear when the thickness
 is greater than the attenuation distance, with the caveat that the Attenuation
 Color assumes white light is passing through the model, so any other light will
 create a blended result. For this demonstration the slider value is limited to
 100, which should be the maximum thickness for all 3 models.`
         }

         RowLayout {
             Label {
                 text: "Attenuation Distance (" + rootView.targetMaterial.attenuationDistance.toFixed(2) + ")"
                 Layout.fillWidth: true
             }
             Slider {
                 from: 0
                 to: 100
                 value: rootView.targetMaterial.attenuationDistance
                 onValueChanged:  {
                     if (value != rootView.targetMaterial.attenuationDistance)
                         rootView.targetMaterial.attenuationDistance = value
                 }
             }
         }
     }
 }
 // qmllint enable missing-property