Qt Quick 3D - Exemple de matériaux personnalisés
Démonstration de l'écriture de matériaux personnalisés ombrés.

Cet exemple montre comment écrire des matériaux ombrés custom materials. Avec les matériaux ombrés, nous n'avons pas besoin d'écrire des programmes de shaders complets. Au lieu de cela, nous écrivons des fonctions qui modifient les shaders standards de Qt. De cette façon, le matériau résultant participera par défaut à l'éclairage, au mappage des ombres et sera compatible avec les sondes lumineuses. Nous n'avons qu'à écrire une logique personnalisée pour les cas où nous voulons un comportement spécial. Pour ce faire, nous augmentons le code du shader qui serait généré pour PrincipledMaterial avec nos propres fonctions personnalisées qui sont appelées à certaines étapes dans les shaders de sommets et de fragments.
Pour créer un matériau personnalisé ombré, réglez 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 définissons le matériau personnalisé sur le 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. Ces propriétés seront prises en compte par le fragment shader.
Le code du fragment shader est court :
void MAIN()
{
SPECULAR_AMOUNT = uSpecular;
BASE_COLOR = uDiffuse;
}Tous les shaders doivent implémenter la fonction MAIN. Dans celle-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 standard du shader de Qt. Notez que nous n'avons pas besoin de les déclarer en tant qu'uniformes : tout ce que nous avons à faire est de nous assurer que les noms correspondent. Nous obtiendrions une erreur de compilation du shader si le matériau n'avait pas les propriétés correspondantes.
Les variables spéciales SPECULAR_AMOUNT et BASE_COLOR correspondent à specularAmount et baseColor de PrincipledMaterial. Elles sont ensuite utilisées par le code shader standard pour effectuer les calculs d'éclairage, comme si nous avions utilisé PrincipledMaterial.
Gestion personnalisée des lumières
L'objet suivant utilise un matériau plus complexe qui met en œuvre un éclairage personnalisé. Le matériau a des noms d'uniformes différents, mais 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 fragment shader 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ère. Voir la documentation de CustomMaterial pour une description de chaque mot-clé. Notez que chaque type de lumière a sa propre fonction. Toute fonction non implémentée utilisera l'implémentation par défaut, se comportant comme PrincipledMaterial. Par exemple : dans ce nuanceur, nous n'avons pas implémenté SPECULAR_LIGHT(), nous obtiendrons donc la réflexion spéculaire intégrée.
Ajout d'un nuanceur de vertex
Un matériau personnalisé peut également utiliser un nuanceur de sommets pour modifier la géométrie du modèle. Ici, nous spécifions à la fois le fragment shader et le vertex shader, et nous ajoutons plusieurs autres propriétés qui seront prises en compte 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 nuanceur de sommets 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;
}Il déforme le modèle en déplaçant chaque sommet selon une onde sinusoïdale qui change avec le temps.
Un matériau transparent
Enfin, nous ajoutons une sphère avec un matériau transparent. Pour des raisons de performance, Qt n'implémente pas la transparence de manière totalement réaliste. Au lieu de cela, Qt rend tous les objets opaques de la scène dans une texture, puis les matériaux transparents lisent à partir de cette texture. Cela signifie que les matériaux transparents donneront le meilleur résultat lorsqu'ils seront 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 simpliste qui n'essaie pas de faire de la réfraction physique réelle :
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 se réfère à la texture montrant 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 un effet de réfraction, avant d'effectuer une recherche de texture.
Enfin, nous ajoutons 20% de blanc pour obtenir un léger effet de nuage. Notez que la sortie est assignée à BASE_COLOR, de sorte que Qt XML ajoutera l'éclairage par-dessus. C'est pourquoi nous pouvons voir des reflets sur la surface de la sphère.
Matériaux non ombrés
Il est également possible d'avoir des matériaux personnalisés qui utilisent des programmes de shaders complets (tout en utilisant les mots-clés de commodité). L'exemple customshaders présente l'autre groupe 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.