CustomMaterial QML Type | Qt Quick 3D (original) (raw)

Base component for creating custom materials used to shade models. More...

Import Statement: import QtQuick3D
Inherits: Material

Properties

Detailed Description

The custom material allows using custom shader code for a material, enabling programmability on graphics shader level. A vertex, fragment, or both shaders can be provided. The vertexShader and fragmentShader properties are URLs, referencing files containing shader snippets, and work very similarly to ShaderEffect or Image.source. Only the file and qrc schemes are supported with custom materials. It is also possible to omit the file scheme, allowing to specify a relative path in a convenient way. Such a path is resolved relative to the component's (the .qml file's) location.

For a getting started guide to custom materials, see the page Programmable Materials, Effects, Geometry, and Texture data.

Introduction

Consider the following versions of the same scene. On the left, the cylinder is using a built-in, non-programmable material. Such materials are configurable through a wide range of properties, but there is no further control given over the shaders that are generated under the hood. On the right, the same cylinder is now associated with a CustomMaterial referencing application-provided vertex and fragment shader snippets. This allows inserting custom, application-specific logic into the vertex shader to transform the geometry, and to determine certain color properties in a custom manner in the fragment shader. As this is a shaded custom material, the cylinder still participates in the scene lighting normally.

View3D { anchors.fill: parent PerspectiveCamera { id: camera position: Qt.vector3d(0, 0, 600) } camera: camera DirectionalLight { position: Qt.vector3d(-500, 500, -100) color: Qt.rgba(0.2, 0.2, 0.2, 1.0) ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0) } Model { source: "#Cylinder" eulerRotation: Qt.vector3d(30, 30, 0) scale: Qt.vector3d(1.5, 1.5, 1.5) materials: [ DefaultMaterial { diffuseColor: Qt.rgba(0, 1, 0, 1) } ] } } View3D { anchors.fill: parent PerspectiveCamera { id: camera position: Qt.vector3d(0, 0, 600) } camera: camera DirectionalLight { position: Qt.vector3d(-500, 500, -100) color: Qt.rgba(0.2, 0.2, 0.2, 1.0) ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0) } Model { source: "#Cylinder" eulerRotation: Qt.vector3d(30, 30, 0) scale: Qt.vector3d(1.5, 1.5, 1.5) materials: [ CustomMaterial { vertexShader: "material.vert" fragmentShader: "material.frag" property real uTime property real uAmplitude: 50 NumberAnimation on uTime { from: 0; to: 100; duration: 10000; loops: -1 } } ] } }

Let's assume that the shader snippets in material.vert and material.frag are the following:

void MAIN() { VERTEX.x += sin(uTime + VERTEX.y) * uAmplitude; } void MAIN() { BASE_COLOR = vec4(0.0, 1.0, 0.0, 1.0); }

Notice how uTime and uAmplitude are properties of the CustomMaterial element. They can change values and get animated normally, the values will be exposed to the shaders automatically without any further action from the developer.

The result is a cylinder that animates its vertices:

Two flavors of custom materials

There are two main types of custom materials. This is specified by the shadingMode property. In unshaded custom materials the fragment shader outputs a single vec4 color, ignoring lights, light probes, shadowing in the scene. In shaded materials the shader is expected to implement certain functions and work with built-in variables to take lighting and shadow contribution into account.

The default choice is typically a shaded material, this is reflected in the default value of the shadingMode property. This fits materials that needs to transform vertices or other incoming data from the geometry, or determine values like BASE_COLOR or EMISSIVE_COLOR in a custom manner, perhaps by sampling SCREEN_TEXTURE or DEPTH_TEXTURE, while still reciving light and shadow contributions from the scene. Additionally, such materials can also override and reimplement the equations used to calculate the contributions from directional, point, and other lights. The application-provided shader snippets are heavily amended by the Qt Quick 3D engine under the hood, in order to provide the features, such as lighting, the standard materials have.

Unshaded materials are useful when the object's appearance is determined completely by the custom shader code. The shaders for such materials receive minimal additions by the engine, and therefore it is completely up to the shader to determine the final fragment color. This gives more freedom, but also limits possiblities to integrate with other elements of the scene, such as lights.

Note: Shader code is always provided using Vulkan-style GLSL, regardless of the graphics API used by Qt at run time.

Note: The vertex and fragment shader code provided by the material are not full, complete GLSL shaders on their own. Rather, they provide a set of functions, which are then amended with further shader code by the engine.

