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