Qt Quick 3D - Exemple de matériaux personnalisés
Illustre la création de matériaux personnalisés ombrés.

Cet exemple montre comment créer des matériaux ombrés custom materials. Avec les matériaux ombrés, il n’est pas nécessaire d’écrire des programmes de shaders complets. À la place, on écrit des fonctions qui modifient les shaders standard de Qt. De cette manière, le matériau obtenu participera par défaut à l’éclairage, au mappage des ombres et sera compatible avec les sondes de lumière. Il suffit d’écrire une logique personnalisée uniquement pour les cas où l’on souhaite un comportement particulier. Pour ce faire, on enrichit efficacement le code de shader qui serait généré pour un « PrincipledMaterial » avec nos propres fonctions personnalisées, appelées à certaines étapes des shaders de sommets et de fragments.
Pour créer un matériau personnalisé avec ombrage, définissez la propriété « shadingMode » sur « CustomMaterial.Shaded ».
Un matériau simple
Le premier modèle utilise un matériau simple qui n’ajoute aucune logique personnalisée. Nous appliquons le matériau personnalisé au modèle comme n’importe quel autre matériau :
Model {
source: "weirdShape.mesh"
scale: Qt.vector3d(100, 100, 100)
rotation: Quaternion.fromEulerAngles(-90, 0, 0)
x: v3d.radius
materials: [
CustomMaterial {
shadingMode: CustomMaterial.Shaded
fragmentShader: "material_simple.frag"
property color uDiffuse: "fuchsia"
property real uSpecular: 1.0
}
]
}En plus de définir les propriétés « shadingMode » et « fragmentShader », nous ajoutons également deux propriétés au matériau : « uDiffuse » et « Specular ». Celles-ci seront prises en compte par le shader de fragment.
Le code du shader de fragment est court :
void MAIN()
{
SPECULAR_AMOUNT = uSpecular;
BASE_COLOR = uDiffuse;
}Tous les shaders doivent implémenter la fonction ` MAIN `. Dans celui-ci, nous utilisons les propriétés définies dans les matériaux pour définir des valeurs qui seront utilisées par le code de shader standard de Qt. Notez que nous n’avons pas besoin de les déclarer comme des uniformes : il suffit simplement de s’assurer que les noms correspondent. Nous obtiendrions une erreur de compilation du shader si le matériau ne disposait pas de propriétés correspondantes.
Les variables spéciales SPECULAR_AMOUNT et BASE_COLOR correspondent respectivement à specularAmount et baseColor de PrincipledMaterial. Celles-ci sont ensuite utilisées par le code de shader standard pour effectuer les calculs d’éclairage, exactement comme si nous avions utilisé un PrincipledMaterial.
Gestion personnalisée des lumières
L'objet suivant utilise un matériau plus complexe qui implémente un éclairage personnalisé. Le matériau comporte des noms d'uniformes différents, mais pour le reste, nous l'utilisons de la même manière :
materials: [
CustomMaterial {
shadingMode: CustomMaterial.Shaded
fragmentShader: "material_customlights.frag"
property color uDiffuse: "orange"
property real uShininess: 150
}
]Le shader de fragment implémente une logique personnalisée pour tous les différents types de lumière :
void MAIN()
{
SPECULAR_AMOUNT = 1.0;
ROUGHNESS = 0.5;
BASE_COLOR = uDiffuse;
}
void AMBIENT_LIGHT()
{
DIFFUSE += uDiffuse.rgb * TOTAL_AMBIENT_COLOR;
}
void DIRECTIONAL_LIGHT()
{
DIFFUSE += uDiffuse.rgb * LIGHT_COLOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(NORMAL), TO_LIGHT_DIR)));
}
void POINT_LIGHT()
{
DIFFUSE += uDiffuse.rgb * LIGHT_COLOR * LIGHT_ATTENUATION * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(NORMAL), TO_LIGHT_DIR)));
}
void SPOT_LIGHT()
{
DIFFUSE += uDiffuse.rgb * LIGHT_COLOR * LIGHT_ATTENUATION * SPOT_FACTOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
}Nous utilisons ici de nombreux nouveaux mots-clés spéciaux faisant référence aux propriétés des différents types de lumières. Consultez la documentation CustomMaterial pour obtenir une description de chaque mot-clé. Notez que chaque type de lumière dispose de sa propre fonction. Toute fonction non implémentée utilisera l’implémentation par défaut, se comportant comme PrincipledMaterial. Par exemple : dans ce shader, nous n’avons pas implémenté SPECULAR_LIGHT(), nous obtiendrons donc la réflexion spéculaire intégrée.
Ajout d’un shader de sommets
Un matériau personnalisé peut également utiliser un shader de sommets pour modifier la géométrie du modèle. Ici, nous spécifions à la fois le shader de fragments et le shader de sommets, et nous ajoutons plusieurs propriétés supplémentaires qui seront récupérées en tant que valeurs uniformes :
materials: [
CustomMaterial {
id: material
shadingMode: CustomMaterial.Shaded
vertexShader: "material_distortion.vert"
fragmentShader: "material_customlights.frag"
property real uTime: 0.0
property real uAmplitude: 0.3
property color uDiffuse: "yellow"
property real uShininess: 50
NumberAnimation {
target: material
property: "uTime"
from: 0.0
to: 31.4
duration: 10000
loops: Animation.Infinite
running: true
}
}
]Le shader de sommet est très court :
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
void MAIN()
{
VERTEX.y += sin(uTime + VERTEX.x*10.0) * uAmplitude;
}Cela déforme le modèle en décalant chaque sommet selon une onde sinusoïdale qui varie dans le temps.
Un matériau transparent
Enfin, nous ajoutons une sphère dotée d’un matériau transparent. Pour des raisons de performances, Qt n’implémente pas la transparence de manière totalement réaliste. À la place, Qt rend tous les objets opaques de la scène dans une texture, puis les matériaux transparents sont lus à partir de cette texture. Cela signifie que les matériaux transparents donneront le meilleur résultat lorsqu’ils sont placés devant d’autres objets :
Model {
id: screenSphere
source: "#Sphere"
scale: Qt.vector3d(0.75, 0.75, 0.75)
y: 60
z: 750;
materials: [
CustomMaterial {
shadingMode: CustomMaterial.Shaded
fragmentShader: "material_transparent.frag"
}
]Pour cet exemple, nous implémentons une fonction de distorsion simplifiée qui ne cherche pas à reproduire une véritable réfraction physique :
void MAIN()
{
vec2 size = vec2(textureSize(SCREEN_TEXTURE, 0));
vec2 uv = FRAGCOORD.xy / size;
vec3 view = normalize(VIEW_VECTOR);
vec3 projection = view - view * normalize(NORMAL);
vec3 refraction = projection * projection;
uv += refraction.xy * 0.5;
vec3 col = texture(SCREEN_TEXTURE, uv).rgb;
col = col * 0.8 + vec3(0.2);
BASE_COLOR = vec4(col, 1.0);
}SCREEN_TEXTURE fait référence à la texture représentant tous les objets opaques de la scène. Nous calculons d’abord les coordonnées UV à l’intérieur de cette texture qui correspondent à la position à l’écran du sommet actuel. Nous ajoutons ensuite un décalage à cette position, simulant ainsi un effet de réfraction, avant d’effectuer une recherche dans la texture.
Enfin, nous ajoutons 20 % de blanc pour obtenir un léger effet de voile. Notez que le résultat est attribué à ` BASE_COLOR`, de sorte que Qt y ajoutera un éclairage. C’est pourquoi nous pouvons voir des reflets à la surface de la sphère.
Matériaux non ombrés
Il est également possible de créer des matériaux personnalisés utilisant des programmes de shader complets (tout en continuant à utiliser les mots-clés de commodité). L’exemple `customshaders` illustre l’autre catégorie de matériaux personnalisés : les matériaux personnalisés non ombrés.
© 2026 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.