Exposing data to the shaders

The dynamic properties of the CustomMaterial can be changed and animated using QML and Qt Quick facilities, and the values are exposed to the shaders automatically. This in practice is very similar ShaderEffect. The following list shows how properties are mapped:

Note: When a uniform referenced in the shader code does not have a corresponding property, it will cause a shader compilation error when processing the material at run time. There are some exceptions to this, such as, sampler uniforms, that get a dummy texture bound when no corresponding QML property is present, but as a general rule, all uniforms and samplers must have a corresponding property declared in the CustomMaterial object.

Unshaded custom materials

The following is an example of an unshaded custom material.

CustomMaterial { // These properties are automatically exposed to the shaders property real time: 0.0 property real amplitude: 5.0 property real alpha: 1.0 property TextureInput tex: TextureInput { enabled: true texture: Texture { source: "image.png" } }

shadingMode: CustomMaterial.Unshaded
sourceBlend: alpha < 1.0 ? CustomMaterial.SrcAlpha : CustomMaterial.NoBlend
destinationBlend: alpha < 1.0 ? CustomMaterial.OneMinusSrcAlpha : CustomMaterial.NoBlend
cullMode: CustomMaterial.BackFaceCulling

vertexShader: "customshader.vert"
fragmentShader: "customshader.frag"

}

With the above example, the unshaded vertex and fragment shaders snippets could look like the following. Note how the shaders do not, and must not, declare uniforms or vertex inputs as that is taken care of by Qt when assembling the final shader code.

VARYING vec3 pos; VARYING vec2 texcoord;

void MAIN() { pos = VERTEX; pos.x += sin(time * 4.0 + pos.y) * amplitude; texcoord = UV0; POSITION = MODELVIEWPROJECTION_MATRIX * vec4(pos, 1.0); }

VARYING vec3 pos; VARYING vec2 texcoord;

void MAIN() { vec4 c = texture(tex, texcoord); FRAGCOLOR = vec4(pos.x * 0.02, pos.y * 0.02, pos.z * 0.02, alpha) * c; }

The following special, uppercase keywords are available:

Shaded custom materials

A shaded material augments the shader code that would be generated by a PrincipledMaterial. Unlike unshaded materials, that provide almost all logic for the vertex and fragment shader main functions on their own, preventing adding generated code for lighting, shadowing, global illumination, etc., shaded materials let shader generation happen normally, as if the CustomMaterial was a PrincipledMaterial. The vertex and fragment shader snippets are expected to provide optional functions that are then invoked at certain points, giving them the possibility to customize the colors and other values that are then used for calculating lighting and the final fragment color.

Rather than implementing just a MAIN function, the fragment shader for a shaded custom material can implement multiple functions. All functions, including MAIN, are optional to implement in shaded custom materials. An empty shader snippet, or, even, not specifying the vertexShader or fragmentShader properties at all can be perfectly valid too.

Vertex shader snippets in a shaded custom material

The following functions can be implemented in a vertex shader snippet:

Note: To pass data without interpolation from the vertex to the fragment stage, add the flat keyword before the type in the VARYING declarations.

Fragment shader snippets in a shaded custom material

The following functions can be implemented in a fragment shader snippet:

}

Custom variables between functions

Additional variables can be delivered from the MAIN function to the others. The SHARED_VARS keyword can be used for defining new custom variables. These user-defined variables can be accessed with SHARED..

For example, a shaded custom material can fetch a shared value in the MAIN and use it in other functions.

SHARED_VARS { vec3 colorThreshold; }; void MAIN() { BASE_COLOR = texture(baseColorMap, UV0); SHARED.colorThreshold = texture(thresholdMap, UV0).rgb; } void DIRECTIONAL_LIGHT() { if (DIFFUSE >= SHARED.colorThreshold) { DIFFUSE = SHARED.colorThreshold; return; } DIFFUSE += LIGHT_COLOR * SHADOW_CONTRIB; }

Note: SHARED can be written on all the functions without POST_PROCESS but it is safe to write it on MAIN and read on the other functions.

Note: A recommended use case to write SHARED on LIGHT functions is reseting it on MAIN first and then accumulating it on each LIGHT functions.

