1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3dcustommaterial_p.h"
5#include <QtQuick3DRuntimeRender/private/qssgrendercustommaterial_p.h>
6#include <ssg/qssgrendercontextcore.h>
7#include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h>
8#include <QtQuick/QQuickWindow>
9
10#include "qquick3dobject_p.h"
11#include "qquick3dviewport_p.h"
12#include "qquick3dscenemanager_p.h"
13
14QT_BEGIN_NAMESPACE
15
16/*!
17 \qmltype CustomMaterial
18 \inherits Material
19 \inqmlmodule QtQuick3D
20 \brief Base component for creating custom materials used to shade models.
21
22 The custom material allows using custom shader code for a material, enabling
23 programmability on graphics shader level. A vertex, fragment, or both
24 shaders can be provided. The \l vertexShader and \l fragmentShader
25 properties are URLs, referencing files containing shader snippets, and work
26 very similarly to ShaderEffect or \l{Image::source}{Image.source}. Only the
27 \c file and \c qrc schemes are supported with custom materials. It is also
28 possible to omit the \c file scheme, allowing to specify a relative path in
29 a convenient way. Such a path is resolved relative to the component's (the
30 \c{.qml} file's) location.
31
32 For a getting started guide to custom materials, see the page \l{Programmable
33 Materials, Effects, Geometry, and Texture data}.
34
35 \section1 Introduction
36
37 Consider the following versions of the same scene. On the left, the cylinder
38 is using a built-in, non-programmable material. Such materials are
39 configurable through a wide range of properties, but there is no further
40 control given over the shaders that are generated under the hood. On the
41 right, the same cylinder is now associated with a CustomMaterial referencing
42 application-provided vertex and fragment shader snippets. This allows
43 inserting custom, application-specific logic into the vertex shader to
44 transform the geometry, and to determine certain color properties in a
45 custom manner in the fragment shader. As this is a
46 \l{shadingMode}{shaded} custom material, the cylinder still
47 participates in the scene lighting normally.
48
49 \table 70%
50 \row
51 \li \qml
52 View3D {
53 anchors.fill: parent
54 PerspectiveCamera {
55 id: camera
56 position: Qt.vector3d(0, 0, 600)
57 }
58 camera: camera
59 DirectionalLight {
60 position: Qt.vector3d(-500, 500, -100)
61 color: Qt.rgba(0.2, 0.2, 0.2, 1.0)
62 ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
63 }
64 Model {
65 source: "#Cylinder"
66 eulerRotation: Qt.vector3d(30, 30, 0)
67 scale: Qt.vector3d(1.5, 1.5, 1.5)
68 materials: [
69 DefaultMaterial {
70 diffuseColor: Qt.rgba(0, 1, 0, 1)
71 }
72 ]
73 }
74 }
75 \endqml
76 \li \qml
77 View3D {
78 anchors.fill: parent
79 PerspectiveCamera {
80 id: camera
81 position: Qt.vector3d(0, 0, 600)
82 }
83 camera: camera
84 DirectionalLight {
85 position: Qt.vector3d(-500, 500, -100)
86 color: Qt.rgba(0.2, 0.2, 0.2, 1.0)
87 ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
88 }
89 Model {
90 source: "#Cylinder"
91 eulerRotation: Qt.vector3d(30, 30, 0)
92 scale: Qt.vector3d(1.5, 1.5, 1.5)
93 materials: [
94 CustomMaterial {
95 vertexShader: "material.vert"
96 fragmentShader: "material.frag"
97 property real uTime
98 property real uAmplitude: 50
99 NumberAnimation on uTime { from: 0; to: 100; duration: 10000; loops: -1 }
100 }
101 ]
102 }
103 }
104 \endqml
105 \endtable
106
107 Let's assume that the shader snippets in \c{material.vert} and \c{material.frag} are
108 the following:
109
110 \table 70%
111 \row
112 \li \badcode
113 void MAIN()
114 {
115 VERTEX.x += sin(uTime + VERTEX.y) * uAmplitude;
116 }
117 \endcode
118 \li \badcode
119 void MAIN()
120 {
121 BASE_COLOR = vec4(0.0, 1.0, 0.0, 1.0);
122 }
123 \endcode
124 \endtable
125
126 Notice how \c uTime and \c uAmplitude are properties of the CustomMaterial
127 element. They can change values and get animated normally, the values will
128 be exposed to the shaders automatically without any further action from the
129 developer.
130
131 The result is a cylinder that animates its vertices:
132
133 \image custommaterial_cylinder.png
134
135 \section1 Two flavors of custom materials
136
137 There are two main types of custom materials. This is specified by the \l
138 shadingMode property. In \l{CustomMaterial::shadingMode}{unshaded} custom
139 materials the fragment shader outputs a single \c vec4 color, ignoring
140 lights, light probes, shadowing in the scene. In
141 \l{CustomMaterial::shadingMode}{shaded} materials the shader is expected to
142 implement certain functions and work with built-in variables to take
143 lighting and shadow contribution into account.
144
145 The default choice is typically a shaded material, this is reflected in the
146 default value of the \l shadingMode property. This fits materials that needs
147 to transform vertices or other incoming data from the geometry, or determine
148 values like \c BASE_COLOR or \c EMISSIVE_COLOR in a custom manner, perhaps
149 by sampling \c SCREEN_TEXTURE or \c DEPTH_TEXTURE, while still reciving
150 light and shadow contributions from the scene. Additionally, such materials
151 can also override and reimplement the equations used to calculate the
152 contributions from directional, point, and other lights. The
153 application-provided shader snippets are heavily amended by the Qt Quick 3D
154 engine under the hood, in order to provide the features, such as lighting,
155 the standard materials have.
156
157 Unshaded materials are useful when the object's appearance is determined
158 completely by the custom shader code. The shaders for such materials
159 receive minimal additions by the engine, and therefore it is completely up
160 to the shader to determine the final fragment color. This gives more
161 freedom, but also limits possiblities to integrate with other elements of
162 the scene, such as lights.
163
164 \note Shader code is always provided using Vulkan-style GLSL, regardless of
165 the graphics API used by Qt at run time.
166
167 \note The vertex and fragment shader code provided by the material are not
168 full, complete GLSL shaders on their own. Rather, they provide a set of
169 functions, which are then amended with further shader code by the engine.
170
171 \section1 Exposing data to the shaders
172
173 The dynamic properties of the CustomMaterial can be changed and animated
174 using QML and Qt Quick facilities, and the values are exposed to the
175 shaders automatically. This in practice is very similar ShaderEffect. The
176 following list shows how properties are mapped:
177
178 \list
179 \li bool, int, real -> bool, int, float
180 \li QColor, \l{QtQml::Qt::rgba()}{color} -> vec4, and the color gets
181 converted to linear, assuming sRGB space for the color value specified in
182 QML. The built-in Qt colors, such as \c{"green"} are in sRGB color space as
183 well, and the same conversion is performed for all color properties of
184 DefaultMaterial and PrincipledMaterial, so this behavior of CustomMaterial
185 matches those. Unlike Qt Quick, for Qt Quick 3D linearizing is essential as
186 there will typically be tonemapping performed on the 3D scene.
187 \li QRect, QRectF, \l{QtQml::Qt::rect()}{rect} -> vec4
188 \li QPoint, QPointF, \l{QtQml::Qt::point()}{point}, QSize, QSizeF, \l{QtQml::Qt::size()}{size} -> vec2
189 \li QVector2D, \l{QtQml::Qt::vector2d()}{vector2d} -> vec2
190 \li QVector3D, \l{QtQml::Qt::vector3d()}{vector3d} -> vec3
191 \li QVector4D, \l{QtQml::Qt::vector4d()}{vector4d} -> vec4
192 \li QMatrix4x4, \l{QtQml::Qt::matrix4x4()}{matrix4x4} -> mat4
193 \li QQuaternion, \l{QtQml::Qt::quaternion()}{quaternion} -> vec4, scalar value is \c w
194
195 \li TextureInput -> sampler2D or samplerCube, depending on whether \l
196 Texture or \l CubeMapTexture is used in the texture property of the
197 TextureInput. Setting the \l{TextureInput::enabled}{enabled} property to
198 false leads to exposing a dummy texture to the shader, meaning the shaders
199 are still functional but will sample a texture with opaque black image
200 content. Pay attention to the fact that properties for samplers must always
201 reference a \l TextureInput object, not a \l Texture directly. When it
202 comes to the \l Texture properties, the source, tiling, and filtering
203 related ones are the only ones that are taken into account implicitly with
204 custom materials, as the rest (such as, UV transformations) is up to the
205 custom shaders to implement as they see fit.
206
207 \endlist
208
209 \note When a uniform referenced in the shader code does not have a
210 corresponding property, it will cause a shader compilation error when
211 processing the material at run time. There are some exceptions to this,
212 such as, sampler uniforms, that get a dummy texture bound when no
213 corresponding QML property is present, but as a general rule, all uniforms
214 and samplers must have a corresponding property declared in the
215 CustomMaterial object.
216
217 \section1 Unshaded custom materials
218
219 The following is an example of an \l{CustomMaterial::shadingMode}{unshaded}
220 custom material.
221
222 \qml
223 CustomMaterial {
224 // These properties are automatically exposed to the shaders
225 property real time: 0.0
226 property real amplitude: 5.0
227 property real alpha: 1.0
228 property TextureInput tex: TextureInput {
229 enabled: true
230 texture: Texture { source: "image.png" }
231 }
232
233 shadingMode: CustomMaterial.Unshaded
234 sourceBlend: alpha < 1.0 ? CustomMaterial.SrcAlpha : CustomMaterial.NoBlend
235 destinationBlend: alpha < 1.0 ? CustomMaterial.OneMinusSrcAlpha : CustomMaterial.NoBlend
236 cullMode: CustomMaterial.BackFaceCulling
237
238 vertexShader: "customshader.vert"
239 fragmentShader: "customshader.frag"
240 }
241 \endqml
242
243 With the above example, the \l{CustomMaterial::shadingMode}{unshaded} vertex
244 and fragment shaders snippets could look like the following. Note how the
245 shaders do not, and must not, declare uniforms or vertex inputs as that is
246 taken care of by Qt when assembling the final shader code.
247
248 \badcode
249 VARYING vec3 pos;
250 VARYING vec2 texcoord;
251
252 void MAIN()
253 {
254 pos = VERTEX;
255 pos.x += sin(time * 4.0 + pos.y) * amplitude;
256 texcoord = UV0;
257 POSITION = MODELVIEWPROJECTION_MATRIX * vec4(pos, 1.0);
258 }
259 \endcode
260
261 \badcode
262 VARYING vec3 pos;
263 VARYING vec2 texcoord;
264
265 void MAIN()
266 {
267 vec4 c = texture(tex, texcoord);
268 FRAGCOLOR = vec4(pos.x * 0.02, pos.y * 0.02, pos.z * 0.02, alpha) * c;
269 }
270 \endcode
271
272 The following special, uppercase keywords are available:
273
274 \list
275
276 \li MAIN -> the name of the entry point in the vertex or fragment shader
277 snippet must always be \c MAIN. Providing this function is mandatory in
278 shader snippets for unshaded custom materials.
279
280 \li VARYING -> declares an output from the vertex shader or an input to the
281 fragment shader
282
283 \li POSITION -> vec4, the output from the vertex shader
284
285 \li FRAGCOLOR -> vec4, the output from the fragment shader. Available only
286 for unshaded custom materials.
287
288 \li VERTEX -> vec3, the vertex position in the vertex shader.
289
290 \li NORMAL -> vec3, the vertex normal in the vertex shader. When the mesh
291 for the associated model does not provide normals, the value is vec3(0.0).
292
293 \li UV0 -> vec2, the first set of texture coordinates in the vertex shader.
294 When the mesh for the associated model does not provide texture
295 coordinates, the value is vec2(0.0).
296
297 \li UV1 -> vec2, the second set of texture coordinates in the vertex
298 shader. When the mesh for the associated model does not provide a second
299 set of texture coordinates, the value is vec2(0.0).
300
301 \li COLOR -> vec4, the vertex color in the vertex shader. When the mesh for
302 the associated model does not provide per-vertex colors, the value is
303 vec4(1.0).
304
305 \li TANGENT -> vec3, tangent in the vertex shader. When the mesh for the
306 associated model does not provide tangent data, the value is vec3(0.0).
307
308 \li BINORMAL -> vec3, binormal in the vertex shader. When the mesh for the
309 associated model does not provide binormal data, the value is vec3(0.0).
310
311 \li JOINTS -> ivec4, joint indexes in the vertex shader. When the mesh for
312 the associated model does not provide joint indexes data, the value is
313 ivec4(0).
314
315 \li WEIGHTS -> vec4, joint weights in the vertex shader. When the mesh for
316 the associated model does not provide joint weights data, the value is
317 vec4(0.0).
318
319 \li MORPH_POSITION(\e{n}) -> vec3, the \e{n+1}th morph target position in the vertex
320 shader. The associated model should provide proper data.
321
322 \li MORPH_NORMAL(\e{n}) -> vec3, the \e{n+1}th morph target normal in the vertex
323 shader. The associated model should provide proper data.
324
325 \li MORPH_TANGENT(\e{n}) -> vec3, the \e{n+1}th morph target tangent in the vertex
326 shader. The associated model should provide proper data.
327
328 \li MORPH_BINORMAL(\e{n}) -> vec3, the \e{n+1}th morph target binormal in the vertex
329 shader. The associated model should provide proper data.
330
331 \li MODELVIEWPROJECTION_MATRIX -> mat4, the model-view-projection matrix.
332 Projection matrices always follow OpenGL conventions, with a baked-in
333 transformation for the Y axis direction and clip depth, depending on the
334 graphics API used at run time.
335
336 \li VIEWPROJECTION_MATRIX -> mat4, the view-projection matrix
337
338 \li PROJECTION_MATRIX -> mat4, the projection matrix
339
340 \li INVERSE_PROJECTION_MATRIX -> mat4, the inverse projection matrix
341
342 \li VIEW_MATRIX -> mat4, the view (camera) matrix
343
344 \li MODEL_MATRIX -> mat4, the model (world) matrix
345
346 \li NORMAL_MATRIX -> mat3, the normal matrix (the transpose of the inverse
347 of the top-left 3x3 part of the model matrix)
348
349 \li BONE_TRANSFORMS -> mat4[], the array of the model's bone matrixes
350
351 \li BONE_NORMAL_TRANSFORMS -> mat3[], the array of the model's bone normal
352 matrixes (the transpose of the inverse of the top-left 3x3 part of the each
353 bone matrixes)
354
355 \li MORPH_WEIGHTS -> float[], the array of the morph weights. The associated model
356 should provide proper data. For safety, \b {QT_MORPH_MAX_COUNT} is defined to the
357 size of this array.
358
359 \li CAMERA_POSITION -> vec3, the camera position in world space
360
361 \li CAMERA_DIRECTION -> vec3, the camera direction vector
362
363 \li CAMERA_PROPERTIES -> vec2, the near and far clip values for the camera
364
365 \li POINT_SIZE -> float, writable in the vertex shader only. When rendering
366 geometry with a topology of points, the custom vertex shader must set this
367 to either 1.0 or another value, both in shaded and unshaded custom
368 materials. See \l{PrincipledMaterial::pointSize} for further notes on
369 support for sizes other than 1.
370
371 \endlist
372
373 \section1 Shaded custom materials
374
375 A \l{CustomMaterial::shadingMode}{shaded} material \c augments the shader code
376 that would be generated by a PrincipledMaterial. Unlike unshaded materials,
377 that provide almost all logic for the vertex and fragment shader main
378 functions on their own, preventing adding generated code for lighting,
379 shadowing, global illumination, etc., shaded materials let shader
380 generation happen normally, as if the CustomMaterial was a
381 PrincipledMaterial. The vertex and fragment shader snippets are expected to
382 provide optional functions that are then invoked at certain points, giving
383 them the possibility to customize the colors and other values that are then
384 used for calculating lighting and the final fragment color.
385
386 Rather than implementing just a \c MAIN function, the fragment shader for a
387 shaded custom material can implement multiple functions. All functions,
388 including \c MAIN, are optional to implement in shaded custom materials. An
389 empty shader snippet, or, even, not specifying the
390 \l{CustomMaterial::vertexShader}{vertexShader} or
391 \l{CustomMaterial::fragmentShader}{fragmentShader} properties at all can be
392 perfectly valid too.
393
394 \section2 Vertex shader snippets in a shaded custom material
395
396 The following functions can be implemented in a vertex shader snippet:
397
398 \list
399
400 \li \c{void MAIN()} When present, this function is called in order to set
401 the value of \c POSITION, the vec4 output from the vertex shader, and,
402 optionally, to modify the values of \c VERTEX, \c COLOR, \c NORMAL, \c UV0,
403 \c UV1, \c TANGENT, \c BINORMAL, \c JOINTS, and \c WEIGHTS. Unlike in
404 unshaded materials, writing to these makes sense because the modified values
405 are then taken into account in the rest of the generated shader code
406 (whereas for unshaded materials there is no additional shader code
407 generated). For example, if the custom vertex shader displaces the vertices
408 or the normals, it will want to store the modified values to \c VERTEX or
409 \c NORMAL, to achieve correct lighting calculations afterwards.
410 Additionally, the function can write to variables defined with \c VARYING in
411 order to pass interpolated data to the fragment shader. When this function
412 or a redefinition of \c POSITION is not present, \c POSITION is calculated
413 based on \c VERTEX and \c MODELVIEWPROJECTION_MATRIX, just like a
414 PrincipledMaterial would do.
415
416 Example, with relying both on QML properties exposed as uniforms, and also
417 passing data to the fragment shader:
418 \badcode
419 VARYING vec3 vNormal;
420 VARYING vec3 vViewVec;
421
422 void MAIN()
423 {
424 VERTEX.x += sin(uTime * 4.0 + VERTEX.y) * uAmplitude;
425 vNormal = normalize(NORMAL_MATRIX * NORMAL);
426 vViewVec = CAMERA_POSITION - (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
427 POSITION = MODELVIEWPROJECTION_MATRIX * vec4(VERTEX, 1.0);
428 }
429 \endcode
430
431 \note In the above example, assigning a value to \c POSITION is optional as
432 the usage in this case is identical to the default behavior.
433
434 \endlist
435
436 \note To pass data without interpolation from the vertex to the fragment
437 stage, add the \c flat keyword before the type in the \c VARYING
438 declarations.
439
440 \section2 Fragment shader snippets in a shaded custom material
441
442 The following functions can be implemented in a fragment shader snippet:
443
444 \list
445
446 \li \c{void MAIN()} When present, this function is called to set the values
447 of the special writable variables \c BASE_COLOR, \c METALNESS, \c ROUGHNESS, \c
448 SPECULAR_AMOUNT, NORMAL, CLEARCOAT_FRESNEL_POWER, CLEARCOAT_FRESNEL_SCALE,
449 CLEARCOAT_FRESNEL_BIAS, CLEARCOAT_AMOUNT, CLEARCOAT_ROUGHNESS, CLEARCOAT_NORMAL,
450 FRESNEL_BIAS, FRESNEL_SCALE, FRESNEL_POWER, IOR, \c TRANSMISSION_FACTOR,
451 THICKNESS_FACTOR, ATTENUATION_COLOR, ATTENUATION_DISTANCE and \c OCCLUSION_AMOUNT.
452
453 One common use case is to set the value of \c BASE_COLOR based on sampling
454 a texture, be it a base color map, \c SCREEN_TEXTURE, or some other kind of
455 source. This can be relevant and convenient especially when no custom light
456 processor functions are implemented. Setting \c{BASE_COLOR.a} to something
457 other than the default 1.0 allows affecting the final alpha value of the
458 fragment. (note that this will often require also enabling alpha blending
459 in \l sourceBlend and \l destinationBlend)
460
461 Another scenario is when there is no custom \c SPECULAR_LIGHT function
462 provided, or when there is a light probe set in the SceneEnvironment. The
463 metalness, roughness, and other values that affect the specular
464 contribution calculation can be set in \c MAIN to their desired custom
465 values.
466
467 The function can write to the following special variables. The values
468 written to these will typically be either hardcoded or be calculated based
469 on QML properties mapped to uniforms. The semantics are identical to
470 PrincipledMaterial.
471
472 \list
473
474 \li vec4 \c BASE_COLOR - The base color and material alpha value.
475 Corresponds to the \l{PrincipledMaterial::baseColor}{built-in materials'
476 color property}. When light processor functions are not implemented, it can
477 be convenient to set a custom base color in \c MAIN because that is then
478 taken into account in the default lighting calculations. The default value
479 is \c{vec4(1.0)}, meaning white with an alpha of 1.0. The alpha value
480 effects the final alpha of the fragment. The final alpha value is the
481 object (model) opacity multiplied by the base color alpha. When specifying
482 the value directly in shader code, not relying on uniform values exposed
483 from \b color properties in QML, be aware that it is up to the shader to
484 perform the sRGB to linear conversion, if needed. For example, assuming
485 a \c{vec3 color} and \c{float alpha} this can be achieved like the following:
486 \badcode
487 float C1 = 0.305306011;
488 vec3 C2 = vec3(0.682171111, 0.682171111, 0.682171111);
489 vec3 C3 = vec3(0.012522878, 0.012522878, 0.012522878);
490 BASE_COLOR = vec4(rgb * (rgb * (rgb * C1 + C2) + C3), alpha);
491 \endcode
492
493 \li vec3 \c EMISSIVE_COLOR - The color of self-illumination. Corresponds to
494 the built-in materials' emissive color which is combined by
495 \l {PrincipledMaterial::emissiveFactor}{built-in materials's emissiveFactor property}
496 and \l {PrincipledMaterial::emissiveMap}{built-in materials's emissiveMap property}.
497 The default value is \c{vec3(0.0)}. When specifying the value
498 directly in shader code, not relying on uniform values exposed from \b color
499 properties in QML, be aware that it is up to the shader to perform the sRGB
500 to linear conversion, if needed.
501
502 \li float \c IOR Specifies the index of refraction of the material. A typical value,
503 and also the default, is \c{1.5} as that is what a PrincipledMaterial would use.
504
505 \li float \c TRANSMISSION_FACTOR Specifies the amount of the translucency. A typical value,
506 would be \c{1.0} and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
507
508 \li float \c THICKNESS_FACTOR Specifies the amount of the translucent material thickness. A typical value,
509 would be \c{10.0} and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
510
511 \li vec3 \c ATTENUATION_COLOR Specifies the color shift of the translucent material by distance. A typical value,
512 would be \c{vec3(1.0, 0.0, 0.0)} and also the default, is \c{vec3(1.0)} as that is what a PrincipledMaterial would use.
513
514 \li float \c ATTENUATION_DISTANCE Specifies the distance attenuation of color shift of the translucent material. A typical value,
515 would be \c{100.0} and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
516
517 \li float \c METALNESS Metalness amount in range 0.0 - 1.0. The default
518 value is 0. Must be set to a non-zero value to have effect.
519
520 \li float \c ROUGHNESS Roughness value in range 0.0 - 1.0. The default value is 0.
521
522 \li float \c CLEARCOAT_FRESNEL_POWER Specifies the fresnel power of the clearcoat layer. A typical value,
523 and also the default, is \c{5.0} as that is what a PrincipledMaterial would use.
524
525 \li float \c CLEARCOAT_FRESNEL_SCALE Specifies the fresnel scale of the clearcoat layer. A typical value,
526 and also the default, is \c{1.0} as that is what a PrincipledMaterial would use.
527
528 \li float \c CLEARCOAT_FRESNEL_BIAS Specifies the fresnel bias of the clearcoat layer. A typical value,
529 and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
530
531 \li float \c CLEARCOAT_AMOUNT Specifies the amount of the clearcoat layer on top of the material. A typical value,
532 would be \c{1.0} and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
533
534 \li float \c CLEARCOAT_ROUGHNESS Specifies the roughness of the clearcoat layer. A typical value,
535 would be \c{1.0} for fully blurred clearcoat layer and also the default, is \c{0.0} as that is
536 what a PrincipledMaterial would use.
537
538 \li vec3 \c CLEARCOAT_NORMAL - The clearcoat layer normal that comes from the vertex shader in world
539 space. While this property has the same initial value as \c VAR_WORLD_NORMAL,
540 only changing the value of \c CLEARCOAT_NORMAL will have an effect on clearcoat layer normal.
541
542 \li float \c FRESNEL_POWER Specifies the fresnel power. A typical value,
543 and also the default, is \c{5.0} as that is what a PrincipledMaterial would use.
544
545 \li float \c FRESNEL_SCALE Specifies the fresnel scale. A typical value,
546 and also the default, is \c{1.0} as that is what a PrincipledMaterial would use.
547
548 \li float \c FRESNEL_BIAS Specifies the fresnel bias. A typical value,
549 and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
550
551 \li float \c SPECULAR_AMOUNT Specular amount in range 0.0 - 1.0. The
552 default value is \c{0.5}, matching \l{PrincipledMaterial::specularAmount}. Must
553 be set to a non-zero value to have effect.
554
555 \li float \c OCCLUSION_AMOUNT Specifies the AO factor. A typical value,
556 and also the default, is \c{1.0} as that is what a PrincipledMaterial would use.
557
558 \li vec3 \c NORMAL - The normal that comes from the vertex shader in world
559 space. While this property has the same initial value as \c VAR_WORLD_NORMAL,
560 only changing the value of \c NORMAL will have an effect on lighting.
561
562 \li vec3 \c TANGENT - The tanget that comes from the vertex shader in world
563 space. This value is potentially adjusted for double-sidedness.
564
565 \li vec3 \c BINORMAL - The binormal that comes from the vertex shader in
566 world space. This value is potentially adjusted for double-sidedness.
567
568 \li vec2 \c UV0 - The first set of texture coordinates from the vertex shader.
569 This property is readonly in the fragment shader.
570
571 \li vec2 \c UV1 - The second set of texture coordinates from the vertex shader.
572 This property is readonly in the fragment shader.
573
574 \endlist
575
576 \note Unlike with unshaded materials, the fragment \c MAIN for a shaded
577 material has no direct control over \c FRAGCOLOR. Rather, it is the \c
578 DIFFUSE and \c SPECULAR values written in the light processor functions
579 that decide what the final fragment color is. When a light processor
580 function is not implemented, the relevant default shading calculations are
581 performed as with a PrincipledMaterial, taking \c BASE_COLOR and other
582 values from the list above into account.
583
584 An example of a simple, metallic custom material shader could be the following:
585 \badcode
586 void MAIN()
587 {
588 METALNESS = 1.0;
589 ROUGHNESS = 0.5;
590 FRESNEL_POWER = 5.0;
591 }
592 \endcode
593
594 Another example, where the base color and alpha are set by sampling a texture:
595 \badcode
596 VARYING vec2 texcoord;
597 void MAIN()
598 {
599 BASE_COLOR = texture(uColorMap, texcoord);
600 }
601 \endcode
602
603 \li \c{void AMBIENT_LIGHT()} When present, this function is called once for
604 each fragment. The task of the function is to add the total ambient
605 contribution to a writable special variable \c DIFFUSE. It can of course
606 choose to calculate a different value, or not touch \c DIFFUSE at all (to
607 ignore ambient lighting completely). When this function is not present at
608 all, the ambient contribution is calculated normally, like a
609 PrincipledMaterial would do.
610
611 The function can write to the following special variables:
612
613 \list
614
615 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per
616 fragment. The light processor functions will typically add (\c{+=}) to it,
617 since overwriting the value would lose the contribution from other lights.
618
619 \endlist
620
621 The function can read the following special variables, in addition to the
622 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
623 uniforms from the table above:
624
625 \list
626 \li vec3 \c TOTAL_AMBIENT_COLOR The total ambient contribution in the scene.
627 \endlist
628
629 Example:
630 \badcode
631 void AMBIENT_LIGHT()
632 {
633 DIFFUSE += TOTAL_AMBIENT_COLOR;
634 }
635 \endcode
636
637 \li \c{void DIRECTIONAL_LIGHT()} When present, this function is called for
638 each active directional light in the scene for each fragment. The task of
639 the function is to add the diffuse contribution to a writable special
640 variable \c DIFFUSE. The function can also choose to do nothing, in which
641 case diffuse contributions from directional lights are ignored. When the
642 function is not present at all, the diffuse contributions from directional
643 lights are accumulated normally, like a PrincipledMaterial would do.
644
645 The function can write to the following special variables:
646
647 \list
648
649 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per
650 fragment. The light processor functions will typically add (\c{+=}) to it,
651 since overwriting the value would lose the contribution from other lights.
652
653 \endlist
654
655 The function can read the following special variables, in addition to the
656 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
657 uniforms from the table above:
658
659 \list
660
661 \li vec3 \c LIGHT_COLOR Diffuse light color.
662 \li float \c SHADOW_CONTRIB Shadow contribution, or 1.0 if not shadowed at all or not reciving shadows.
663 \li vec3 \c TO_LIGHT_DIR Vector pointing towards the light source.
664 \li vec3 \c NORMAL The normal vector in world space.
665 \li vec4 \c BASE_COLOR The base color and material alpha value.
666 \li float \c METALNESS The Metalness amount.
667 \li float \c ROUGHNESS The Roughness amount.
668
669 \endlist
670
671 Example:
672 \badcode
673 void DIRECTIONAL_LIGHT()
674 {
675 DIFFUSE += LIGHT_COLOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
676 }
677 \endcode
678
679 \li \c{void POINT_LIGHT()} When present, this function is called for
680 each active point light in the scene for each fragment. The task of
681 the function is to add the diffuse contribution to a writable special
682 variable \c DIFFUSE. The function can also choose to do nothing, in which
683 case diffuse contributions from point lights are ignored. When the
684 function is not present at all, the diffuse contributions from point
685 lights are accumulated normally, like a PrincipledMaterial would do.
686
687 The function can write to the following special variables:
688
689 \list
690 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per fragment.
691 \endlist
692
693 The function can read the following special variables, in addition to the
694 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
695 uniforms from the table above:
696
697 \list
698 \li vec3 \c LIGHT_COLOR Diffuse light color.
699 \li float \c LIGHT_ATTENUATION Light attenuation.
700 \li float \c SHADOW_CONTRIB Shadow contribution, or 1.0 if not shadowed at all or not reciving shadows.
701 \li vec3 \c TO_LIGHT_DIR Vector pointing towards the light source.
702 \li vec3 \c NORMAL The normal vector in world space.
703 \li vec4 \c BASE_COLOR The base color and material alpha value.
704 \li float \c METALNESS The Metalness amount.
705 \li float \c ROUGHNESS The Roughness amount.
706 \endlist
707
708 Example:
709 \badcode
710 void POINT_LIGHT()
711 {
712 DIFFUSE += LIGHT_COLOR * LIGHT_ATTENUATION * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
713 }
714 \endcode
715
716 \li \c{void SPOT_LIGHT()} When present, this function is called for
717 each active spot light in the scene for each fragment. The task of
718 the function is to add the diffuse contribution to a writable special
719 variable \c DIFFUSE. The function can also choose to do nothing, in which
720 case diffuse contributions from spot lights are ignored. When the
721 function is not present at all, the diffuse contributions from spot
722 lights are accumulated normally, like a PrincipledMaterial would do.
723
724 The function can write to the following special variables:
725
726 \list
727 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per fragment.
728 \endlist
729
730 The function can read the following special variables, in addition to the
731 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
732 uniforms from the table above:
733
734 \list
735 \li vec3 \c LIGHT_COLOR Diffuse light color.
736 \li float \c LIGHT_ATTENUATION Light attenuation.
737 \li float \c SHADOW_CONTRIB Shadow contribution, or 1.0 if not shadowed at all or not reciving shadows.
738 \li vec3 \c TO_LIGHT_DIR Vector pointing towards the light source.
739 \li float \c SPOT_FACTOR Spot light factor.
740 \li vec3 \c NORMAL The normal vector in world space.
741 \li vec4 \c BASE_COLOR The base color and material alpha value.
742 \li float \c METALNESS The Metalness amount.
743 \li float \c ROUGHNESS The Roughness amount.
744 \endlist
745
746 Example:
747 \badcode
748 void SPOT_LIGHT()
749 {
750 DIFFUSE += LIGHT_COLOR * LIGHT_ATTENUATION * SPOT_FACTOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
751 }
752 \endcode
753
754 \li \c{void SPECULAR_LIGHT()} When present, this function is called for
755 each active light in the scene for each fragment. The task of the function
756 is to add the specular contribution to a writable special variable \c
757 SPECULAR. The function can also choose to do nothing, in which case
758 specular contributions from lights are ignored. When the function is not
759 present at all, the specular contributions from lights are accumulated
760 normally, like a PrincipledMaterial would do.
761
762 The function can write to the following special variables:
763
764 \list
765
766 \li vec3 \c SPECULAR Accumulates the specular light contributions, per
767 frament. The light processor functions will typically add (\c{+=}) to it,
768 since overwriting the value would lose the contribution from other lights.
769
770 \endlist
771
772 The function can read the following special variables, in addition to the
773 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
774 uniforms from the table above:
775
776 \list
777 \li vec3 \c LIGHT_COLOR Specular light color.
778 \li float \c LIGHT_ATTENUATION Light attenuation. For directional lights the value is 1.0. For spot lights the value is the same as \c {LIGHT_ATTENUATION * SPOT_FACTOR} of \c {void SPOT_LIGHT()}.
779 \li float \c SHADOW_CONTRIB Shadow contribution, or 1.0 if not shadowed at all or not reciving shadows.
780 \li vec3 \c FRESNEL_CONTRIB Fresnel contribution from built in Fresnel calculation.
781 \li vec3 \c TO_LIGHT_DIR Vector pointing towards the light source.
782 \li vec3 \c NORMAL The normal vector in world space.
783 \li vec4 \c BASE_COLOR The base color and material alpha value.
784 \li float \c METALNESS The Metalness amount.
785 \li float \c ROUGHNESS The Roughness amount.
786 \li float \c SPECULAR_AMOUNT The specular amount. This value will be between
787 0.0 and 1.0 will be the same value set in the custom \c MAIN function. This
788 value will useful for calculating Fresnel contributions when not using the
789 built-in Fresnel contribution provided by \c FRESNEL_CONTRIB.
790 \endlist
791
792 \badcode
793 void SPECULAR_LIGHT()
794 {
795 vec3 H = normalize(VIEW_VECTOR + TO_LIGHT_DIR);
796 float cosAlpha = max(0.0, dot(H, normalize(NORMAL)));
797 float shine = pow(cosAlpha, exp2(15.0 * (1.0 - ROUGHNESS) + 1.0) * 0.25);
798 SPECULAR += shine * LIGHT_COLOR * FRESNEL_CONTRIB * SHADOW_CONTRIB * LIGHT_ATTENUATION;
799 }
800 \endcode
801
802 \li \c{void POST_PROCESS()} When present, this function is called at the
803 end of the fragment pipeline. The task of the function is to finalize
804 \c COLOR_SUM with final diffuse, specular and emissive terms. Unlike
805 \c FRAGCOLOR for a unshaded material, \c COLOR_SUM will be automatically
806 tonemapped before written to the framebuffer. For debugging purposes it is
807 sometimes useful to output a value that should not be treated as a color.
808 To avoid the tonemapping distorting this value it can be disabled by
809 setting the \l {SceneEnvironment::tonemapMode}{tonemapMode} property
810 to \c TonemapModeNone
811
812 The function can write to the following special variables:
813
814 \list
815 \li vec4 \c COLOR_SUM the output from the fragment shader. The default value
816 is vec4(DIFFUSE.rgb + SPECULAR + EMISSIVE, DIFFUSE.a)
817 \endlist
818
819 The function can read the following special variables.
820
821 \list
822 \li vec4 \c DIFFUSE The final diffuse term of the fragment pipeline.
823 \li vec3 \c SPECULAR The final specular term of the fragment pipeline.
824 \li vec3 \c EMISSIVE The final emissive term of the fragment pipeline.
825 \li vec2 \c UV0 - The first set of texture coordinates from the vertex shader.
826 \li vec2 \c UV1 - The second set of texture coordinates from the vertex shader.
827 \endlist
828
829 \badcode
830 void POST_PROCESS()
831 {
832 float center_x = textureSize(SCREEN_TEXTURE, 0).x * 0.5;
833 if (gl_FragCoord.x > center_x)
834 COLOR_SUM = DIFFUSE;
835 else
836 COLOR_SUM = vec4(EMISSIVE, DIFFUSE.a);
837 }
838 \endcode
839
840 \li \c{void IBL_PROBE()} When present, this function is called for IBL
841 (Image-Based Lighting).
842 The task of the function is to add both the diffuse and the specular
843 contributions of IBL to writable special variables \c DIFFUSE and
844 \c SPECULAR.
845
846 The function can write to the following special variables:
847
848 \list
849 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per fragment.
850 \li vec3 \c SPECULAR Accumulates the specular light contributions, per
851 frament.
852 \endlist
853
854 The function can read the following special variables.
855
856 \list
857 \li vec4 \c BASE_COLOR The base color and material alpha value.
858 \li float \c AO_FACTOR The screen space occlusion factor.
859 \li float \c SPECULAR_AMOUNT The specular amount.
860 \li float \c ROUGHNESS The final emissive term of the fragment pipeline.
861 \li vec3 \c NORMAL The normal vector in world space.
862 \li vec3 \c VIEW_VECTOR Points towards the camera.
863 \li mat3 \c IBL_ORIENTATION The orientation of the light probe. It comes
864 from \l {SceneEnvironment::probeOrientation}.
865 \endlist
866
867 \badcode
868 void IBL_PROBE()
869 {
870 vec3 smpDir = IBL_ORIENTATION * NORMAL;
871 DIFFUSE += AO_FACTOR * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, smpDir, IBL_MAXMIPMAP).rgb;
872 }
873 \endcode
874
875 \endlist
876
877 \sa SceneEnvironment::tonemapMode, {Using Image-Based Lighting}
878
879 \section2 Custom variables between functions
880
881 Additional variables can be delivered from the MAIN function to the others.
882 The \c SHARED_VARS keyword can be used for defining new custom variables.
883 These user-defined variables can be accessed with SHARED.<variable name>.
884
885 For example, a shaded custom material can fetch a shared value in the MAIN
886 and use it in other functions.
887
888 \badcode
889 SHARED_VARS {
890 vec3 colorThreshold;
891 };
892 void MAIN()
893 {
894 BASE_COLOR = texture(baseColorMap, UV0);
895 SHARED.colorThreshold = texture(thresholdMap, UV0).rgb;
896 }
897 void DIRECTIONAL_LIGHT()
898 {
899 if (DIFFUSE >= SHARED.colorThreshold) {
900 DIFFUSE = SHARED.colorThreshold;
901 return;
902 }
903 DIFFUSE += LIGHT_COLOR * SHADOW_CONTRIB;
904 }
905 \endcode
906
907 \note SHARED can be written on all the functions without POST_PROCESS but it
908 is safe to write it on MAIN and read on the other functions.
909
910 \note A recommended use case to write SHARED on LIGHT functions is
911 reseting it on MAIN first and then accumulating it on each LIGHT functions.
912
913 \badcode
914 SHARED_VARS {
915 float sheenIntensity;
916 float sheenRoughness;
917 vec3 sheenColor;
918 vec3 outSheenColor;
919 };
920 void MAIN()
921 {
922 ...
923 vec4 tex = texture(uSheenMap, UV0);
924 SHARED.sheenColor = tex.rgb;
925 SHARED.sheenIntensity = tex.a;
926 SHARED.sheenRoughness = uSheenRoughness;
927 SHARED.outSheenColor = vec3(0.0);
928 }
929 void SPECULAR_LIGHT()
930 {
931 SHARED.outSheenColor += ...;
932 }
933 void POST_PROCESS()
934 {
935 COLOR_SUM = DIFFUSE + SPECULAR + EMISSIVE + SHARED.outSheenColor;
936 }
937 \endcode
938
939 \note MAIN is called before others, and POST_PROCESS after all others,
940 but that there is no guarantee for any other ordering for light processors.
941
942 \section2 Additional special keywords
943
944 The custom fragment shader code can freely access uniforms (such as, \c
945 CAMERA_DIRECTION or \c CAMERA_POSITION), and varyings passed on from the
946 custom vertex shader. Additionally, there are a number of built-in varyings
947 available as special keywords. Some of these are optional in the sense that
948 a vertex \c MAIN could calculate and pass on these on its own, but to
949 reduce duplicated data fragment shaders can also rely on these built-ins
950 instead. These built-ins are available in light processor functions and in
951 the fragment MAIN.
952
953 \list
954
955 \li vec3 \c VAR_WORLD_NORMAL - Interpolated normal transformed by \c
956 NORMAL_MATRIX.
957
958 \li vec3 \c VAR_WORLD_TANGENT - Interpolated tangent transformed by \c
959 MODEL_MATRIX.
960
961 \li vec3 \c VAR_WORLD_BINORMAL - Interpolated binormal transformed by \c
962 MODEL_MATRIX
963
964 \li vec3 \c NORMAL - Unlike \c VAR_WORLD_NORMAL, which is the
965 interpolated normal as-is, this value is potentially adjusted for
966 double-sidedness: when rendering with culling disabled, the normal will get
967 inverted as necessary. Therefore lighting and other calculations are
968 recommended to use \c NORMAL instead of \c VAR_WORLD_NORMAL in order
969 behave correctly with all culling modes.
970
971 \li vec3 \c TANGENT - Like \c NORMAL, this value is potentially adjusted for
972 double-sidedness: when rendering with culling disabled, the tangent will get
973 inverted as necessary.
974
975 \li vec3 \c BINORMAL - Like \c NORMAL, this value is potentially adjusted for
976 double-sidedness: when rendering with culling disabled, the binormal will get
977 inverted as necessary.
978
979 \li vec3 \c VAR_WORLD_POSITION - Interpolated world space vertex position
980 (\c{(MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz})
981
982 \li vec4 \c VAR_COLOR - The interpolated vertex color when colors are
983 provided in the mesh. \c{vec4(1.0)} otherwise.
984
985 \li vec3 \c VIEW_VECTOR - Points towards the camera. This is
986 effectively the \c{CAMERA_POSITION - VAR_WORLD_POSITION} vector normalized.
987
988 \li vec4 \c FRAGCOORD - Contains the window-relative coordinates of the
989 current fragment.
990
991 \li float \c FRAMEBUFFER_Y_UP - The value is \c 1 when the Y axis points up
992 in the coordinate system for framebuffers (textures), meaning \c{(0, 0)} is
993 the bottom-left corner. The value is \c{-1} when the Y axis points down,
994 \c{(0, 0)} being the top-left corner. Such differences in the underlying
995 graphics APIs do not concern most custom materials. One notable exception
996 is sampling \c SCREEN_TEXTURE with texture coordinates \b not based on
997 \c FRAGCOORD. As the orientation of \c SCREEN_TEXTURE is tied to the
998 underlying graphics API by nature, using texture coordinates from a mesh
999 may need appropriate adjustments to the Y coordinate.
1000
1001 For example, the following fragment shader, suitable for Rectangle or Cube
1002 meshes, will display the opaque objects from the scene on the model:
1003
1004 \badcode
1005 VARYING vec2 texcoord;
1006 void MAIN()
1007 {
1008 vec2 screencoord = texcoord;
1009 if (FRAMEBUFFER_Y_UP < 0.0) // effectively: if not OpenGL
1010 screencoord.y = 1.0 - screencoord.y;
1011 BASE_COLOR = texture(SCREEN_TEXTURE, screencoord);
1012 }
1013 \endcode
1014
1015 When sampling textures other than \c SCREEN_TEXTURE and \c DEPTH_TEXTURE,
1016 or when \c FRAGCOORD is used to calculate the texture coordinate (which
1017 would be the typical use case for accessing the screen and depth textures),
1018 such an adjustment is not necessary.
1019
1020 \li float \c NDC_Y_UP - The value is \c 1 when the Y axis points up in
1021 normalized device coordinate space, and \c{-1} when the Y axis points down.
1022 Y pointing down is the case when rendering happens with Vulkan. Most
1023 materials do not need to be concerned by this, but being able to branch
1024 based on this can become useful in certain advanced use cases.
1025
1026 \li float \c NEAR_CLIP_VALUE - The value is \c -1 for when the clipping plane
1027 range's starts at \c -1 and goes to \c 1. This is true when using OpenGL for
1028 rendering. For other rendering backends the value of this property will be
1029 \c 0 meaning the clipping plane range is \c 0 to \c 1. This value is useful
1030 with certain techniques involving the \c DEPTH_TEXTURE
1031
1032 For example, the following fragment shader demonstrates a technique for
1033 reconstructing the position of a value from the depth buffer to determine
1034 the distance from the current position being rendered. When used in
1035 combination with \c INVERSE_PROJECTION_MATRIX the value of depth needs
1036 to be in normalized device coordinates so it is important to make sure that
1037 the range of depth value reflects that. When the \c NEAR_CLIP_VALUE is
1038 \c -1 then the depth value gets scaled to be between \c -1 and \c 1.
1039
1040 \badcode
1041 void MAIN() {
1042 vec2 screen_uv = FRAGCOORD.xy / vec2(textureSize(SCREEN_TEXTURE, 0));
1043 float depth = texture(DEPTH_TEXTURE, screen_uv).r;
1044
1045 if (NEAR_CLIP_VALUE < 0.0) // effectively: if opengl
1046 depth = depth * 2.0 - 1.0;
1047
1048 vec4 unproject = INVERSE_PROJECTION_MATRIX * vec4(screen_uv, depth, 1.0);
1049 depth = (unproject.xyz / unproject.w).z;
1050 float viewVectorZ = (VIEW_MATRIX * vec4(VAR_WORLD_POSITION, 1.0)).z;
1051 depth = viewVectorZ - depth;
1052
1053 BASE_COLOR = vec4(depth, depth, depth, 1.0);
1054 }
1055 \endcode
1056
1057 \li float \c IBL_EXPOSE - The amount of light emitted by the light probe.
1058 It comes from \l {SceneEnvironment::probeExposure}.
1059 \badcode
1060 DIFFUSE += AO_FACTOR * IBL_EXPOSE * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, NORMAL, IBL_MAXMIPMAP).rgb;
1061 \endcode
1062
1063 \li float \c IBL_HORIZON - The horizontal cut-off value of reflections from
1064 the lower half environment. It comes from \l {SceneEnvironment::probeHorizon}
1065 {Horizon Cut-Off} but remapped to [-1, 0).
1066 \badcode
1067 vec3 diffuse += AO_FACTOR * IBL_EXPOSE * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, NORMAL, IBL_MAXMIPMAP).rgb;
1068 if (IBL_HORIZON > -1.0) {
1069 float ctr = 0.5 + 0.5 * IBL_HORIZON;
1070 float vertWt = smoothstep(ctr * 0.25, ctr + 0.25, NORMAL.y);
1071 float wtScaled = mix(1.0, vertWt, IBL_HORIZON + 1.0);
1072 diffuse *= wtScaled;
1073 }
1074 \endcode
1075
1076 \li float \c IBL_MAXMIPMAP - The maximum mipmap level of IBL_TEXTURE.
1077
1078 \endlist
1079
1080 \section2 Instancing
1081
1082 When doing instanced rendering, some of the keywords above do not apply.
1083 The following keywords are only available with instancing:
1084
1085 \list
1086 \li \c INSTANCE_MODEL_MATRIX -> mat4, replacement for \c MODEL_MATRIX, including the instancing transformation.
1087 \li \c INSTANCE_MODELVIEWPROJECTION_MATRIX -> mat4, replacement for \c MODELVIEWPROJECTION_MATRIX, including the instancing transformation.
1088 \li \c INSTANCE_COLOR -> vec4, the instance color: to be combined with \c {COLOR}.
1089 \li \c INSTANCE_DATA -> vec4, instance custom data.
1090 \li \c INSTANCE_INDEX -> int, the instance number, and index into the instancing table.
1091 \endlist
1092
1093 \section1 Screen, depth, and other textures
1094
1095 The rendering pipeline can expose a number of textures to the custom
1096 material shaders with content from special render passes. This applies both
1097 to shaded and unshaded custom materials.
1098
1099 For example, a shader may want access to a depth texture that contains the
1100 depth buffer contents for the opaque objects in the scene. This is achieved
1101 by sampling \c DEPTH_TEXTURE. Such a texture is not normally generated,
1102 unless there is a real need for it. Therefore, the presence of the
1103 following keywords in the vertex or fragment shader also acts as a toggle
1104 for opting in to the - potentially expensive - passes for generating the
1105 texture in question. (of course, it could be that some of these become
1106 already enabled due to other settings, such as the ambient occlusion
1107 parameters in SceneEnvironment or due to a post-processing effect relying
1108 on the depth texture, in which case the textures in question are generated
1109 regardless of the custom material and so sampling these special textures in
1110 the material comes at no extra cost apart from the texture access itself)
1111
1112 \list
1113
1114 \li \c SCREEN_TEXTURE - When present, a texture (\c sampler2D or \c
1115 sampler2DArray) with the color buffer from a rendering pass containing the
1116 contents of the scene excluding any transparent materials or any materials
1117 also using the SCREEN_TEXTURE is exposed to the shader under this name. The
1118 texture can be used for techniques that require the contents of the
1119 framebuffer they are being rendered to. The SCREEN_TEXTURE texture uses the
1120 same clear mode as the View3D. The size of these textures matches the size
1121 of the View3D in pixels. For example, a fragment shader could contain the
1122 following:
1123 \badcode
1124 vec2 uv = FRAGCOORD.xy / vec2(textureSize(SCREEN_TEXTURE, 0));
1125 vec2 displace = vec2(0.1);
1126 vec4 c = texture(SCREEN_TEXTURE, uv + displace);
1127 \endcode
1128
1129 Be aware that using \c SCREEN_TEXTURE requires appropriate, conscious
1130 design of the scene. Objects using such materials have to be positioned
1131 carefully, typically above all other objects that are expected to be
1132 visible in the texture. Objects that employ semi-transparency in some form
1133 are never part of the \c SCREEN_TEXTURE. Often \c SCREEN_TEXTURE will be
1134 used in combination with \c BASE_COLOR in \c MAIN. For example, the
1135 following custom fragment shader applies an emboss effect, while keeping
1136 fragments not touched by opaque objects transparent. This assumes that the
1137 object with the material is placed in the front, and that it has blending
1138 enabled. \badcode
1139 void MAIN()
1140 {
1141 vec2 size = vec2(textureSize(SCREEN_TEXTURE, 0));
1142 vec2 uv = FRAGCOORD.xy / size;
1143
1144 // basic emboss effect
1145 vec2 d = vec2(1.0 / size.x, 1.0 / size.y);
1146 vec4 diff = texture(SCREEN_TEXTURE, uv + d) - texture(SCREEN_TEXTURE, uv - d);
1147 float c = (diff.x + diff.y + diff.z) + 0.5;
1148
1149 float alpha = texture(SCREEN_TEXTURE, uv).a;
1150 BASE_COLOR = vec4(vec3(c), alpha);
1151 }
1152 \endcode
1153 With \l{Multiview Rendering}{multiview rendering}, \c SCREEN_TEXTURE is a \c
1154 sampler2DArray. Use \c VIEW_INDEX to select the layer to use. For VR/AR
1155 applications that wish to support both types of rendering, the portable
1156 approach is the following:
1157 \badcode
1158 #if QSHADER_VIEW_COUNT >= 2
1159 vec4 c = texture(SCREEN_TEXTURE, vec3(uv, VIEW_INDEX));
1160 #else
1161 vec4 c = texture(SCREEN_TEXTURE, uv);
1162 #endif
1163 \endcode
1164
1165 \li \c SCREEN_MIP_TEXTURE - Identical to \c SCREEN_TEXTURE in most ways,
1166 the difference being that this texture has mipmaps generated. This can be
1167 an expensive feature performance-wise, depending on the screen size, and
1168 due to having to generate the mipmaps every time the scene is rendered.
1169 Therefore, prefer using \c SCREEN_TEXTURE always, unless a technique
1170 relying on the texture mip levels (e.g. using \c textureLod in the shader)
1171 is implemented by the custom material.
1172
1173 \li \c DEPTH_TEXTURE - When present, a texture (\c sampler2D or \c
1174 sampler2DArray) with the (non-linearized) depth buffer contents is exposed
1175 to the shader under this name. Only opaque objects are included.
1176 For example, a fragment shader could contain the following: \badcode
1177 ivec2 dtSize = textureSize(DEPTH_TEXTURE, 0);
1178 vec2 dtUV = (FRAGCOORD.xy) / vec2(dtSize);
1179 vec4 depthSample = texture(DEPTH_TEXTURE, dtUV);
1180 float zNear = CAMERA_PROPERTIES.x;
1181 float zFar = CAMERA_PROPERTIES.y;
1182 float zRange = zFar - zNear;
1183 float z_n = 2.0 * depthSample.r - 1.0;
1184 float d = 2.0 * zNear * zFar / (zFar + zNear - z_n * zRange);
1185 d /= zFar;
1186 \endcode
1187 With \l{Multiview Rendering}{multiview rendering}, \c DEPTH_TEXTURE is a \c
1188 sampler2DArray. Use \c VIEW_INDEX to select the layer to use. For VR/AR
1189 applications that wish to support both types of rendering, the portable
1190 approach is the following:
1191 \badcode
1192 #if QSHADER_VIEW_COUNT >= 2
1193 vec4 depthSample = texture(DEPTH_TEXTURE, vec3(uv, VIEW_INDEX));
1194 #else
1195 vec4 depthSample = texture(DEPTH_TEXTURE, uv);
1196 #endif
1197 \endcode
1198
1199 \li \c AO_TEXTURE - When present and screen space ambient occlusion is
1200 enabled (meaning when the AO strength and distance are both non-zero) in
1201 SceneEnvironment, the SSAO texture (\c sampler2D or \c sampler2DArray) is
1202 exposed to the shader under this name. Sampling this texture can be useful
1203 in unshaded materials. Shaded materials have ambient occlusion support built
1204 in. This means that the ambient occlusion factor is taken into account
1205 automatically. Whereas in a fragment shader for an unshaded material one
1206 could write the following to achieve the same: \badcode
1207 ivec2 aoSize = textureSize(AO_TEXTURE, 0);
1208 vec2 aoUV = (FRAGCOORD.xy) / vec2(aoSize);
1209 float aoFactor = texture(AO_TEXTURE, aoUV).x;
1210 \endcode
1211 With \l{Multiview Rendering}{multiview rendering}, \c AO_TEXTURE is a \c
1212 sampler2DArray. Use \c VIEW_INDEX to select the layer to use. For VR/AR
1213 applications that wish to support both types of rendering, the portable
1214 approach is the following:
1215 \badcode
1216 #if QSHADER_VIEW_COUNT >= 2
1217 ivec2 aoSize = textureSize(AO_TEXTURE, 0).xy;
1218 vec2 aoUV = (FRAGCOORD.xy) / vec2(aoSize);
1219 float aoFactor = texture(AO_TEXTURE, vec3(aoUV, VIEW_INDEX)).x;
1220 #else
1221 ivec2 aoSize = textureSize(AO_TEXTURE, 0);
1222 vec2 aoUV = (FRAGCOORD.xy) / vec2(aoSize);
1223 float aoFactor = texture(AO_TEXTURE, aoUV).x;
1224 #endif
1225 \endcode
1226
1227 \li \c IBL_TEXTURE - It will not enable any special rendering pass, but it can
1228 be used when the material has \l {Material::lightProbe} or the model is in the scope of
1229 \l {SceneEnvironment::lightProbe}.
1230
1231 \badcode
1232 void IBL_PROBE()
1233 {
1234 DIFFUSE += AO_FACTOR * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, NORMAL, IBL_MAXMIPMAP).rgb;
1235 }
1236 \endcode
1237
1238 \li \c VIEW_INDEX - When used in the custom shader code, this is a
1239 (non-interpolated) uint variable. When \l{Multiview Rendering}{multiview
1240 rendering} is not used, the value is always 0. With multiview rendering, the
1241 value is the current view index (e.g., gl_ViewIndex). Useful in particular
1242 in combination with \c DEPTH_TEXTURE and similar when multiview rendering is
1243 enabled.
1244
1245 \endlist
1246
1247 \sa {Qt Quick 3D - Custom Shaders Example}, {Qt Quick 3D - Custom Materials Example}, {Programmable Materials, Effects, Geometry, and Texture data}
1248*/
1249
1250/*!
1251 \qmlproperty url CustomMaterial::vertexShader
1252
1253 Specfies the file with the snippet of custom vertex shader code.
1254
1255 The value is a URL and must either be a local file or use the qrc scheme to
1256 access files embedded via the Qt resource system. Relative file paths
1257 (without a scheme) are also accepted, in which case the file is treated as
1258 relative to the component (the \c{.qml} file).
1259
1260 \warning Shader snippets are assumed to be trusted content. Application
1261 developers are advised to carefully consider the potential implications
1262 before allowing the loading of user-provided content that is not part of the
1263 application.
1264
1265 \sa fragmentShader
1266*/
1267
1268/*!
1269 \qmlproperty url CustomMaterial::fragmentShader
1270
1271 Specfies the file with the snippet of custom fragment shader code.
1272
1273 The value is a URL and must either be a local file or use the qrc scheme to
1274 access files embedded via the Qt resource system. Relative file paths
1275 (without a scheme) are also accepted, in which case the file is treated as
1276 relative to the component (the \c{.qml} file).
1277
1278 \warning Shader snippets are assumed to be trusted content. Application
1279 developers are advised to carefully consider the potential implications
1280 before allowing the loading of user-provided content that is not part of the
1281 application.
1282
1283 \sa vertexShader
1284*/
1285
1286/*!
1287 \qmlproperty enumeration CustomMaterial::shadingMode
1288 Specifies the type of the material. The default value is Shaded.
1289
1290 \value CustomMaterial.Unshaded
1291 \value CustomMaterial.Shaded
1292*/
1293
1294/*!
1295 \qmlproperty bool CustomMaterial::alwaysDirty
1296 Specifies that the material state is always dirty, which indicates that the material needs
1297 to be refreshed every time it is used by the QtQuick3D.
1298*/
1299
1300/*!
1301 \qmlproperty enumeration CustomMaterial::sourceBlend
1302
1303 Specifies the source blend factor. The default value is \c
1304 CustomMaterial.NoBlend.
1305
1306 \value CustomMaterial.NoBlend
1307 \value CustomMaterial.Zero
1308 \value CustomMaterial.One
1309 \value CustomMaterial.SrcColor
1310 \value CustomMaterial.OneMinusSrcColor
1311 \value CustomMaterial.DstColor
1312 \value CustomMaterial.OneMinusDstColor
1313 \value CustomMaterial.SrcAlpha
1314 \value CustomMaterial.OneMinusSrcAlpha
1315 \value CustomMaterial.DstAlpha
1316 \value CustomMaterial.OneMinusDstAlpha
1317 \value CustomMaterial.ConstantColor
1318 \value CustomMaterial.OneMinusConstantColor
1319 \value CustomMaterial.ConstantAlpha
1320 \value CustomMaterial.OneMinusConstantAlpha
1321 \value CustomMaterial.SrcAlphaSaturate
1322
1323 \note Both \l sourceBlend and \l destinationBlend needs to be set to a non-default
1324 value before blending is enabled.
1325
1326 \sa destinationBlend
1327*/
1328
1329/*!
1330 \qmlproperty enumeration CustomMaterial::destinationBlend
1331
1332 Specifies the destination blend factor. The default value is \c
1333 CustomMaterial.NoBlend.
1334
1335 \value CustomMaterial.NoBlend
1336 \value CustomMaterial.Zero
1337 \value CustomMaterial.One
1338 \value CustomMaterial.SrcColor
1339 \value CustomMaterial.OneMinusSrcColor
1340 \value CustomMaterial.DstColor
1341 \value CustomMaterial.OneMinusDstColor
1342 \value CustomMaterial.SrcAlpha
1343 \value CustomMaterial.OneMinusSrcAlpha
1344 \value CustomMaterial.DstAlpha
1345 \value CustomMaterial.OneMinusDstAlpha
1346 \value CustomMaterial.ConstantColor
1347 \value CustomMaterial.OneMinusConstantColor
1348 \value CustomMaterial.ConstantAlpha
1349 \value CustomMaterial.OneMinusConstantAlpha
1350 \value CustomMaterial.SrcAlphaSaturate
1351
1352 \note Both \l sourceBlend and \l destinationBlend needs to be set to a non-default
1353 value before blending is enabled.
1354
1355 \sa sourceBlend
1356*/
1357
1358/*!
1359 \qmlproperty real CustomMaterial::lineWidth
1360
1361 This property determines the width of the lines rendered, when the geometry
1362 is using a primitive type of lines or line strips. The default value is
1363 1.0. This property is not relevant when rendering other types of geometry,
1364 such as, triangle meshes.
1365
1366 \warning Line widths other than 1 may not be suported at run time,
1367 depending on the underlying graphics API. When that is the case, the
1368 request to change the width is ignored. For example, none of the following
1369 can be expected to support wide lines: Direct3D, Metal, OpenGL with core
1370 profile contexts.
1371
1372 \note Unlike the line width, the value of which is part of the graphics
1373 pipeline object, the point size for geometries with a topology of points is
1374 controlled by the vertex shader (when supported), and has therefore no
1375 corresponding QML property.
1376*/
1377
1378/*!
1379 \qmlproperty enumeration CustomMaterial::sourceAlphaBlend
1380 \since 6.7
1381
1382 Specifies the source alpha blend factor. The default value is \c
1383 CustomMaterial.NoBlend. This value is only actively used if \l sourceBlend and
1384 \l destinationBlend is set to a non-default value.
1385
1386 \value CustomMaterial.NoBlend
1387 \value CustomMaterial.Zero
1388 \value CustomMaterial.One
1389 \value CustomMaterial.SrcColor
1390 \value CustomMaterial.OneMinusSrcColor
1391 \value CustomMaterial.DstColor
1392 \value CustomMaterial.OneMinusDstColor
1393 \value CustomMaterial.SrcAlpha
1394 \value CustomMaterial.OneMinusSrcAlpha
1395 \value CustomMaterial.DstAlpha
1396 \value CustomMaterial.OneMinusDstAlpha
1397 \value CustomMaterial.ConstantColor
1398 \value CustomMaterial.OneMinusConstantColor
1399 \value CustomMaterial.ConstantAlpha
1400 \value CustomMaterial.OneMinusConstantAlpha
1401 \value CustomMaterial.SrcAlphaSaturate
1402
1403 \note For backwards compatibility purposes, when left to its default value,
1404 will be assigned the same value as \l sourceBlend when \l sourceBlend and
1405 \l destinationBlend is set to non-default values.
1406
1407 \sa sourceBlend
1408*/
1409
1410/*!
1411 \qmlproperty enumeration CustomMaterial::destinationAlphaBlend
1412 \since 6.7
1413
1414 Specifies the destination alpha blend factor. The default value is \c
1415 CustomMaterial.NoBlend. This value is only actively used if \l sourceBlend and
1416 \l destinationBlend is set to a non-default value.
1417
1418 \value CustomMaterial.NoBlend
1419 \value CustomMaterial.Zero
1420 \value CustomMaterial.One
1421 \value CustomMaterial.SrcColor
1422 \value CustomMaterial.OneMinusSrcColor
1423 \value CustomMaterial.DstColor
1424 \value CustomMaterial.OneMinusDstColor
1425 \value CustomMaterial.SrcAlpha
1426 \value CustomMaterial.OneMinusSrcAlpha
1427 \value CustomMaterial.DstAlpha
1428 \value CustomMaterial.OneMinusDstAlpha
1429 \value CustomMaterial.ConstantColor
1430 \value CustomMaterial.OneMinusConstantColor
1431 \value CustomMaterial.ConstantAlpha
1432 \value CustomMaterial.OneMinusConstantAlpha
1433 \value CustomMaterial.SrcAlphaSaturate
1434
1435 \note For backwards compatibility purposes, when left to its default value,
1436 will be assigned the same value as \l destinationBlend when \l sourceBlend and
1437 \l destinationBlend is set to non-default values.
1438
1439 \sa destinationBlend
1440*/
1441
1442static inline QRhiGraphicsPipeline::BlendFactor toRhiBlendFactor(QQuick3DCustomMaterial::BlendMode mode)
1443{
1444 switch (mode) {
1445 case QQuick3DCustomMaterial::BlendMode::Zero:
1446 return QRhiGraphicsPipeline::Zero;
1447 case QQuick3DCustomMaterial::BlendMode::One:
1448 return QRhiGraphicsPipeline::One;
1449 case QQuick3DCustomMaterial::BlendMode::SrcColor:
1450 return QRhiGraphicsPipeline::SrcColor;
1451 case QQuick3DCustomMaterial::BlendMode::OneMinusSrcColor:
1452 return QRhiGraphicsPipeline::OneMinusSrcColor;
1453 case QQuick3DCustomMaterial::BlendMode::DstColor:
1454 return QRhiGraphicsPipeline::DstColor;
1455 case QQuick3DCustomMaterial::BlendMode::OneMinusDstColor:
1456 return QRhiGraphicsPipeline::OneMinusDstColor;
1457 case QQuick3DCustomMaterial::BlendMode::SrcAlpha:
1458 return QRhiGraphicsPipeline::SrcAlpha;
1459 case QQuick3DCustomMaterial::BlendMode::OneMinusSrcAlpha:
1460 return QRhiGraphicsPipeline::OneMinusSrcAlpha;
1461 case QQuick3DCustomMaterial::BlendMode::DstAlpha:
1462 return QRhiGraphicsPipeline::DstAlpha;
1463 case QQuick3DCustomMaterial::BlendMode::OneMinusDstAlpha:
1464 return QRhiGraphicsPipeline::OneMinusDstAlpha;
1465 case QQuick3DCustomMaterial::BlendMode::ConstantColor:
1466 return QRhiGraphicsPipeline::ConstantColor;
1467 case QQuick3DCustomMaterial::BlendMode::OneMinusConstantColor:
1468 return QRhiGraphicsPipeline::OneMinusConstantColor;
1469 case QQuick3DCustomMaterial::BlendMode::ConstantAlpha:
1470 return QRhiGraphicsPipeline::ConstantAlpha;
1471 case QQuick3DCustomMaterial::BlendMode::OneMinusConstantAlpha:
1472 return QRhiGraphicsPipeline::OneMinusConstantAlpha;
1473 case QQuick3DCustomMaterial::BlendMode::SrcAlphaSaturate:
1474 return QRhiGraphicsPipeline::SrcAlphaSaturate;
1475 default:
1476 return QRhiGraphicsPipeline::One;
1477 }
1478}
1479
1480QQuick3DCustomMaterial::QQuick3DCustomMaterial(QQuick3DObject *parent)
1481 : QQuick3DMaterial(*(new QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::CustomMaterial)), parent)
1482{
1483}
1484
1485QQuick3DCustomMaterial::~QQuick3DCustomMaterial() {}
1486
1487QQuick3DCustomMaterial::BlendMode QQuick3DCustomMaterial::srcBlend() const
1488{
1489 return m_srcBlend;
1490}
1491
1492void QQuick3DCustomMaterial::setSrcBlend(BlendMode mode)
1493{
1494 if (m_srcBlend == mode)
1495 return;
1496
1497 m_srcBlend = mode;
1498 update();
1499 emit srcBlendChanged();
1500}
1501
1502QQuick3DCustomMaterial::BlendMode QQuick3DCustomMaterial::dstBlend() const
1503{
1504 return m_dstBlend;
1505}
1506
1507void QQuick3DCustomMaterial::setDstBlend(BlendMode mode)
1508{
1509 if (m_dstBlend == mode)
1510 return;
1511
1512 m_dstBlend = mode;
1513 update();
1514 emit dstBlendChanged();
1515}
1516
1517QQuick3DCustomMaterial::BlendMode QQuick3DCustomMaterial::srcAlphaBlend() const
1518{
1519 return m_srcAlphaBlend;
1520}
1521
1522void QQuick3DCustomMaterial::setSrcAlphaBlend(QQuick3DCustomMaterial::BlendMode mode)
1523{
1524 if (m_srcAlphaBlend == mode)
1525 return;
1526
1527 m_srcAlphaBlend = mode;
1528 update();
1529 emit srcAlphaBlendChanged();
1530}
1531
1532QQuick3DCustomMaterial::BlendMode QQuick3DCustomMaterial::dstAlphaBlend() const
1533{
1534 return m_dstAlphaBlend;
1535}
1536
1537void QQuick3DCustomMaterial::setDstAlphaBlend(QQuick3DCustomMaterial::BlendMode mode)
1538{
1539 if (m_dstAlphaBlend == mode)
1540 return;
1541
1542 m_dstAlphaBlend = mode;
1543 update();
1544 emit dstAlphaBlendChanged();
1545}
1546
1547QQuick3DCustomMaterial::ShadingMode QQuick3DCustomMaterial::shadingMode() const
1548{
1549 return m_shadingMode;
1550}
1551
1552void QQuick3DCustomMaterial::setShadingMode(ShadingMode mode)
1553{
1554 if (m_shadingMode == mode)
1555 return;
1556
1557 m_shadingMode = mode;
1558 markDirty(that&: *this, type: Dirty::ShaderSettingsDirty);
1559 emit shadingModeChanged();
1560}
1561
1562QUrl QQuick3DCustomMaterial::vertexShader() const
1563{
1564 return m_vertexShader;
1565}
1566
1567void QQuick3DCustomMaterial::setVertexShader(const QUrl &url)
1568{
1569 if (m_vertexShader == url)
1570 return;
1571
1572 m_vertexShader = url;
1573 markDirty(that&: *this, type: Dirty::ShaderSettingsDirty);
1574 emit vertexShaderChanged();
1575}
1576
1577QUrl QQuick3DCustomMaterial::fragmentShader() const
1578{
1579 return m_fragmentShader;
1580}
1581
1582void QQuick3DCustomMaterial::setFragmentShader(const QUrl &url)
1583{
1584 if (m_fragmentShader == url)
1585 return;
1586
1587 m_fragmentShader = url;
1588 markDirty(that&: *this, type: Dirty::ShaderSettingsDirty);
1589 emit fragmentShaderChanged();
1590}
1591
1592
1593QString QQuick3DCustomMaterial::vertexShaderCode() const
1594{
1595 return m_vertexShaderCode;
1596}
1597
1598void QQuick3DCustomMaterial::setVertexShaderCode(const QString &code)
1599{
1600 if (m_vertexShaderCode == code)
1601 return;
1602
1603 m_vertexShaderCode = code;
1604 markDirty(that&: *this, type: Dirty::ShaderSettingsDirty);
1605 emit vertexShaderCodeChanged();
1606}
1607
1608QString QQuick3DCustomMaterial::fragmentShaderCode() const
1609{
1610 return m_fragmentShaderCode;
1611}
1612
1613void QQuick3DCustomMaterial::setFragmentShaderCode(const QString &code)
1614{
1615 if (m_fragmentShaderCode == code)
1616 return;
1617
1618 m_fragmentShaderCode = code;
1619 markDirty(that&: *this, type: Dirty::ShaderSettingsDirty);
1620 emit fragmentShaderCodeChanged();
1621}
1622
1623float QQuick3DCustomMaterial::lineWidth() const
1624{
1625 return m_lineWidth;
1626}
1627
1628void QQuick3DCustomMaterial::setLineWidth(float width)
1629{
1630 if (qFuzzyCompare(p1: m_lineWidth, p2: width))
1631 return;
1632 m_lineWidth = width;
1633 update();
1634 emit lineWidthChanged();
1635}
1636
1637void QQuick3DCustomMaterial::markAllDirty()
1638{
1639 m_dirtyAttributes |= Dirty::AllDirty;
1640 QQuick3DMaterial::markAllDirty();
1641}
1642
1643void QQuick3DCustomMaterial::markDirty(QQuick3DCustomMaterial &that, Dirty type)
1644{
1645 if (!(that.m_dirtyAttributes & quint32(type))) {
1646 that.m_dirtyAttributes |= quint32(type);
1647 that.update();
1648 }
1649}
1650
1651bool QQuick3DCustomMaterial::alwaysDirty() const
1652{
1653 return m_alwaysDirty;
1654}
1655
1656void QQuick3DCustomMaterial::setAlwaysDirty(bool alwaysDirty)
1657{
1658 if (m_alwaysDirty == alwaysDirty)
1659 return;
1660
1661 m_alwaysDirty = alwaysDirty;
1662 update();
1663 emit alwaysDirtyChanged();
1664}
1665
1666static void setCustomMaterialFlagsFromShader(QSSGRenderCustomMaterial *material, const QSSGCustomShaderMetaData &meta)
1667{
1668 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesScreenTexture))
1669 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ScreenTexture, on: true);
1670 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesScreenMipTexture))
1671 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ScreenMipTexture, on: true);
1672 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesDepthTexture))
1673 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::DepthTexture, on: true);
1674 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesAoTexture))
1675 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::AoTexture, on: true);
1676 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesProjectionMatrix))
1677 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix, on: true);
1678 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesInverseProjectionMatrix))
1679 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix, on: true);
1680 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesVarColor))
1681 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::VarColor, on: true);
1682 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesIblOrientation))
1683 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::IblOrientation, on: true);
1684 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesLightmap))
1685 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Lightmap, on: true);
1686 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesSkinning))
1687 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Skinning, on: true);
1688 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesMorphing))
1689 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Morphing, on: true);
1690 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesViewIndex))
1691 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ViewIndex, on: true);
1692 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesClearcoat))
1693 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Clearcoat, on: true);
1694 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesClearcoatFresnelScaleBias))
1695 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ClearcoatFresnelScaleBias, on: true);
1696 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesFresnelScaleBias))
1697 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::FresnelScaleBias, on: true);
1698 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesTransmission)) {
1699 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Transmission, on: true);
1700 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ScreenTexture, on: true);
1701 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ScreenMipTexture, on: true);
1702 }
1703
1704 // vertex only
1705 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::OverridesPosition))
1706 material->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::OverridesPosition, on: true);
1707
1708 // fragment only
1709 if (meta.flags.testFlag(flag: QSSGCustomShaderMetaData::UsesSharedVars))
1710 material->m_usesSharedVariables = true;
1711}
1712
1713static QByteArray prepareCustomShader(QSSGRenderCustomMaterial *customMaterial,
1714 const QSSGShaderCustomMaterialAdapter::StringPairList &uniforms,
1715 const QByteArray &snippet,
1716 QSSGShaderCache::ShaderType shaderType,
1717 QSSGCustomShaderMetaData &meta,
1718 bool multiViewCompatible)
1719{
1720 if (snippet.isEmpty())
1721 return QByteArray();
1722
1723 QByteArray sourceCode = snippet;
1724 QByteArray buf;
1725 auto result = QSSGShaderCustomMaterialAdapter::prepareCustomShader(dst&: buf,
1726 shaderCode: sourceCode,
1727 type: shaderType,
1728 baseUniforms: uniforms,
1729 baseInputs: {},
1730 baseOutputs: {},
1731 multiViewCompatible);
1732 sourceCode = result.first;
1733 sourceCode.append(a: buf);
1734 meta = result.second;
1735 setCustomMaterialFlagsFromShader(material: customMaterial, meta);
1736 return sourceCode;
1737}
1738
1739QSSGRenderGraphObject *QQuick3DCustomMaterial::updateSpatialNode(QSSGRenderGraphObject *node)
1740{
1741 using namespace QSSGShaderUtils;
1742
1743 const auto &renderContext = QQuick3DObjectPrivate::get(item: this)->sceneManager->wattached->rci();
1744 if (!renderContext) {
1745 qWarning(msg: "QQuick3DCustomMaterial: No render context interface?");
1746 return nullptr;
1747 }
1748
1749 QSSGShaderCustomMaterialAdapter::StringPairList uniforms;
1750 QSSGRenderCustomMaterial *customMaterial = static_cast<QSSGRenderCustomMaterial *>(node);
1751 bool newBackendNode = false;
1752 bool shadersMayChange = false;
1753 if (!customMaterial) {
1754 customMaterial = new QSSGRenderCustomMaterial;
1755 newBackendNode = true;
1756 } else if (m_dirtyAttributes & ShaderSettingsDirty) {
1757 shadersMayChange = true;
1758 }
1759
1760 if (newBackendNode || shadersMayChange) {
1761 markAllDirty();
1762
1763 customMaterial->m_properties.clear();
1764 customMaterial->m_textureProperties.clear();
1765
1766 customMaterial->m_shadingMode = QSSGRenderCustomMaterial::ShadingMode(int(m_shadingMode));
1767
1768 QMetaMethod propertyDirtyMethod;
1769 const int idx = metaObject()->indexOfSlot(slot: "onPropertyDirty()");
1770 if (idx != -1)
1771 propertyDirtyMethod = metaObject()->method(index: idx);
1772
1773 const int propCount = metaObject()->propertyCount();
1774 int propOffset = metaObject()->propertyOffset();
1775
1776 // Custom materials can have multilayered inheritance structure, so find the actual propOffset
1777 const QMetaObject *superClass = metaObject()->superClass();
1778 while (superClass && qstrcmp(str1: superClass->className(), str2: "QQuick3DCustomMaterial") != 0) {
1779 propOffset = superClass->propertyOffset();
1780 superClass = superClass->superClass();
1781 }
1782
1783 using TextureInputProperty = QPair<QQuick3DShaderUtilsTextureInput *, const char *>;
1784 QVector<TextureInputProperty> textureProperties; // We'll deal with these later
1785
1786 for (int i = propOffset; i != propCount; ++i) {
1787 const auto property = metaObject()->property(index: i);
1788 if (Q_UNLIKELY(!property.isValid()))
1789 continue;
1790
1791 const auto name = property.name();
1792 QMetaType propType = property.metaType();
1793 QVariant propValue = property.read(obj: this);
1794 if (propType == QMetaType(QMetaType::QVariant))
1795 propType = propValue.metaType();
1796
1797 if (propType.id() >= QMetaType::User) {
1798 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
1799 if (QQuick3DShaderUtilsTextureInput *texture = property.read(obj: this).value<QQuick3DShaderUtilsTextureInput *>())
1800 textureProperties.push_back(t: {texture, name});
1801 }
1802 } else if (propType == QMetaType(QMetaType::QObjectStar)) {
1803 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(object: propValue.value<QObject *>()))
1804 textureProperties.push_back(t: {texture, name});
1805 } else {
1806 const auto type = uniformType(type: propType);
1807 if (type != QSSGRenderShaderValue::Unknown) {
1808 uniforms.append(t: { uniformTypeName(type: propType), name });
1809 customMaterial->m_properties.push_back(t: { name, propValue, uniformType(type: propType), i});
1810 if (newBackendNode) {
1811 // Track the property changes
1812 if (property.hasNotifySignal() && propertyDirtyMethod.isValid())
1813 connect(sender: this, signal: property.notifySignal(), receiver: this, method: propertyDirtyMethod);
1814 } // else already connected
1815 } else {
1816 // ### figure out how _not_ to warn when there are no dynamic
1817 // properties defined (because warnings like Blah blah objectName etc. are not helpful)
1818 //qWarning("No known uniform conversion found for effect property %s. Skipping", property.name());
1819 }
1820 }
1821 }
1822
1823 const auto processTextureProperty = [&](QQuick3DShaderUtilsTextureInput &texture, const QByteArray &name) {
1824 texture.name = name;
1825
1826 QSSGRenderCustomMaterial::TextureProperty textureData;
1827 textureData.texInput = &texture;
1828 textureData.name = name;
1829 textureData.shaderDataType = QSSGRenderShaderValue::Texture;
1830
1831 if (newBackendNode) {
1832 connect(sender: &texture, signal: &QQuick3DShaderUtilsTextureInput::enabledChanged, context: this, slot: &QQuick3DCustomMaterial::onTextureDirty);
1833 connect(sender: &texture, signal: &QQuick3DShaderUtilsTextureInput::textureChanged, context: this, slot: &QQuick3DCustomMaterial::onTextureDirty);
1834 } // else already connected
1835
1836 QQuick3DTexture *tex = texture.texture(); // may be null if the TextureInput has no 'texture' set
1837 if (tex && QQuick3DObjectPrivate::get(item: tex)->type == QQuick3DObjectPrivate::Type::ImageCube)
1838 uniforms.append(t: { QByteArrayLiteral("samplerCube"), textureData.name });
1839 else if (tex && tex->textureData() && tex->textureData()->depth() > 0)
1840 uniforms.append(t: { QByteArrayLiteral("sampler3D"), textureData.name });
1841 else
1842 uniforms.append(t: { QByteArrayLiteral("sampler2D"), textureData.name });
1843
1844 customMaterial->m_textureProperties.push_back(t: textureData);
1845 };
1846
1847 for (const auto &textureProperty : std::as_const(t&: textureProperties))
1848 processTextureProperty(*textureProperty.first, textureProperty.second);
1849
1850 if (customMaterial->incompleteBuildTimeObject || (m_dirtyAttributes & DynamicPropertiesDirty)) { // This object came from the shadergen tool
1851 const auto names = dynamicPropertyNames();
1852 for (const auto &name : names) {
1853 QVariant propValue = property(name: name.constData());
1854 QMetaType propType = propValue.metaType();
1855 if (propType == QMetaType(QMetaType::QVariant))
1856 propType = propValue.metaType();
1857
1858 if (propType.id() >= QMetaType::User) {
1859 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
1860 if (QQuick3DShaderUtilsTextureInput *texture = propValue.value<QQuick3DShaderUtilsTextureInput *>())
1861 textureProperties.push_back(t: {texture, name});
1862 }
1863 } else if (propType.id() == QMetaType::QObjectStar) {
1864 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(object: propValue.value<QObject *>()))
1865 textureProperties.push_back(t: {texture, name});
1866 } else {
1867 const auto type = uniformType(type: propType);
1868 if (type != QSSGRenderShaderValue::Unknown) {
1869 uniforms.append(t: { uniformTypeName(type: propType), name });
1870 customMaterial->m_properties.push_back(t: { name, propValue,
1871 uniformType(type: propType), -1 /* aka. dynamic property */});
1872 // We don't need to track property changes
1873 } else {
1874 // ### figure out how _not_ to warn when there are no dynamic
1875 // properties defined (because warnings like Blah blah objectName etc. are not helpful)
1876 qWarning(msg: "No known uniform conversion found for custom material property %s. Skipping", name.constData());
1877 }
1878 }
1879 }
1880
1881 for (const auto &property : std::as_const(t&: textureProperties))
1882 processTextureProperty(*property.first, property.second);
1883 }
1884
1885 const QQmlContext *context = qmlContext(this);
1886 QByteArray vertex;
1887 QByteArray fragment;
1888 QByteArray vertexProcessed[2];
1889 QSSGCustomShaderMetaData vertexMeta;
1890 QByteArray fragmentProcessed[2];
1891 QSSGCustomShaderMetaData fragmentMeta;
1892 QByteArray shaderPathKey("custom material --");
1893
1894 customMaterial->m_renderFlags = {};
1895
1896 if (!m_vertexShader.isEmpty())
1897 vertex = QSSGShaderUtils::resolveShader(fileUrl: m_vertexShader, context, shaderPathKey);
1898 else if (!m_vertexShaderCode.isEmpty())
1899 vertex = m_vertexShaderCode.toLatin1();
1900
1901 if (!m_fragmentShader.isEmpty())
1902 fragment = QSSGShaderUtils::resolveShader(fileUrl: m_fragmentShader, context, shaderPathKey);
1903 else if (!m_fragmentShaderCode.isEmpty())
1904 fragment = m_fragmentShaderCode.toLatin1();
1905
1906 // Multiview is a problem, because we will get a dedicated snippet after
1907 // preparation (the one that has [qt_viewIndex] added where it matters).
1908 // But at least the view count plays no role here on this level. So one
1909 // normal and one multiview "variant" is good enough.
1910
1911 vertexProcessed[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex] =
1912 prepareCustomShader(customMaterial, uniforms, snippet: vertex, shaderType: QSSGShaderCache::ShaderType::Vertex, meta&: vertexMeta, multiViewCompatible: false);
1913 fragmentProcessed[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex] =
1914 prepareCustomShader(customMaterial, uniforms, snippet: fragment, shaderType: QSSGShaderCache::ShaderType::Fragment, meta&: fragmentMeta, multiViewCompatible: false);
1915
1916 vertexProcessed[QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex] =
1917 prepareCustomShader(customMaterial, uniforms, snippet: vertex, shaderType: QSSGShaderCache::ShaderType::Vertex, meta&: vertexMeta, multiViewCompatible: true);
1918 fragmentProcessed[QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex] =
1919 prepareCustomShader(customMaterial, uniforms, snippet: fragment, shaderType: QSSGShaderCache::ShaderType::Fragment, meta&: fragmentMeta, multiViewCompatible: true);
1920
1921 // At this point we have snippets that look like this:
1922 // - the original code, with VARYING ... lines removed
1923 // - followed by QQ3D_SHADER_META block for uniforms
1924 // - followed by QQ3D_SHADER_META block for inputs/outputs
1925
1926 customMaterial->m_customShaderPresence = {};
1927 for (int i : { QSSGRenderCustomMaterial::RegularShaderPathKeyIndex, QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex }) {
1928 if (vertexProcessed[i].isEmpty() && fragmentProcessed[i].isEmpty())
1929 continue;
1930
1931 const QByteArray key = shaderPathKey + ':' + QCryptographicHash::hash(data: QByteArray(vertexProcessed[i] + fragmentProcessed[i]), method: QCryptographicHash::Algorithm::Sha1).toHex();
1932 // the processed snippet code is different for regular and multiview, so 'key' reflects that already
1933 customMaterial->m_shaderPathKey[i] = key;
1934 if (!vertexProcessed[i].isEmpty()) {
1935 customMaterial->m_customShaderPresence.setFlag(flag: QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Vertex);
1936 renderContext->shaderLibraryManager()->setShaderSource(inShaderPathKey: key, type: QSSGShaderCache::ShaderType::Vertex, inSource: vertexProcessed[i], meta: vertexMeta);
1937 }
1938 if (!fragmentProcessed[i].isEmpty()) {
1939 customMaterial->m_customShaderPresence.setFlag(flag: QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Fragment);
1940 renderContext->shaderLibraryManager()->setShaderSource(inShaderPathKey: key, type: QSSGShaderCache::ShaderType::Fragment, inSource: fragmentProcessed[i], meta: fragmentMeta);
1941 }
1942 }
1943 }
1944
1945 customMaterial->setAlwaysDirty(m_alwaysDirty);
1946 if (m_srcBlend != BlendMode::NoBlend && m_dstBlend != BlendMode::NoBlend) { // both must be set to something other than NoBlend
1947 customMaterial->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Blending, on: true);
1948 customMaterial->m_srcBlend = toRhiBlendFactor(mode: m_srcBlend);
1949 customMaterial->m_dstBlend = toRhiBlendFactor(mode: m_dstBlend);
1950 // alpha blending is only active if rgb blending is
1951 if (m_srcAlphaBlend != BlendMode::NoBlend && m_dstAlphaBlend != BlendMode::NoBlend) {
1952 customMaterial->m_srcAlphaBlend = toRhiBlendFactor(mode: m_srcAlphaBlend);
1953 customMaterial->m_dstAlphaBlend = toRhiBlendFactor(mode: m_dstAlphaBlend);
1954 } else {
1955 customMaterial->m_srcAlphaBlend = customMaterial->m_srcBlend;
1956 customMaterial->m_dstAlphaBlend = customMaterial->m_dstBlend;
1957 }
1958 } else {
1959 customMaterial->m_renderFlags.setFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Blending, on: false);
1960 }
1961 customMaterial->m_lineWidth = m_lineWidth;
1962
1963 QQuick3DMaterial::updateSpatialNode(node: customMaterial);
1964
1965 if (m_dirtyAttributes & Dirty::PropertyDirty) {
1966 for (auto &prop : customMaterial->m_properties) {
1967 auto p = metaObject()->property(index: prop.pid);
1968 if (Q_LIKELY(p.isValid()))
1969 prop.value = p.read(obj: this);
1970 }
1971 }
1972
1973 if (m_dirtyAttributes & Dirty::TextureDirty) {
1974 for (QSSGRenderCustomMaterial::TextureProperty &prop : customMaterial->m_textureProperties) {
1975 QQuick3DTexture *tex = prop.texInput->texture();
1976 if (tex) {
1977 if (prop.texInput->enabled)
1978 prop.texImage = tex->getRenderImage();
1979 else
1980 prop.texImage = nullptr;
1981 prop.minFilterType = tex->minFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
1982 : QSSGRenderTextureFilterOp::Linear;
1983 prop.magFilterType = tex->magFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
1984 : QSSGRenderTextureFilterOp::Linear;
1985 prop.mipFilterType = tex->generateMipmaps() ? (tex->mipFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
1986 : QSSGRenderTextureFilterOp::Linear)
1987 : QSSGRenderTextureFilterOp::None;
1988 prop.horizontalClampType = tex->horizontalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
1989 : (tex->horizontalTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
1990 : QSSGRenderTextureCoordOp::MirroredRepeat;
1991 prop.verticalClampType = tex->verticalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
1992 : (tex->verticalTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
1993 : QSSGRenderTextureCoordOp::MirroredRepeat;
1994 prop.zClampType = tex->depthTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
1995 : (tex->depthTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
1996 : QSSGRenderTextureCoordOp::MirroredRepeat;
1997 } else {
1998 prop.texImage = nullptr;
1999 }
2000
2001 if (tex != prop.lastConnectedTexture) {
2002 prop.lastConnectedTexture = tex;
2003 disconnect(prop.minFilterChangedConn);
2004 disconnect(prop.magFilterChangedConn);
2005 disconnect(prop.mipFilterChangedConn);
2006 disconnect(prop.horizontalTilingChangedConn);
2007 disconnect(prop.verticalTilingChangedConn);
2008 disconnect(prop.depthTilingChangedConn);
2009 if (tex) {
2010 prop.minFilterChangedConn = connect(sender: tex, signal: &QQuick3DTexture::minFilterChanged, context: this, slot: &QQuick3DCustomMaterial::onTextureDirty);
2011 prop.magFilterChangedConn = connect(sender: tex, signal: &QQuick3DTexture::magFilterChanged, context: this, slot: &QQuick3DCustomMaterial::onTextureDirty);
2012 prop.mipFilterChangedConn = connect(sender: tex, signal: &QQuick3DTexture::mipFilterChanged, context: this, slot: &QQuick3DCustomMaterial::onTextureDirty);
2013 prop.horizontalTilingChangedConn = connect(sender: tex, signal: &QQuick3DTexture::horizontalTilingChanged, context: this, slot: &QQuick3DCustomMaterial::onTextureDirty);
2014 prop.verticalTilingChangedConn = connect(sender: tex, signal: &QQuick3DTexture::verticalTilingChanged, context: this, slot: &QQuick3DCustomMaterial::onTextureDirty);
2015 prop.depthTilingChangedConn = connect(sender: tex, signal: &QQuick3DTexture::depthTilingChanged, context: this, slot: &QQuick3DCustomMaterial::onTextureDirty);
2016 }
2017 }
2018 }
2019 }
2020
2021 m_dirtyAttributes = 0;
2022
2023 return customMaterial;
2024}
2025
2026void QQuick3DCustomMaterial::itemChange(QQuick3DObject::ItemChange change, const QQuick3DObject::ItemChangeData &value)
2027{
2028 QQuick3DMaterial::itemChange(change, value);
2029 if (change == QQuick3DObject::ItemSceneChange) {
2030 if (auto sceneManager = value.sceneManager) {
2031 for (const auto &it : std::as_const(t&: m_dynamicTextureMaps)) {
2032 if (auto tex = it->texture())
2033 QQuick3DObjectPrivate::refSceneManager(obj: tex, mgr&: *sceneManager);
2034 }
2035 } else {
2036 for (const auto &it : std::as_const(t&: m_dynamicTextureMaps)) {
2037 if (auto tex = it->texture())
2038 QQuick3DObjectPrivate::derefSceneManager(obj: tex);
2039 }
2040 }
2041 }
2042}
2043
2044void QQuick3DCustomMaterial::onPropertyDirty()
2045{
2046 markDirty(that&: *this, type: Dirty::PropertyDirty);
2047 update();
2048}
2049
2050void QQuick3DCustomMaterial::onTextureDirty()
2051{
2052 markDirty(that&: *this, type: Dirty::TextureDirty);
2053 update();
2054}
2055
2056void QQuick3DCustomMaterial::setDynamicTextureMap(QQuick3DShaderUtilsTextureInput *textureMap)
2057{
2058 // There can only be one texture input per property, as the texture input is a combination
2059 // of the texture used and the uniform name!
2060 auto it = m_dynamicTextureMaps.constFind(value: textureMap);
2061
2062 if (it == m_dynamicTextureMaps.constEnd()) {
2063 // Track the object, if it's destroyed we need to remove it from our table.
2064 connect(sender: textureMap, signal: &QQuick3DShaderUtilsTextureInput::destroyed, context: this, slot: [this, textureMap]() {
2065 auto it = m_dynamicTextureMaps.constFind(value: textureMap);
2066 if (it != m_dynamicTextureMaps.constEnd())
2067 m_dynamicTextureMaps.erase(i: it);
2068 });
2069 m_dynamicTextureMaps.insert(value: textureMap);
2070
2071 update();
2072 }
2073}
2074
2075QT_END_NAMESPACE
2076

source code of qtquick3d/src/quick3d/qquick3dcustommaterial.cpp