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