SHARED_VARS { float sheenIntensity; float sheenRoughness; vec3 sheenColor; vec3 outSheenColor; }; void MAIN() { ... vec4 tex = texture(uSheenMap, UV0); SHARED.sheenColor = tex.rgb; SHARED.sheenIntensity = tex.a; SHARED.sheenRoughness = uSheenRoughness; SHARED.outSheenColor = vec3(0.0); } void SPECULAR_LIGHT() { SHARED.outSheenColor += ...; } void POST_PROCESS() { COLOR_SUM = DIFFUSE + SPECULAR + EMISSIVE + SHARED.outSheenColor; }

Note: MAIN is called before others, and POST_PROCESS after all others, but that there is no guarantee for any other ordering for light processors.

Additional special keywords

The custom fragment shader code can freely access uniforms (such as, CAMERA_DIRECTION or CAMERA_POSITION), and varyings passed on from the custom vertex shader. Additionally, there are a number of built-in varyings available as special keywords. Some of these are optional in the sense that a vertex MAIN could calculate and pass on these on its own, but to reduce duplicated data fragment shaders can also rely on these built-ins instead. These built-ins are available in light processor functions and in the fragment MAIN.

Instancing

When doing instanced rendering, some of the keywords above do not apply. The following keywords are only available with instancing:

Screen, depth, and other textures

The rendering pipeline can expose a number of textures to the custom material shaders with content from special render passes. This applies both to shaded and unshaded custom materials.

For example, a shader may want access to a depth texture that contains the depth buffer contents for the opaque objects in the scene. This is achieved by sampling DEPTH_TEXTURE. Such a texture is not normally generated, unless there is a real need for it. Therefore, the presence of the following keywords in the vertex or fragment shader also acts as a toggle for opting in to the - potentially expensive - passes for generating the texture in question. (of course, it could be that some of these become already enabled due to other settings, such as the ambient occlusion parameters in SceneEnvironment or due to a post-processing effect relying on the depth texture, in which case the textures in question are generated regardless of the custom material and so sampling these special textures in the material comes at no extra cost apart from the texture access itself)

#if QSHADER_VIEW_COUNT >= 2
vec4 c = texture(SCREEN_TEXTURE, vec3(uv, VIEW_INDEX));
#else
vec4 c = texture(SCREEN_TEXTURE, uv);
#endif

#if QSHADER_VIEW_COUNT >= 2
vec4 depthSample = texture(DEPTH_TEXTURE, vec3(uv, VIEW_INDEX));
#else
vec4 depthSample = texture(DEPTH_TEXTURE, uv);
#endif

#if QSHADER_VIEW_COUNT >= 2
ivec2 aoSize = textureSize(AO_TEXTURE, 0).xy;
vec2 aoUV = (FRAGCOORD.xy) / vec2(aoSize);
float aoFactor = texture(AO_TEXTURE, vec3(aoUV, VIEW_INDEX)).x;
#else
ivec2 aoSize = textureSize(AO_TEXTURE, 0);
vec2 aoUV = (FRAGCOORD.xy) / vec2(aoSize);
float aoFactor = texture(AO_TEXTURE, aoUV).x;
#endif

See also SceneEnvironment::tonemapMode, Using Image-Based Lighting, Qt Quick 3D - Custom Shaders Example, Qt Quick 3D - Custom Materials Example, and Programmable Materials, Effects, Geometry, and Texture data.

Property Documentation

Specifies that the material state is always dirty, which indicates that the material needs to be refreshed every time it is used by the QtQuick3D.

destinationAlphaBlend : enumeration [since 6.7]

Specifies the destination alpha blend factor. The default value is CustomMaterial.NoBlend. This value is only actively used if sourceBlend and destinationBlend is set to a non-default value.

Constant Value
CustomMaterial.NoBlend
CustomMaterial.Zero
CustomMaterial.One
CustomMaterial.SrcColor
CustomMaterial.OneMinusSrcColor
CustomMaterial.DstColor
CustomMaterial.OneMinusDstColor
CustomMaterial.SrcAlpha
CustomMaterial.OneMinusSrcAlpha
CustomMaterial.DstAlpha
CustomMaterial.OneMinusDstAlpha
CustomMaterial.ConstantColor
CustomMaterial.OneMinusConstantColor
CustomMaterial.ConstantAlpha
CustomMaterial.OneMinusConstantAlpha
CustomMaterial.SrcAlphaSaturate

Note: For backwards compatibility purposes, when left to its default value, will be assigned the same value as destinationBlend when sourceBlend and destinationBlend is set to non-default values.

This property was introduced in Qt 6.7.

See also destinationBlend.

destinationBlend : enumeration

Specifies the destination blend factor. The default value is CustomMaterial.NoBlend.

Constant Value
CustomMaterial.NoBlend
CustomMaterial.Zero
CustomMaterial.One
CustomMaterial.SrcColor
CustomMaterial.OneMinusSrcColor
CustomMaterial.DstColor
CustomMaterial.OneMinusDstColor
CustomMaterial.SrcAlpha
CustomMaterial.OneMinusSrcAlpha
CustomMaterial.DstAlpha
CustomMaterial.OneMinusDstAlpha
CustomMaterial.ConstantColor
CustomMaterial.OneMinusConstantColor
CustomMaterial.ConstantAlpha
CustomMaterial.OneMinusConstantAlpha
CustomMaterial.SrcAlphaSaturate

Note: Both sourceBlend and destinationBlend needs to be set to a non-default value before blending is enabled.

See also sourceBlend.

Specfies the file with the snippet of custom fragment shader code.

The value is a URL and must either be a local file or use the qrc scheme to access files embedded via the Qt resource system. Relative file paths (without a scheme) are also accepted, in which case the file is treated as relative to the component (the .qml file).

Warning: Shader snippets are assumed to be trusted content. Application developers are advised to carefully consider the potential implications before allowing the loading of user-provided content that is not part of the application.

See also vertexShader.

This property determines the width of the lines rendered, when the geometry is using a primitive type of lines or line strips. The default value is 1.0. This property is not relevant when rendering other types of geometry, such as, triangle meshes.

Warning: Line widths other than 1 may not be suported at run time, depending on the underlying graphics API. When that is the case, the request to change the width is ignored. For example, none of the following can be expected to support wide lines: Direct3D, Metal, OpenGL with core profile contexts.

Note: Unlike the line width, the value of which is part of the graphics pipeline object, the point size for geometries with a topology of points is controlled by the vertex shader (when supported), and has therefore no corresponding QML property.

shadingMode : enumeration

Specifies the type of the material. The default value is Shaded.

Constant Value
CustomMaterial.Unshaded
CustomMaterial.Shaded

sourceAlphaBlend : enumeration [since 6.7]

Specifies the source alpha blend factor. The default value is CustomMaterial.NoBlend. This value is only actively used if sourceBlend and destinationBlend is set to a non-default value.

Constant Value
CustomMaterial.NoBlend
CustomMaterial.Zero
CustomMaterial.One
CustomMaterial.SrcColor
CustomMaterial.OneMinusSrcColor
CustomMaterial.DstColor
CustomMaterial.OneMinusDstColor
CustomMaterial.SrcAlpha
CustomMaterial.OneMinusSrcAlpha
CustomMaterial.DstAlpha
CustomMaterial.OneMinusDstAlpha
CustomMaterial.ConstantColor
CustomMaterial.OneMinusConstantColor
CustomMaterial.ConstantAlpha
CustomMaterial.OneMinusConstantAlpha
CustomMaterial.SrcAlphaSaturate

Note: For backwards compatibility purposes, when left to its default value, will be assigned the same value as sourceBlend when sourceBlend and destinationBlend is set to non-default values.

This property was introduced in Qt 6.7.

See also sourceBlend.

sourceBlend : enumeration

Specifies the source blend factor. The default value is CustomMaterial.NoBlend.

Constant Value
CustomMaterial.NoBlend
CustomMaterial.Zero
CustomMaterial.One
CustomMaterial.SrcColor
CustomMaterial.OneMinusSrcColor
CustomMaterial.DstColor
CustomMaterial.OneMinusDstColor
CustomMaterial.SrcAlpha
CustomMaterial.OneMinusSrcAlpha
CustomMaterial.DstAlpha
CustomMaterial.OneMinusDstAlpha
CustomMaterial.ConstantColor
CustomMaterial.OneMinusConstantColor
CustomMaterial.ConstantAlpha
CustomMaterial.OneMinusConstantAlpha
CustomMaterial.SrcAlphaSaturate

Note: Both sourceBlend and destinationBlend needs to be set to a non-default value before blending is enabled.

See also destinationBlend.

Specfies the file with the snippet of custom vertex shader code.

The value is a URL and must either be a local file or use the qrc scheme to access files embedded via the Qt resource system. Relative file paths (without a scheme) are also accepted, in which case the file is treated as relative to the component (the .qml file).

Warning: Shader snippets are assumed to be trusted content. Application developers are advised to carefully consider the potential implications before allowing the loading of user-provided content that is not part of the application.

See also fragmentShader.

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.