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 SHEEN_COLOR, SHEEN_ROUGHNESS, ANISOTROPY_STRENGTH, ANISOTROPY_ROTATION,
456 IRIDESCENCE_FACTOR, IRIDESCENCE_IOR, IRIDESCENCE_THICKNESS, DISPERSION,
457 FRESNEL_BIAS, FRESNEL_SCALE, FRESNEL_POWER, IOR, \c TRANSMISSION_FACTOR,
458 THICKNESS_FACTOR, ATTENUATION_COLOR, ATTENUATION_DISTANCE and \c OCCLUSION_AMOUNT.
459
460 One common use case is to set the value of \c BASE_COLOR based on sampling
461 a texture, be it a base color map, \c SCREEN_TEXTURE, or some other kind of
462 source. This can be relevant and convenient especially when no custom light
463 processor functions are implemented. Setting \c{BASE_COLOR.a} to something
464 other than the default 1.0 allows affecting the final alpha value of the
465 fragment. (note that this will often require also enabling alpha blending
466 in \l sourceBlend and \l destinationBlend)
467
468 Another scenario is when there is no custom \c SPECULAR_LIGHT function
469 provided, or when there is a light probe set in the SceneEnvironment. The
470 metalness, roughness, and other values that affect the specular
471 contribution calculation can be set in \c MAIN to their desired custom
472 values.
473
474 The function can write to the following special variables. The values
475 written to these will typically be either hardcoded or be calculated based
476 on QML properties mapped to uniforms. The semantics are identical to
477 PrincipledMaterial.
478
479 \list
480
481 \li vec4 \c BASE_COLOR - The base color and material alpha value.
482 Corresponds to the \l{PrincipledMaterial::baseColor}{built-in materials'
483 color property}. When light processor functions are not implemented, it can
484 be convenient to set a custom base color in \c MAIN because that is then
485 taken into account in the default lighting calculations. The default value
486 is \c{vec4(1.0)}, meaning white with an alpha of 1.0. The alpha value
487 effects the final alpha of the fragment. The final alpha value is the
488 object (model) opacity multiplied by the base color alpha. When specifying
489 the value directly in shader code, not relying on uniform values exposed
490 from \b color properties in QML, be aware that it is up to the shader to
491 perform the sRGB to linear conversion, if needed. For example, assuming
492 a \c{vec3 color} and \c{float alpha} this can be achieved like the following:
493 \badcode
494 float C1 = 0.305306011;
495 vec3 C2 = vec3(0.682171111, 0.682171111, 0.682171111);
496 vec3 C3 = vec3(0.012522878, 0.012522878, 0.012522878);
497 BASE_COLOR = vec4(rgb * (rgb * (rgb * C1 + C2) + C3), alpha);
498 \endcode
499
500 \li vec3 \c EMISSIVE_COLOR - The color of self-illumination. Corresponds to
501 the built-in materials' emissive color which is combined by
502 \l {PrincipledMaterial::emissiveFactor}{built-in materials's emissiveFactor property}
503 and \l {PrincipledMaterial::emissiveMap}{built-in materials's emissiveMap property}.
504 The default value is \c{vec3(0.0)}. When specifying the value
505 directly in shader code, not relying on uniform values exposed from \b color
506 properties in QML, be aware that it is up to the shader to perform the sRGB
507 to linear conversion, if needed.
508
509 \li float \c IOR Specifies the index of refraction of the material. A typical value,
510 and also the default, is \c{1.5} as that is what a PrincipledMaterial would use.
511
512 \li float \c TRANSMISSION_FACTOR Specifies the amount of the translucency. A typical value,
513 would be \c{1.0} and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
514
515 \li float \c THICKNESS_FACTOR Specifies the amount of the translucent material thickness. A typical value,
516 would be \c{10.0} and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
517
518 \li vec3 \c ATTENUATION_COLOR Specifies the color shift of the translucent material by distance. A typical value,
519 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.
520
521 \li float \c ATTENUATION_DISTANCE Specifies the distance attenuation of color shift of the translucent material. A typical value,
522 would be \c{100.0} and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
523
524 \li float \c METALNESS Metalness amount in range 0.0 - 1.0. The default
525 value is 0. Must be set to a non-zero value to have effect.
526
527 \li float \c ROUGHNESS Roughness value in range 0.0 - 1.0. The default value is 0.
528
529 \li float \c CLEARCOAT_FRESNEL_POWER Specifies the fresnel power of the clearcoat layer. A typical value,
530 and also the default, is \c{5.0} as that is what a PrincipledMaterial would use.
531
532 \li float \c CLEARCOAT_FRESNEL_SCALE Specifies the fresnel scale of the clearcoat layer. A typical value,
533 and also the default, is \c{1.0} as that is what a PrincipledMaterial would use.
534
535 \li float \c CLEARCOAT_FRESNEL_BIAS Specifies the fresnel bias of the clearcoat layer. A typical value,
536 and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
537
538 \li float \c CLEARCOAT_AMOUNT Specifies the amount of the clearcoat layer on top of the material. A typical value,
539 would be \c{1.0} and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
540
541 \li float \c CLEARCOAT_ROUGHNESS Specifies the roughness of the clearcoat layer. A typical value,
542 would be \c{1.0} for fully blurred clearcoat layer and also the default, is \c{0.0} as that is
543 what a PrincipledMaterial would use.
544
545 \li vec3 \c CLEARCOAT_NORMAL - The clearcoat layer normal that comes from the vertex shader in world
546 space. While this property has the same initial value as \c VAR_WORLD_NORMAL,
547 only changing the value of \c CLEARCOAT_NORMAL will have an effect on clearcoat layer normal.
548
549 \li vec3 \c SHEEN_COLOR Specifies the color of the sheen layer, which also acts as its
550 strength: the default \c{vec3(0.0)} leaves the sheen layer disabled, as that is what a
551 PrincipledMaterial with the default sheenColor would do.
552 \note Available since Qt 6.13.
553
554 \li float \c SHEEN_ROUGHNESS Specifies the roughness of the sheen layer, where higher
555 values spread the highlight further from the silhouette. A typical value, and also the
556 default, is \c{0.0} as that is what a PrincipledMaterial would use.
557 \note Available since Qt 6.13.
558
559 \li float \c ANISOTROPY_STRENGTH Specifies how strongly the specular highlight is
560 stretched along the anisotropy direction. A typical value, and also the default, is
561 \c{0.0} as that is what a PrincipledMaterial would use, which leaves the highlight
562 round.
563 \note Available since Qt 6.13.
564
565 \li float \c ANISOTROPY_ROTATION Specifies the rotation of the anisotropy direction
566 within the tangent plane, in radians. Note that unlike the PrincipledMaterial property
567 of the same name this is in radians, since shader code works in radians throughout. The
568 default is \c{0.0}.
569 \note Available since Qt 6.13.
570
571 \li float \c IRIDESCENCE_FACTOR Specifies the strength of the thin film that produces
572 iridescence. A typical value, and also the default, is \c{0.0} as that is what a
573 PrincipledMaterial would use, which disables the effect.
574 \note Available since Qt 6.13.
575
576 \li float \c IRIDESCENCE_IOR Specifies the index of refraction of the thin film, which
577 together with its thickness decides the colors produced. The default is \c{1.3}, roughly
578 that of a soap film.
579 \note Available since Qt 6.13.
580
581 \li float \c IRIDESCENCE_THICKNESS Specifies the thickness of the thin film in
582 nanometers. Note that this is the final thickness rather than the minimum and maximum
583 pair a PrincipledMaterial interpolates between, since a custom shader can compute it
584 directly. The default is \c{400.0}.
585 \note Available since Qt 6.13.
586
587 \li float \c DISPERSION Specifies how much the index of refraction varies across
588 wavelengths, splitting refracted light into color fringes. Only has an effect together
589 with \c TRANSMISSION_FACTOR. A typical value, and also the default, is \c{0.0} as that
590 is what a PrincipledMaterial would use.
591 \note Available since Qt 6.13.
592
593 \li float \c FRESNEL_POWER Specifies the fresnel power. A typical value,
594 and also the default, is \c{5.0} as that is what a PrincipledMaterial would use.
595
596 \li float \c FRESNEL_SCALE Specifies the fresnel scale. A typical value,
597 and also the default, is \c{1.0} as that is what a PrincipledMaterial would use.
598
599 \li float \c FRESNEL_BIAS Specifies the fresnel bias. A typical value,
600 and also the default, is \c{0.0} as that is what a PrincipledMaterial would use.
601
602 \li float \c SPECULAR_AMOUNT Specular amount in range 0.0 - 1.0. The
603 default value is \c{0.5}, matching \l{PrincipledMaterial::specularAmount}. Must
604 be set to a non-zero value to have effect.
605
606 \li float \c OCCLUSION_AMOUNT Specifies the AO factor. A typical value,
607 and also the default, is \c{1.0} as that is what a PrincipledMaterial would use.
608
609 \li vec3 \c NORMAL - The normal that comes from the vertex shader in world
610 space. While this property has the same initial value as \c VAR_WORLD_NORMAL,
611 only changing the value of \c NORMAL will have an effect on lighting.
612
613 \li vec3 \c TANGENT - The tanget that comes from the vertex shader in world
614 space. This value is potentially adjusted for double-sidedness.
615
616 \li vec3 \c BINORMAL - The binormal that comes from the vertex shader in
617 world space. This value is potentially adjusted for double-sidedness.
618
619 \li vec2 \c UV0 - The first set of texture coordinates from the vertex shader.
620 This property is readonly in the fragment shader.
621
622 \li vec2 \c UV1 - The second set of texture coordinates from the vertex shader.
623 This property is readonly in the fragment shader.
624
625 \endlist
626
627 \note Unlike with unshaded materials, the fragment \c MAIN for a shaded
628 material has no direct control over \c FRAGCOLOR. Rather, it is the \c
629 DIFFUSE and \c SPECULAR values written in the light processor functions
630 that decide what the final fragment color is. When a light processor
631 function is not implemented, the relevant default shading calculations are
632 performed as with a PrincipledMaterial, taking \c BASE_COLOR and other
633 values from the list above into account.
634
635 An example of a simple, metallic custom material shader could be the following:
636 \badcode
637 void MAIN()
638 {
639 METALNESS = 1.0;
640 ROUGHNESS = 0.5;
641 FRESNEL_POWER = 5.0;
642 }
643 \endcode
644
645 Another example, where the base color and alpha are set by sampling a texture:
646 \badcode
647 VARYING vec2 texcoord;
648 void MAIN()
649 {
650 BASE_COLOR = texture(uColorMap, texcoord);
651 }
652 \endcode
653
654 \li \c{void AMBIENT_LIGHT()} When present, this function is called once for
655 each fragment. The task of the function is to add the total ambient
656 contribution to a writable special variable \c DIFFUSE. It can of course
657 choose to calculate a different value, or not touch \c DIFFUSE at all (to
658 ignore ambient lighting completely). When this function is not present at
659 all, the ambient contribution is calculated normally, like a
660 PrincipledMaterial would do.
661
662 The function can write to the following special variables:
663
664 \list
665
666 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per
667 fragment. The light processor functions will typically add (\c{+=}) to it,
668 since overwriting the value would lose the contribution from other lights.
669
670 \endlist
671
672 The function can read the following special variables, in addition to the
673 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
674 uniforms from the table above:
675
676 \list
677 \li vec3 \c TOTAL_AMBIENT_COLOR The total ambient contribution in the scene.
678 \endlist
679
680 Example:
681 \badcode
682 void AMBIENT_LIGHT()
683 {
684 DIFFUSE += TOTAL_AMBIENT_COLOR;
685 }
686 \endcode
687
688 \li \c{void DIRECTIONAL_LIGHT()} When present, this function is called for
689 each active directional light in the scene for each fragment. The task of
690 the function is to add the diffuse contribution to a writable special
691 variable \c DIFFUSE. The function can also choose to do nothing, in which
692 case diffuse contributions from directional lights are ignored. When the
693 function is not present at all, the diffuse contributions from directional
694 lights are accumulated normally, like a PrincipledMaterial would do.
695
696 The function can write to the following special variables:
697
698 \list
699
700 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per
701 fragment. The light processor functions will typically add (\c{+=}) to it,
702 since overwriting the value would lose the contribution from other lights.
703
704 \endlist
705
706 The function can read the following special variables, in addition to the
707 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
708 uniforms from the table above:
709
710 \list
711
712 \li vec3 \c LIGHT_COLOR Diffuse light color.
713 \li float \c SHADOW_CONTRIB Shadow contribution, or 1.0 if not shadowed at all or not reciving shadows.
714 \li vec3 \c TO_LIGHT_DIR Vector pointing towards the light source.
715 \li vec3 \c NORMAL The normal vector in world space.
716 \li vec4 \c BASE_COLOR The base color and material alpha value.
717 \li float \c METALNESS The Metalness amount.
718 \li float \c ROUGHNESS The Roughness amount.
719
720 \endlist
721
722 Example:
723 \badcode
724 void DIRECTIONAL_LIGHT()
725 {
726 DIFFUSE += LIGHT_COLOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
727 }
728 \endcode
729
730 \li \c{void POINT_LIGHT()} When present, this function is called for
731 each active point light in the scene for each fragment. The task of
732 the function is to add the diffuse contribution to a writable special
733 variable \c DIFFUSE. The function can also choose to do nothing, in which
734 case diffuse contributions from point lights are ignored. When the
735 function is not present at all, the diffuse contributions from point
736 lights are accumulated normally, like a PrincipledMaterial would do.
737
738 The function can write to the following special variables:
739
740 \list
741 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per fragment.
742 \endlist
743
744 The function can read the following special variables, in addition to the
745 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
746 uniforms from the table above:
747
748 \list
749 \li vec3 \c LIGHT_COLOR Diffuse light color.
750 \li float \c LIGHT_ATTENUATION Light attenuation.
751 \li float \c SHADOW_CONTRIB Shadow contribution, or 1.0 if not shadowed at all or not reciving shadows.
752 \li vec3 \c TO_LIGHT_DIR Vector pointing towards the light source.
753 \li vec3 \c NORMAL The normal vector in world space.
754 \li vec4 \c BASE_COLOR The base color and material alpha value.
755 \li float \c METALNESS The Metalness amount.
756 \li float \c ROUGHNESS The Roughness amount.
757 \endlist
758
759 Example:
760 \badcode
761 void POINT_LIGHT()
762 {
763 DIFFUSE += LIGHT_COLOR * LIGHT_ATTENUATION * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
764 }
765 \endcode
766
767 \li \c{void SPOT_LIGHT()} When present, this function is called for
768 each active spot light in the scene for each fragment. The task of
769 the function is to add the diffuse contribution to a writable special
770 variable \c DIFFUSE. The function can also choose to do nothing, in which
771 case diffuse contributions from spot lights are ignored. When the
772 function is not present at all, the diffuse contributions from spot
773 lights are accumulated normally, like a PrincipledMaterial would do.
774
775 The function can write to the following special variables:
776
777 \list
778 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per fragment.
779 \endlist
780
781 The function can read the following special variables, in addition to the
782 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
783 uniforms from the table above:
784
785 \list
786 \li vec3 \c LIGHT_COLOR Diffuse light color.
787 \li float \c LIGHT_ATTENUATION Light attenuation.
788 \li float \c SHADOW_CONTRIB Shadow contribution, or 1.0 if not shadowed at all or not reciving shadows.
789 \li vec3 \c TO_LIGHT_DIR Vector pointing towards the light source.
790 \li float \c SPOT_FACTOR Spot light factor.
791 \li vec3 \c NORMAL The normal vector in world space.
792 \li vec4 \c BASE_COLOR The base color and material alpha value.
793 \li float \c METALNESS The Metalness amount.
794 \li float \c ROUGHNESS The Roughness amount.
795 \endlist
796
797 Example:
798 \badcode
799 void SPOT_LIGHT()
800 {
801 DIFFUSE += LIGHT_COLOR * LIGHT_ATTENUATION * SPOT_FACTOR * SHADOW_CONTRIB * vec3(max(0.0, dot(normalize(VAR_WORLD_NORMAL), TO_LIGHT_DIR)));
802 }
803 \endcode
804
805 \li \c{void SPECULAR_LIGHT()} When present, this function is called for
806 each active light in the scene for each fragment. The task of the function
807 is to add the specular contribution to a writable special variable \c
808 SPECULAR. The function can also choose to do nothing, in which case
809 specular contributions from lights are ignored. When the function is not
810 present at all, the specular contributions from lights are accumulated
811 normally, like a PrincipledMaterial would do.
812
813 The function can write to the following special variables:
814
815 \list
816
817 \li vec3 \c SPECULAR Accumulates the specular light contributions, per
818 frament. The light processor functions will typically add (\c{+=}) to it,
819 since overwriting the value would lose the contribution from other lights.
820
821 \endlist
822
823 The function can read the following special variables, in addition to the
824 matrix (such as, \c MODEL_MATRIX) and vector (such as, \c CAMERA_POSITION)
825 uniforms from the table above:
826
827 \list
828 \li vec3 \c LIGHT_COLOR Specular light color.
829 \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()}.
830 \li float \c SHADOW_CONTRIB Shadow contribution, or 1.0 if not shadowed at all or not reciving shadows.
831 \li vec3 \c FRESNEL_CONTRIB Fresnel contribution from built in Fresnel calculation.
832 \li vec3 \c TO_LIGHT_DIR Vector pointing towards the light source.
833 \li vec3 \c NORMAL The normal vector in world space.
834 \li vec4 \c BASE_COLOR The base color and material alpha value.
835 \li float \c METALNESS The Metalness amount.
836 \li float \c ROUGHNESS The Roughness amount.
837 \li float \c SPECULAR_AMOUNT The specular amount. This value will be between
838 0.0 and 1.0 will be the same value set in the custom \c MAIN function. This
839 value will useful for calculating Fresnel contributions when not using the
840 built-in Fresnel contribution provided by \c FRESNEL_CONTRIB.
841 \endlist
842
843 \badcode
844 void SPECULAR_LIGHT()
845 {
846 vec3 H = normalize(VIEW_VECTOR + TO_LIGHT_DIR);
847 float cosAlpha = max(0.0, dot(H, normalize(NORMAL)));
848 float shine = pow(cosAlpha, exp2(15.0 * (1.0 - ROUGHNESS) + 1.0) * 0.25);
849 SPECULAR += shine * LIGHT_COLOR * FRESNEL_CONTRIB * SHADOW_CONTRIB * LIGHT_ATTENUATION;
850 }
851 \endcode
852
853 \li \c{void POST_PROCESS()} When present, this function is called at the
854 end of the fragment pipeline. The task of the function is to finalize
855 \c COLOR_SUM with final diffuse, specular and emissive terms. Unlike
856 \c FRAGCOLOR for a unshaded material, \c COLOR_SUM will be automatically
857 tonemapped before written to the framebuffer. For debugging purposes it is
858 sometimes useful to output a value that should not be treated as a color.
859 To avoid the tonemapping distorting this value it can be disabled by
860 setting the \l {SceneEnvironment::tonemapMode}{tonemapMode} property
861 to \c TonemapModeNone
862
863 The function can write to the following special variables:
864
865 \list
866 \li vec4 \c COLOR_SUM the output from the fragment shader. The default value
867 is vec4(DIFFUSE.rgb + SPECULAR + EMISSIVE, DIFFUSE.a)
868 \endlist
869
870 The function can read the following special variables.
871
872 \list
873 \li vec4 \c DIFFUSE The final diffuse term of the fragment pipeline.
874 \li vec3 \c SPECULAR The final specular term of the fragment pipeline.
875 \li vec3 \c EMISSIVE The final emissive term of the fragment pipeline.
876 \li vec2 \c UV0 - The first set of texture coordinates from the vertex shader.
877 \li vec2 \c UV1 - The second set of texture coordinates from the vertex shader.
878 \endlist
879
880 \badcode
881 void POST_PROCESS()
882 {
883 float center_x = textureSize(SCREEN_TEXTURE, 0).x * 0.5;
884 if (gl_FragCoord.x > center_x)
885 COLOR_SUM = DIFFUSE;
886 else
887 COLOR_SUM = vec4(EMISSIVE, DIFFUSE.a);
888 }
889 \endcode
890
891 \li \c{void IBL_PROBE()} When present, this function is called for IBL
892 (Image-Based Lighting).
893 The task of the function is to add both the diffuse and the specular
894 contributions of IBL to writable special variables \c DIFFUSE and
895 \c SPECULAR.
896
897 The function can write to the following special variables:
898
899 \list
900 \li vec3 \c DIFFUSE Accumulates the diffuse light contributions, per fragment.
901 \li vec3 \c SPECULAR Accumulates the specular light contributions, per
902 frament.
903 \endlist
904
905 The function can read the following special variables.
906
907 \list
908 \li vec4 \c BASE_COLOR The base color and material alpha value.
909 \li float \c AO_FACTOR The screen space occlusion factor.
910 \li float \c SPECULAR_AMOUNT The specular amount.
911 \li float \c ROUGHNESS The final emissive term of the fragment pipeline.
912 \li vec3 \c NORMAL The normal vector in world space.
913 \li vec3 \c VIEW_VECTOR Points towards the camera.
914 \li mat3 \c IBL_ORIENTATION The orientation of the light probe. It comes
915 from \l {SceneEnvironment::probeOrientation}.
916 \endlist
917
918 \badcode
919 void IBL_PROBE()
920 {
921 vec3 smpDir = IBL_ORIENTATION * NORMAL;
922 DIFFUSE += AO_FACTOR * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, smpDir, IBL_MAXMIPMAP).rgb;
923 }
924 \endcode
925
926 \endlist
927
928 \sa SceneEnvironment::tonemapMode, {Using Image-Based Lighting}
929
930 \section2 Custom variables between functions
931
932 Additional variables can be delivered from the MAIN function to the others.
933 The \c SHARED_VARS keyword can be used for defining new custom variables.
934 These user-defined variables can be accessed with SHARED.<variable name>.
935
936 For example, a shaded custom material can fetch a shared value in the MAIN
937 and use it in other functions.
938
939 \badcode
940 SHARED_VARS {
941 vec3 colorThreshold;
942 };
943 void MAIN()
944 {
945 BASE_COLOR = texture(baseColorMap, UV0);
946 SHARED.colorThreshold = texture(thresholdMap, UV0).rgb;
947 }
948 void DIRECTIONAL_LIGHT()
949 {
950 if (DIFFUSE >= SHARED.colorThreshold) {
951 DIFFUSE = SHARED.colorThreshold;
952 return;
953 }
954 DIFFUSE += LIGHT_COLOR * SHADOW_CONTRIB;
955 }
956 \endcode
957
958 \note SHARED can be written on all the functions without POST_PROCESS but it
959 is safe to write it on MAIN and read on the other functions.
960
961 \note A recommended use case to write SHARED on LIGHT functions is
962 reseting it on MAIN first and then accumulating it on each LIGHT functions.
963
964 \badcode
965 SHARED_VARS {
966 float sheenIntensity;
967 float sheenRoughness;
968 vec3 sheenColor;
969 vec3 outSheenColor;
970 };
971 void MAIN()
972 {
973 ...
974 vec4 tex = texture(uSheenMap, UV0);
975 SHARED.sheenColor = tex.rgb;
976 SHARED.sheenIntensity = tex.a;
977 SHARED.sheenRoughness = uSheenRoughness;
978 SHARED.outSheenColor = vec3(0.0);
979 }
980 void SPECULAR_LIGHT()
981 {
982 SHARED.outSheenColor += ...;
983 }
984 void POST_PROCESS()
985 {
986 COLOR_SUM = DIFFUSE + SPECULAR + EMISSIVE + SHARED.outSheenColor;
987 }
988 \endcode
989
990 \note MAIN is called before others, and POST_PROCESS after all others,
991 but that there is no guarantee for any other ordering for light processors.
992
993 \section2 Additional special keywords
994
995 The custom fragment shader code can freely access uniforms (such as, \c
996 CAMERA_DIRECTION or \c CAMERA_POSITION), and varyings passed on from the
997 custom vertex shader. Additionally, there are a number of built-in varyings
998 available as special keywords. Some of these are optional in the sense that
999 a vertex \c MAIN could calculate and pass on these on its own, but to
1000 reduce duplicated data fragment shaders can also rely on these built-ins
1001 instead. These built-ins are available in light processor functions and in
1002 the fragment MAIN.
1003
1004 \list
1005
1006 \li vec3 \c VAR_WORLD_NORMAL - Interpolated normal transformed by \c
1007 NORMAL_MATRIX.
1008
1009 \li vec3 \c VAR_WORLD_TANGENT - Interpolated tangent transformed by \c
1010 MODEL_MATRIX.
1011
1012 \li vec3 \c VAR_WORLD_BINORMAL - Interpolated binormal transformed by \c
1013 MODEL_MATRIX
1014
1015 \li vec3 \c NORMAL - Unlike \c VAR_WORLD_NORMAL, which is the
1016 interpolated normal as-is, this value is potentially adjusted for
1017 double-sidedness: when rendering with culling disabled, the normal will get
1018 inverted as necessary. Therefore lighting and other calculations are
1019 recommended to use \c NORMAL instead of \c VAR_WORLD_NORMAL in order
1020 behave correctly with all culling modes.
1021
1022 \li vec3 \c TANGENT - Like \c NORMAL, this value is potentially adjusted for
1023 double-sidedness: when rendering with culling disabled, the tangent will get
1024 inverted as necessary.
1025
1026 \li vec3 \c BINORMAL - Like \c NORMAL, this value is potentially adjusted for
1027 double-sidedness: when rendering with culling disabled, the binormal will get
1028 inverted as necessary.
1029
1030 \li vec3 \c VAR_WORLD_POSITION - Interpolated world space vertex position
1031 (\c{(MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz})
1032
1033 \li vec4 \c VAR_COLOR - The interpolated vertex color when colors are
1034 provided in the mesh. \c{vec4(1.0)} otherwise.
1035
1036 \li vec3 \c VIEW_VECTOR - Points towards the camera. This is
1037 effectively the \c{CAMERA_POSITION - VAR_WORLD_POSITION} vector normalized.
1038
1039 \li vec4 \c FRAGCOORD - Contains the window-relative coordinates of the
1040 current fragment.
1041
1042 \li float \c FRAMEBUFFER_Y_UP - The value is \c 1 when the Y axis points up
1043 in the coordinate system for framebuffers (textures), meaning \c{(0, 0)} is
1044 the bottom-left corner. The value is \c{-1} when the Y axis points down,
1045 \c{(0, 0)} being the top-left corner. Such differences in the underlying
1046 graphics APIs do not concern most custom materials. One notable exception
1047 is sampling \c SCREEN_TEXTURE with texture coordinates \b not based on
1048 \c FRAGCOORD. As the orientation of \c SCREEN_TEXTURE is tied to the
1049 underlying graphics API by nature, using texture coordinates from a mesh
1050 may need appropriate adjustments to the Y coordinate.
1051
1052 For example, the following fragment shader, suitable for Rectangle or Cube
1053 meshes, will display the opaque objects from the scene on the model:
1054
1055 \badcode
1056 VARYING vec2 texcoord;
1057 void MAIN()
1058 {
1059 vec2 screencoord = texcoord;
1060 if (FRAMEBUFFER_Y_UP < 0.0) // effectively: if not OpenGL
1061 screencoord.y = 1.0 - screencoord.y;
1062 BASE_COLOR = texture(SCREEN_TEXTURE, screencoord);
1063 }
1064 \endcode
1065
1066 When sampling textures other than \c SCREEN_TEXTURE, and \c DEPTH_TEXTURE,
1067 or when \c FRAGCOORD is used to calculate the texture coordinate (which
1068 would be the typical use case for accessing the screen and depth textures),
1069 such an adjustment is not necessary.
1070
1071 \li float \c NDC_Y_UP - The value is \c 1 when the Y axis points up in
1072 normalized device coordinate space, and \c{-1} when the Y axis points down.
1073 Y pointing down is the case when rendering happens with Vulkan. Most
1074 materials do not need to be concerned by this, but being able to branch
1075 based on this can become useful in certain advanced use cases.
1076
1077 \li float \c NEAR_CLIP_VALUE - The value is \c -1 for when the clipping plane
1078 range's starts at \c -1 and goes to \c 1. This is true when using OpenGL for
1079 rendering. For other rendering backends the value of this property will be
1080 \c 0 meaning the clipping plane range is \c 0 to \c 1. This value is useful
1081 with certain techniques involving the \c DEPTH_TEXTURE
1082
1083 For example, the following fragment shader demonstrates a technique for
1084 reconstructing the position of a value from the depth buffer to determine
1085 the distance from the current position being rendered. When used in
1086 combination with \c INVERSE_PROJECTION_MATRIX the value of depth needs
1087 to be in normalized device coordinates so it is important to make sure that
1088 the range of depth value reflects that. When the \c NEAR_CLIP_VALUE is
1089 \c -1 then the depth value gets scaled to be between \c -1 and \c 1.
1090
1091 \badcode
1092 void MAIN() {
1093 vec2 screen_uv = FRAGCOORD.xy / vec2(textureSize(SCREEN_TEXTURE, 0));
1094 float depth = texture(DEPTH_TEXTURE, screen_uv).r;
1095
1096 if (NEAR_CLIP_VALUE < 0.0) // effectively: if opengl
1097 depth = depth * 2.0 - 1.0;
1098
1099 vec4 unproject = INVERSE_PROJECTION_MATRIX * vec4(screen_uv, depth, 1.0);
1100 depth = (unproject.xyz / unproject.w).z;
1101 float viewVectorZ = (VIEW_MATRIX * vec4(VAR_WORLD_POSITION, 1.0)).z;
1102 depth = viewVectorZ - depth;
1103
1104 BASE_COLOR = vec4(depth, depth, depth, 1.0);
1105 }
1106 \endcode
1107
1108 \li float \c IBL_EXPOSE - The amount of light emitted by the light probe.
1109 It comes from \l {SceneEnvironment::probeExposure}.
1110 \badcode
1111 DIFFUSE += AO_FACTOR * IBL_EXPOSE * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, NORMAL, IBL_MAXMIPMAP).rgb;
1112 \endcode
1113
1114 \li float \c IBL_HORIZON - The horizontal cut-off value of reflections from
1115 the lower half environment. It comes from \l {SceneEnvironment::probeHorizon}
1116 {Horizon Cut-Off} but remapped to [-1, 0).
1117 \badcode
1118 vec3 diffuse += AO_FACTOR * IBL_EXPOSE * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, NORMAL, IBL_MAXMIPMAP).rgb;
1119 if (IBL_HORIZON > -1.0) {
1120 float ctr = 0.5 + 0.5 * IBL_HORIZON;
1121 float vertWt = smoothstep(ctr * 0.25, ctr + 0.25, NORMAL.y);
1122 float wtScaled = mix(1.0, vertWt, IBL_HORIZON + 1.0);
1123 diffuse *= wtScaled;
1124 }
1125 \endcode
1126
1127 \li float \c IBL_MAXMIPMAP - The maximum mipmap level of IBL_TEXTURE.
1128
1129 \endlist
1130
1131 \section2 Instancing
1132
1133 When doing instanced rendering, some of the keywords above do not apply.
1134 The following keywords are only available with instancing:
1135
1136 \list
1137 \li \c INSTANCE_MODEL_MATRIX -> mat4, replacement for \c MODEL_MATRIX, including the instancing transformation.
1138 \li \c INSTANCE_MODELVIEWPROJECTION_MATRIX -> mat4, replacement for \c MODELVIEWPROJECTION_MATRIX, including the instancing transformation.
1139 \li \c INSTANCE_COLOR -> vec4, the instance color: to be combined with \c {COLOR}.
1140 \li \c INSTANCE_DATA -> vec4, instance custom data.
1141 \li \c INSTANCE_INDEX -> int, the instance number, and index into the instancing table.
1142 \endlist
1143
1144 \section1 Screen, depth, and other textures
1145
1146 The rendering pipeline can expose a number of textures to the custom
1147 material shaders with content from special render passes. This applies both
1148 to shaded and unshaded custom materials.
1149
1150 For example, a shader may want access to a depth texture that contains the
1151 depth buffer contents for the opaque objects in the scene. This is achieved
1152 by sampling \c DEPTH_TEXTURE. Such a texture is not normally generated,
1153 unless there is a real need for it. Therefore, the presence of the
1154 following keywords in the vertex or fragment shader also acts as a toggle
1155 for opting in to the - potentially expensive - passes for generating the
1156 texture in question. (of course, it could be that some of these become
1157 already enabled due to other settings, such as the ambient occlusion
1158 parameters in SceneEnvironment or due to a post-processing effect relying
1159 on the depth texture, in which case the textures in question are generated
1160 regardless of the custom material and so sampling these special textures in
1161 the material comes at no extra cost apart from the texture access itself)
1162
1163 \list
1164
1165 \li \c SCREEN_TEXTURE - When present, a texture (\c sampler2D or \c
1166 sampler2DArray) with the color buffer from a rendering pass containing the
1167 contents of the scene excluding any transparent materials or any materials
1168 also using the SCREEN_TEXTURE is exposed to the shader under this name. The
1169 texture can be used for techniques that require the contents of the
1170 framebuffer they are being rendered to. The SCREEN_TEXTURE texture uses the
1171 same clear mode as the View3D. The size of these textures matches the size
1172 of the View3D in pixels. For example, a fragment shader could contain the
1173 following:
1174 \badcode
1175 vec2 uv = FRAGCOORD.xy / vec2(textureSize(SCREEN_TEXTURE, 0));
1176 vec2 displace = vec2(0.1);
1177 vec4 c = texture(SCREEN_TEXTURE, uv + displace);
1178 \endcode
1179
1180 Be aware that using \c SCREEN_TEXTURE requires appropriate, conscious
1181 design of the scene. Objects using such materials have to be positioned
1182 carefully, typically above all other objects that are expected to be
1183 visible in the texture. Objects that employ semi-transparency in some form
1184 are never part of the \c SCREEN_TEXTURE. Often \c SCREEN_TEXTURE will be
1185 used in combination with \c BASE_COLOR in \c MAIN. For example, the
1186 following custom fragment shader applies an emboss effect, while keeping
1187 fragments not touched by opaque objects transparent. This assumes that the
1188 object with the material is placed in the front, and that it has blending
1189 enabled. \badcode
1190 void MAIN()
1191 {
1192 vec2 size = vec2(textureSize(SCREEN_TEXTURE, 0));
1193 vec2 uv = FRAGCOORD.xy / size;
1194
1195 // basic emboss effect
1196 vec2 d = vec2(1.0 / size.x, 1.0 / size.y);
1197 vec4 diff = texture(SCREEN_TEXTURE, uv + d) - texture(SCREEN_TEXTURE, uv - d);
1198 float c = (diff.x + diff.y + diff.z) + 0.5;
1199
1200 float alpha = texture(SCREEN_TEXTURE, uv).a;
1201 BASE_COLOR = vec4(vec3(c), alpha);
1202 }
1203 \endcode
1204 With \l{Multiview Rendering}{multiview rendering}, \c SCREEN_TEXTURE is a \c
1205 sampler2DArray. Use \c VIEW_INDEX to select the layer to use. For VR/AR
1206 applications that wish to support both types of rendering, the portable
1207 approach is the following:
1208 \badcode
1209 #if QSHADER_VIEW_COUNT >= 2
1210 vec4 c = texture(SCREEN_TEXTURE, vec3(uv, VIEW_INDEX));
1211 #else
1212 vec4 c = texture(SCREEN_TEXTURE, uv);
1213 #endif
1214 \endcode
1215
1216 \li \c SCREEN_MIP_TEXTURE - Identical to \c SCREEN_TEXTURE in most ways,
1217 the difference being that this texture has mipmaps generated. This can be
1218 an expensive feature performance-wise, depending on the screen size, and
1219 due to having to generate the mipmaps every time the scene is rendered.
1220 Therefore, prefer using \c SCREEN_TEXTURE always, unless a technique
1221 relying on the texture mip levels (e.g. using \c textureLod in the shader)
1222 is implemented by the custom material.
1223
1224 \li \c DEPTH_TEXTURE - When present, a texture (\c sampler2D or \c
1225 sampler2DArray) with the (non-linearized) depth buffer contents is exposed
1226 to the shader under this name. Only opaque objects are included.
1227 For example, a fragment shader could contain the following: \badcode
1228 ivec2 dtSize = textureSize(DEPTH_TEXTURE, 0);
1229 vec2 dtUV = (FRAGCOORD.xy) / vec2(dtSize);
1230 vec4 depthSample = texture(DEPTH_TEXTURE, dtUV);
1231 float zNear = CAMERA_PROPERTIES.x;
1232 float zFar = CAMERA_PROPERTIES.y;
1233 float zRange = zFar - zNear;
1234 float z_n = 2.0 * depthSample.r - 1.0;
1235 float d = 2.0 * zNear * zFar / (zFar + zNear - z_n * zRange);
1236 d /= zFar;
1237 \endcode
1238 With \l{Multiview Rendering}{multiview rendering}, \c DEPTH_TEXTURE is a \c
1239 sampler2DArray. Use \c VIEW_INDEX to select the layer to use. For VR/AR
1240 applications that wish to support both types of rendering, the portable
1241 approach is the following:
1242 \badcode
1243 #if QSHADER_VIEW_COUNT >= 2
1244 vec4 depthSample = texture(DEPTH_TEXTURE, vec3(uv, VIEW_INDEX));
1245 #else
1246 vec4 depthSample = texture(DEPTH_TEXTURE, uv);
1247 #endif
1248 \endcode
1249
1250 \li \c NORMAL_ROUGHNESS_TEXTURE - When present, a texture (\c sampler2D)
1251 with the world-space normals and the material roughness is exposed to the
1252 shader under this name. Only opaque objects are included. The roughness is
1253 stored in the alpha channel.
1254 For example, a fragment shader could contain the following: \badcode
1255 vec3 N = normalize(texture(NORMAL_ROUGHNESS_TEXTURE, uv).rgb);
1256 \endcode
1257
1258 \li \c AO_TEXTURE - When present and screen space ambient occlusion is
1259 enabled (meaning when the AO strength and distance are both non-zero) in
1260 SceneEnvironment, the SSAO texture (\c sampler2D or \c sampler2DArray) is
1261 exposed to the shader under this name. Sampling this texture can be useful
1262 in unshaded materials. Shaded materials have ambient occlusion support built
1263 in. This means that the ambient occlusion factor is taken into account
1264 automatically. Whereas in a fragment shader for an unshaded material one
1265 could write the following to achieve the same: \badcode
1266 ivec2 aoSize = textureSize(AO_TEXTURE, 0);
1267 vec2 aoUV = (FRAGCOORD.xy) / vec2(aoSize);
1268 float aoFactor = texture(AO_TEXTURE, aoUV).x;
1269 \endcode
1270 With \l{Multiview Rendering}{multiview rendering}, \c AO_TEXTURE is a \c
1271 sampler2DArray. Use \c VIEW_INDEX to select the layer to use. For VR/AR
1272 applications that wish to support both types of rendering, the portable
1273 approach is the following:
1274 \badcode
1275 #if QSHADER_VIEW_COUNT >= 2
1276 ivec2 aoSize = textureSize(AO_TEXTURE, 0).xy;
1277 vec2 aoUV = (FRAGCOORD.xy) / vec2(aoSize);
1278 float aoFactor = texture(AO_TEXTURE, vec3(aoUV, VIEW_INDEX)).x;
1279 #else
1280 ivec2 aoSize = textureSize(AO_TEXTURE, 0);
1281 vec2 aoUV = (FRAGCOORD.xy) / vec2(aoSize);
1282 float aoFactor = texture(AO_TEXTURE, aoUV).x;
1283 #endif
1284 \endcode
1285
1286 \li \c IBL_TEXTURE - It will not enable any special rendering pass, but it can
1287 be used when the material has \l {Material::lightProbe} or the model is in the scope of
1288 \l {SceneEnvironment::lightProbe}.
1289
1290 \badcode
1291 void IBL_PROBE()
1292 {
1293 DIFFUSE += AO_FACTOR * BASE_COLOR.rgb * textureLod(IBL_TEXTURE, NORMAL, IBL_MAXMIPMAP).rgb;
1294 }
1295 \endcode
1296
1297 \li \c MOTION_VECTOR_TEXTURE — Enables a dedicated rendering pass that computes per-object
1298 motion vectors for the scene.
1299 The output is a four-channel texture: the R and G components contain the scaled motion vectors
1300 of the models, while the B and A components store the unscaled motion vectors.
1301
1302 \li \c VIEW_INDEX - When used in the custom shader code, this is a
1303 (non-interpolated) uint variable. When \l{Multiview Rendering}{multiview
1304 rendering} is not used, the value is always 0. With multiview rendering, the
1305 value is the current view index (e.g., gl_ViewIndex). Useful in particular
1306 in combination with \c DEPTH_TEXTURE and similar when multiview rendering is
1307 enabled.
1308
1309 \endlist
1310
1311 \sa {Qt Quick 3D - Custom Shaders Example}, {Qt Quick 3D - Custom Materials Example}, {Programmable Materials, Effects, Geometry, and Texture data}
1312*/
1313
1314/*!
1315 \qmlproperty url CustomMaterial::vertexShader
1316
1317 Specfies the file with the snippet of custom vertex shader code.
1318
1319 The value is a URL and must either be a local file or use the qrc scheme to
1320 access files embedded via the Qt resource system. Relative file paths
1321 (without a scheme) are also accepted, in which case the file is treated as
1322 relative to the component (the \c{.qml} file).
1323
1324 \warning Shader snippets are assumed to be trusted content. Application
1325 developers are advised to carefully consider the potential implications
1326 before allowing the loading of user-provided content that is not part of the
1327 application.
1328
1329 \sa fragmentShader
1330*/
1331
1332/*!
1333 \qmlproperty url CustomMaterial::fragmentShader
1334
1335 Specfies the file with the snippet of custom fragment shader code.
1336
1337 The value is a URL and must either be a local file or use the qrc scheme to
1338 access files embedded via the Qt resource system. Relative file paths
1339 (without a scheme) are also accepted, in which case the file is treated as
1340 relative to the component (the \c{.qml} file).
1341
1342 \warning Shader snippets are assumed to be trusted content. Application
1343 developers are advised to carefully consider the potential implications
1344 before allowing the loading of user-provided content that is not part of the
1345 application.
1346
1347 \sa vertexShader
1348*/
1349
1350/*!
1351 \qmlproperty enumeration CustomMaterial::shadingMode
1352 Specifies the type of the material. The default value is Shaded.
1353
1354 \value CustomMaterial.Unshaded
1355 \value CustomMaterial.Shaded
1356*/
1357
1358/*!
1359 \qmlproperty bool CustomMaterial::alwaysDirty
1360 Specifies that the material state is always dirty, which indicates that the material needs
1361 to be refreshed every time it is used by the QtQuick3D.
1362*/
1363
1364/*!
1365 \qmlproperty enumeration CustomMaterial::sourceBlend
1366
1367 Specifies the source blend factor. The default value is \c
1368 CustomMaterial.NoBlend.
1369
1370 \value CustomMaterial.NoBlend
1371 \value CustomMaterial.Zero
1372 \value CustomMaterial.One
1373 \value CustomMaterial.SrcColor
1374 \value CustomMaterial.OneMinusSrcColor
1375 \value CustomMaterial.DstColor
1376 \value CustomMaterial.OneMinusDstColor
1377 \value CustomMaterial.SrcAlpha
1378 \value CustomMaterial.OneMinusSrcAlpha
1379 \value CustomMaterial.DstAlpha
1380 \value CustomMaterial.OneMinusDstAlpha
1381 \value CustomMaterial.ConstantColor
1382 \value CustomMaterial.OneMinusConstantColor
1383 \value CustomMaterial.ConstantAlpha
1384 \value CustomMaterial.OneMinusConstantAlpha
1385 \value CustomMaterial.SrcAlphaSaturate
1386
1387 \note Both \l sourceBlend and \l destinationBlend needs to be set to a non-default
1388 value before blending is enabled.
1389
1390 \sa destinationBlend
1391*/
1392
1393/*!
1394 \qmlproperty enumeration CustomMaterial::destinationBlend
1395
1396 Specifies the destination blend factor. The default value is \c
1397 CustomMaterial.NoBlend.
1398
1399 \value CustomMaterial.NoBlend
1400 \value CustomMaterial.Zero
1401 \value CustomMaterial.One
1402 \value CustomMaterial.SrcColor
1403 \value CustomMaterial.OneMinusSrcColor
1404 \value CustomMaterial.DstColor
1405 \value CustomMaterial.OneMinusDstColor
1406 \value CustomMaterial.SrcAlpha
1407 \value CustomMaterial.OneMinusSrcAlpha
1408 \value CustomMaterial.DstAlpha
1409 \value CustomMaterial.OneMinusDstAlpha
1410 \value CustomMaterial.ConstantColor
1411 \value CustomMaterial.OneMinusConstantColor
1412 \value CustomMaterial.ConstantAlpha
1413 \value CustomMaterial.OneMinusConstantAlpha
1414 \value CustomMaterial.SrcAlphaSaturate
1415
1416 \note Both \l sourceBlend and \l destinationBlend needs to be set to a non-default
1417 value before blending is enabled.
1418
1419 \sa sourceBlend
1420*/
1421
1422/*!
1423 \qmlproperty real CustomMaterial::lineWidth
1424
1425 This property determines the width of the lines rendered, when the geometry
1426 is using a primitive type of lines or line strips. The default value is
1427 1.0. This property is not relevant when rendering other types of geometry,
1428 such as, triangle meshes.
1429
1430 \warning Line widths other than 1 may not be suported at run time,
1431 depending on the underlying graphics API. When that is the case, the
1432 request to change the width is ignored. For example, none of the following
1433 can be expected to support wide lines: Direct3D, Metal, OpenGL with core
1434 profile contexts.
1435
1436 \note Unlike the line width, the value of which is part of the graphics
1437 pipeline object, the point size for geometries with a topology of points is
1438 controlled by the vertex shader (when supported), and has therefore no
1439 corresponding QML property.
1440*/
1441
1442/*!
1443 \qmlproperty enumeration CustomMaterial::sourceAlphaBlend
1444 \since 6.7
1445
1446 Specifies the source alpha blend factor. The default value is \c
1447 CustomMaterial.NoBlend. This value is only actively used if \l sourceBlend and
1448 \l destinationBlend is set to a non-default value.
1449
1450 \value CustomMaterial.NoBlend
1451 \value CustomMaterial.Zero
1452 \value CustomMaterial.One
1453 \value CustomMaterial.SrcColor
1454 \value CustomMaterial.OneMinusSrcColor
1455 \value CustomMaterial.DstColor
1456 \value CustomMaterial.OneMinusDstColor
1457 \value CustomMaterial.SrcAlpha
1458 \value CustomMaterial.OneMinusSrcAlpha
1459 \value CustomMaterial.DstAlpha
1460 \value CustomMaterial.OneMinusDstAlpha
1461 \value CustomMaterial.ConstantColor
1462 \value CustomMaterial.OneMinusConstantColor
1463 \value CustomMaterial.ConstantAlpha
1464 \value CustomMaterial.OneMinusConstantAlpha
1465 \value CustomMaterial.SrcAlphaSaturate
1466
1467 \note For backwards compatibility purposes, when left to its default value,
1468 will be assigned the same value as \l sourceBlend when \l sourceBlend and
1469 \l destinationBlend is set to non-default values.
1470
1471 \sa sourceBlend
1472*/
1473
1474/*!
1475 \qmlproperty enumeration CustomMaterial::destinationAlphaBlend
1476 \since 6.7
1477
1478 Specifies the destination alpha blend factor. The default value is \c
1479 CustomMaterial.NoBlend. This value is only actively used if \l sourceBlend and
1480 \l destinationBlend is set to a non-default value.
1481
1482 \value CustomMaterial.NoBlend
1483 \value CustomMaterial.Zero
1484 \value CustomMaterial.One
1485 \value CustomMaterial.SrcColor
1486 \value CustomMaterial.OneMinusSrcColor
1487 \value CustomMaterial.DstColor
1488 \value CustomMaterial.OneMinusDstColor
1489 \value CustomMaterial.SrcAlpha
1490 \value CustomMaterial.OneMinusSrcAlpha
1491 \value CustomMaterial.DstAlpha
1492 \value CustomMaterial.OneMinusDstAlpha
1493 \value CustomMaterial.ConstantColor
1494 \value CustomMaterial.OneMinusConstantColor
1495 \value CustomMaterial.ConstantAlpha
1496 \value CustomMaterial.OneMinusConstantAlpha
1497 \value CustomMaterial.SrcAlphaSaturate
1498
1499 \note For backwards compatibility purposes, when left to its default value,
1500 will be assigned the same value as \l destinationBlend when \l sourceBlend and
1501 \l destinationBlend is set to non-default values.
1502
1503 \sa destinationBlend
1504*/
1505
1506static inline QRhiGraphicsPipeline::BlendFactor toRhiBlendFactor(QQuick3DCustomMaterial::BlendMode mode)
1507{
1508 switch (mode) {
1509 case QQuick3DCustomMaterial::BlendMode::Zero:
1510 return QRhiGraphicsPipeline::Zero;
1511 case QQuick3DCustomMaterial::BlendMode::One:
1512 return QRhiGraphicsPipeline::One;
1513 case QQuick3DCustomMaterial::BlendMode::SrcColor:
1514 return QRhiGraphicsPipeline::SrcColor;
1515 case QQuick3DCustomMaterial::BlendMode::OneMinusSrcColor:
1516 return QRhiGraphicsPipeline::OneMinusSrcColor;
1517 case QQuick3DCustomMaterial::BlendMode::DstColor:
1518 return QRhiGraphicsPipeline::DstColor;
1519 case QQuick3DCustomMaterial::BlendMode::OneMinusDstColor:
1520 return QRhiGraphicsPipeline::OneMinusDstColor;
1521 case QQuick3DCustomMaterial::BlendMode::SrcAlpha:
1522 return QRhiGraphicsPipeline::SrcAlpha;
1523 case QQuick3DCustomMaterial::BlendMode::OneMinusSrcAlpha:
1524 return QRhiGraphicsPipeline::OneMinusSrcAlpha;
1525 case QQuick3DCustomMaterial::BlendMode::DstAlpha:
1526 return QRhiGraphicsPipeline::DstAlpha;
1527 case QQuick3DCustomMaterial::BlendMode::OneMinusDstAlpha:
1528 return QRhiGraphicsPipeline::OneMinusDstAlpha;
1529 case QQuick3DCustomMaterial::BlendMode::ConstantColor:
1530 return QRhiGraphicsPipeline::ConstantColor;
1531 case QQuick3DCustomMaterial::BlendMode::OneMinusConstantColor:
1532 return QRhiGraphicsPipeline::OneMinusConstantColor;
1533 case QQuick3DCustomMaterial::BlendMode::ConstantAlpha:
1534 return QRhiGraphicsPipeline::ConstantAlpha;
1535 case QQuick3DCustomMaterial::BlendMode::OneMinusConstantAlpha:
1536 return QRhiGraphicsPipeline::OneMinusConstantAlpha;
1537 case QQuick3DCustomMaterial::BlendMode::SrcAlphaSaturate:
1538 return QRhiGraphicsPipeline::SrcAlphaSaturate;
1539 default:
1540 return QRhiGraphicsPipeline::One;
1541 }
1542}
1543
1544QQuick3DCustomMaterial::QQuick3DCustomMaterial(QQuick3DObject *parent)
1545 : QQuick3DMaterial(*(new QQuick3DObjectPrivate(QQuick3DObjectPrivate::Type::CustomMaterial)), parent)
1546{
1547}
1548
1549QQuick3DCustomMaterial::~QQuick3DCustomMaterial() {}
1550
1551QQuick3DCustomMaterial::BlendMode QQuick3DCustomMaterial::srcBlend() const
1552{
1553 return m_srcBlend;
1554}
1555
1556void QQuick3DCustomMaterial::setSrcBlend(BlendMode mode)
1557{
1558 if (m_srcBlend == mode)
1559 return;
1560
1561 m_srcBlend = mode;
1562 update();
1563 emit srcBlendChanged();
1564}
1565
1566QQuick3DCustomMaterial::BlendMode QQuick3DCustomMaterial::dstBlend() const
1567{
1568 return m_dstBlend;
1569}
1570
1571void QQuick3DCustomMaterial::setDstBlend(BlendMode mode)
1572{
1573 if (m_dstBlend == mode)
1574 return;
1575
1576 m_dstBlend = mode;
1577 update();
1578 emit dstBlendChanged();
1579}
1580
1581QQuick3DCustomMaterial::BlendMode QQuick3DCustomMaterial::srcAlphaBlend() const
1582{
1583 return m_srcAlphaBlend;
1584}
1585
1586void QQuick3DCustomMaterial::setSrcAlphaBlend(QQuick3DCustomMaterial::BlendMode mode)
1587{
1588 if (m_srcAlphaBlend == mode)
1589 return;
1590
1591 m_srcAlphaBlend = mode;
1592 update();
1593 emit srcAlphaBlendChanged();
1594}
1595
1596QQuick3DCustomMaterial::BlendMode QQuick3DCustomMaterial::dstAlphaBlend() const
1597{
1598 return m_dstAlphaBlend;
1599}
1600
1601void QQuick3DCustomMaterial::setDstAlphaBlend(QQuick3DCustomMaterial::BlendMode mode)
1602{
1603 if (m_dstAlphaBlend == mode)
1604 return;
1605
1606 m_dstAlphaBlend = mode;
1607 update();
1608 emit dstAlphaBlendChanged();
1609}
1610
1611QQuick3DCustomMaterial::ShadingMode QQuick3DCustomMaterial::shadingMode() const
1612{
1613 return m_shadingMode;
1614}
1615
1616void QQuick3DCustomMaterial::setShadingMode(ShadingMode mode)
1617{
1618 if (m_shadingMode == mode)
1619 return;
1620
1621 m_shadingMode = mode;
1622 markDirty(*this, Dirty::ShaderSettingsDirty);
1623 emit shadingModeChanged();
1624}
1625
1626QUrl QQuick3DCustomMaterial::vertexShader() const
1627{
1628 return m_vertexShader;
1629}
1630
1631void QQuick3DCustomMaterial::setVertexShader(const QUrl &url)
1632{
1633 if (m_vertexShader == url)
1634 return;
1635
1636 m_vertexShader = url;
1637 markDirty(*this, Dirty::ShaderSettingsDirty);
1638 emit vertexShaderChanged();
1639}
1640
1641QUrl QQuick3DCustomMaterial::fragmentShader() const
1642{
1643 return m_fragmentShader;
1644}
1645
1646void QQuick3DCustomMaterial::setFragmentShader(const QUrl &url)
1647{
1648 if (m_fragmentShader == url)
1649 return;
1650
1651 m_fragmentShader = url;
1652 markDirty(*this, Dirty::ShaderSettingsDirty);
1653 emit fragmentShaderChanged();
1654}
1655
1656
1657QString QQuick3DCustomMaterial::vertexShaderCode() const
1658{
1659 return m_vertexShaderCode;
1660}
1661
1662void QQuick3DCustomMaterial::setVertexShaderCode(const QString &code)
1663{
1664 if (m_vertexShaderCode == code)
1665 return;
1666
1667 m_vertexShaderCode = code;
1668 markDirty(*this, Dirty::ShaderSettingsDirty);
1669 emit vertexShaderCodeChanged();
1670}
1671
1672QString QQuick3DCustomMaterial::fragmentShaderCode() const
1673{
1674 return m_fragmentShaderCode;
1675}
1676
1677void QQuick3DCustomMaterial::setFragmentShaderCode(const QString &code)
1678{
1679 if (m_fragmentShaderCode == code)
1680 return;
1681
1682 m_fragmentShaderCode = code;
1683 markDirty(*this, Dirty::ShaderSettingsDirty);
1684 emit fragmentShaderCodeChanged();
1685}
1686
1687float QQuick3DCustomMaterial::lineWidth() const
1688{
1689 return m_lineWidth;
1690}
1691
1692void QQuick3DCustomMaterial::setLineWidth(float width)
1693{
1694 if (qFuzzyCompare(m_lineWidth, width))
1695 return;
1696 m_lineWidth = width;
1697 update();
1698 emit lineWidthChanged();
1699}
1700
1701void QQuick3DCustomMaterial::markAllDirty()
1702{
1703 m_dirtyAttributes |= Dirty::AllDirty;
1704 QQuick3DMaterial::markAllDirty();
1705}
1706
1707void QQuick3DCustomMaterial::markDirty(QQuick3DCustomMaterial &that, Dirty type)
1708{
1709 if (!(that.m_dirtyAttributes & quint32(type))) {
1710 that.m_dirtyAttributes |= quint32(type);
1711 that.update();
1712 }
1713}
1714
1715bool QQuick3DCustomMaterial::alwaysDirty() const
1716{
1717 return m_alwaysDirty;
1718}
1719
1720void QQuick3DCustomMaterial::setAlwaysDirty(bool alwaysDirty)
1721{
1722 if (m_alwaysDirty == alwaysDirty)
1723 return;
1724
1725 m_alwaysDirty = alwaysDirty;
1726 update();
1727 emit alwaysDirtyChanged();
1728}
1729
1730static void setCustomMaterialFlagsFromShader(QSSGRenderCustomMaterial *material, const QSSGCustomShaderMetaData &meta)
1731{
1732 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenTexture))
1733 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenTexture, true);
1734 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesScreenMipTexture))
1735 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenMipTexture, true);
1736 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesDepthTexture))
1737 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::DepthTexture, true);
1738 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesNormalTexture))
1739 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::NormalTexture, true);
1740 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesAoTexture))
1741 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::AoTexture, true);
1742 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesProjectionMatrix))
1743 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix, true);
1744 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesInverseProjectionMatrix))
1745 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix, true);
1746 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesVarColor))
1747 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::VarColor, true);
1748 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesIblOrientation))
1749 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::IblOrientation, true);
1750 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesLightmap))
1751 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Lightmap, true);
1752 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesSkinning))
1753 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Skinning, true);
1754 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesMorphing))
1755 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Morphing, true);
1756 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesViewIndex))
1757 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ViewIndex, true);
1758 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesClearcoat))
1759 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Clearcoat, true);
1760 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesSheen))
1761 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Sheen, true);
1762 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesAnisotropy))
1763 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Anisotropy, true);
1764 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesIridescence))
1765 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Iridescence, true);
1766 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesDispersion))
1767 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Dispersion, true);
1768 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesClearcoatFresnelScaleBias))
1769 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ClearcoatFresnelScaleBias, true);
1770 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesFresnelScaleBias))
1771 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::FresnelScaleBias, true);
1772 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesMotionVectorTexture))
1773 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::MotionVectorTexture, true);
1774 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesTransmission)) {
1775 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Transmission, true);
1776 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenTexture, true);
1777 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenMipTexture, true);
1778 }
1779
1780 // vertex only
1781 if (meta.flags.testFlag(QSSGCustomShaderMetaData::OverridesPosition))
1782 material->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::OverridesPosition, true);
1783
1784 // fragment only
1785 if (meta.flags.testFlag(QSSGCustomShaderMetaData::UsesSharedVars))
1786 material->m_usesSharedVariables = true;
1787}
1788
1789static QByteArray prepareCustomShader(QSSGRenderCustomMaterial *customMaterial,
1790 const QSSGShaderCustomMaterialAdapter::StringPairList &uniforms,
1791 const QByteArray &snippet,
1792 QSSGShaderCache::ShaderType shaderType,
1793 QSSGCustomShaderMetaData &meta,
1794 bool multiViewCompatible)
1795{
1796 if (snippet.isEmpty())
1797 return QByteArray();
1798
1799 QByteArray sourceCode = snippet;
1800 QByteArray buf;
1801
1802 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
1803 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
1804 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, sourceCode, shaderType, multiViewCompatible);
1805 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf, scratch, result, shaderType, multiViewCompatible, uniforms, {}, {}, {}, {});
1806
1807 sourceCode = result.first;
1808 sourceCode.append(buf);
1809 meta = result.second;
1810 setCustomMaterialFlagsFromShader(customMaterial, meta);
1811 return sourceCode;
1812}
1813
1814QSSGRenderGraphObject *QQuick3DCustomMaterial::updateSpatialNode(QSSGRenderGraphObject *node)
1815{
1816 using namespace QSSGShaderUtils;
1817
1818 const auto &renderContext = QQuick3DObjectPrivate::get(this)->sceneManager->wattached->rci();
1819 if (!renderContext) {
1820 qWarning("QQuick3DCustomMaterial: No render context interface?");
1821 return nullptr;
1822 }
1823
1824 QSSGShaderCustomMaterialAdapter::StringPairList uniforms;
1825 QSSGRenderCustomMaterial *customMaterial = static_cast<QSSGRenderCustomMaterial *>(node);
1826 bool newBackendNode = false;
1827 bool shadersMayChange = false;
1828 if (!customMaterial) {
1829 customMaterial = new QSSGRenderCustomMaterial;
1830 newBackendNode = true;
1831 } else if (m_dirtyAttributes & ShaderSettingsDirty) {
1832 shadersMayChange = true;
1833 }
1834
1835 if (newBackendNode || shadersMayChange) {
1836 markAllDirty();
1837
1838 customMaterial->m_properties.clear();
1839 customMaterial->m_textureProperties.clear();
1840
1841 customMaterial->m_shadingMode = QSSGRenderCustomMaterial::ShadingMode(int(m_shadingMode));
1842
1843 QMetaMethod propertyDirtyMethod;
1844 const int idx = metaObject()->indexOfSlot("onPropertyDirty()");
1845 if (idx != -1)
1846 propertyDirtyMethod = metaObject()->method(idx);
1847
1848 const int propCount = metaObject()->propertyCount();
1849 int propOffset = metaObject()->propertyOffset();
1850
1851 // Custom materials can have multilayered inheritance structure, so find the actual propOffset
1852 const QMetaObject *superClass = metaObject()->superClass();
1853 while (superClass && qstrcmp(superClass->className(), "QQuick3DCustomMaterial") != 0) {
1854 propOffset = superClass->propertyOffset();
1855 superClass = superClass->superClass();
1856 }
1857
1858 using TextureInputProperty = QPair<QQuick3DShaderUtilsTextureInput *, const char *>;
1859 QVector<TextureInputProperty> textureProperties; // We'll deal with these later
1860
1861 for (int i = propOffset; i != propCount; ++i) {
1862 const auto property = metaObject()->property(i);
1863 if (Q_UNLIKELY(!property.isValid()))
1864 continue;
1865
1866 const auto name = property.name();
1867 QMetaType propType = property.metaType();
1868 QVariant propValue = property.read(this);
1869 if (propType == QMetaType(QMetaType::QVariant))
1870 propType = propValue.metaType();
1871
1872 if (propType.id() >= QMetaType::User) {
1873 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
1874 if (QQuick3DShaderUtilsTextureInput *texture = property.read(this).value<QQuick3DShaderUtilsTextureInput *>())
1875 textureProperties.push_back({texture, name});
1876 }
1877 } else if (propType == QMetaType(QMetaType::QObjectStar)) {
1878 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(propValue.value<QObject *>()))
1879 textureProperties.push_back({texture, name});
1880 } else {
1881 const auto type = uniformType(propType);
1882 if (type != QSSGRenderShaderValue::Unknown) {
1883 uniforms.append({ uniformTypeName(propType), name });
1884 customMaterial->m_properties.push_back({ name, propValue, uniformType(propType), i});
1885 if (newBackendNode) {
1886 // Track the property changes
1887 if (property.hasNotifySignal() && propertyDirtyMethod.isValid())
1888 connect(this, property.notifySignal(), this, propertyDirtyMethod);
1889 } // else already connected
1890 } else {
1891 // ### figure out how _not_ to warn when there are no dynamic
1892 // properties defined (because warnings like Blah blah objectName etc. are not helpful)
1893 //qWarning("No known uniform conversion found for effect property %s. Skipping", property.name());
1894 }
1895 }
1896 }
1897
1898 const auto processTextureProperty = [&](QQuick3DShaderUtilsTextureInput &texture, const QByteArray &name) {
1899 texture.name = name;
1900
1901 QSSGRenderCustomMaterial::TextureProperty textureData;
1902 textureData.texInput = &texture;
1903 textureData.name = name;
1904 textureData.shaderDataType = QSSGRenderShaderValue::Texture;
1905
1906 if (newBackendNode) {
1907 connect(&texture, &QQuick3DShaderUtilsTextureInput::enabledChanged, this, &QQuick3DCustomMaterial::onTextureDirty);
1908 connect(&texture, &QQuick3DShaderUtilsTextureInput::textureChanged, this, &QQuick3DCustomMaterial::onTextureDirty);
1909 } // else already connected
1910
1911 QQuick3DTexture *tex = texture.texture(); // may be null if the TextureInput has no 'texture' set
1912 if (tex && QQuick3DObjectPrivate::get(tex)->type == QQuick3DObjectPrivate::Type::ImageCube) {
1913 uniforms.append({ QByteArrayLiteral("samplerCube"), textureData.name });
1914 } else if (tex && tex->textureData() && tex->textureData()->depth() > 0) {
1915 uniforms.append({ QByteArrayLiteral("sampler3D"), textureData.name });
1916 } else if (tex && tex->textureProvider() && QQuick3DObjectPrivate::get(tex->textureProvider())->type == QQuick3DObjectPrivate::Type::TextureProvider) {
1917 auto textureProvider = static_cast<QQuick3DTextureProviderExtension *>(tex->textureProvider());
1918 switch (textureProvider->samplerHint()) {
1919 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2D:
1920 uniforms.append({ QByteArrayLiteral("sampler2D"), textureData.name });
1921 break;
1922 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2DArray:
1923 uniforms.append({ QByteArrayLiteral("sampler2DArray"), textureData.name });
1924 break;
1925 case QQuick3DTextureProviderExtension::SamplerHint::Sampler3D:
1926 uniforms.append({ QByteArrayLiteral("sampler3D"), textureData.name });
1927 break;
1928 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCube:
1929 uniforms.append({ QByteArrayLiteral("samplerCube"), textureData.name });
1930 break;
1931 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCubeArray:
1932 uniforms.append({ QByteArrayLiteral("samplerCubeArray"), textureData.name });
1933 break;
1934 case QQuick3DTextureProviderExtension::SamplerHint::SamplerBuffer:
1935 uniforms.append({ QByteArrayLiteral("samplerBuffer"), textureData.name });
1936 break;
1937 }
1938 } else {
1939 uniforms.append({ QByteArrayLiteral("sampler2D"), textureData.name });
1940 }
1941
1942 customMaterial->m_textureProperties.push_back(textureData);
1943 };
1944
1945 for (const auto &textureProperty : std::as_const(textureProperties))
1946 processTextureProperty(*textureProperty.first, textureProperty.second);
1947
1948 if (customMaterial->incompleteBuildTimeObject || (m_dirtyAttributes & DynamicPropertiesDirty)) { // This object came from the shadergen tool
1949 const auto names = dynamicPropertyNames();
1950 for (const auto &name : names) {
1951 QVariant propValue = property(name.constData());
1952 QMetaType propType = propValue.metaType();
1953 if (propType == QMetaType(QMetaType::QVariant))
1954 propType = propValue.metaType();
1955
1956 if (propType.id() >= QMetaType::User) {
1957 if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) {
1958 if (QQuick3DShaderUtilsTextureInput *texture = propValue.value<QQuick3DShaderUtilsTextureInput *>())
1959 textureProperties.push_back({texture, name});
1960 }
1961 } else if (propType.id() == QMetaType::QObjectStar) {
1962 if (QQuick3DShaderUtilsTextureInput *texture = qobject_cast<QQuick3DShaderUtilsTextureInput *>(propValue.value<QObject *>()))
1963 textureProperties.push_back({texture, name});
1964 } else {
1965 const auto type = uniformType(propType);
1966 if (type != QSSGRenderShaderValue::Unknown) {
1967 uniforms.append({ uniformTypeName(propType), name });
1968 customMaterial->m_properties.push_back({ name, propValue,
1969 uniformType(propType), -1 /* aka. dynamic property */});
1970 // We don't need to track property changes
1971 } else {
1972 // ### figure out how _not_ to warn when there are no dynamic
1973 // properties defined (because warnings like Blah blah objectName etc. are not helpful)
1974 qWarning("No known uniform conversion found for custom material property %s. Skipping", name.constData());
1975 }
1976 }
1977 }
1978
1979 for (const auto &property : std::as_const(textureProperties))
1980 processTextureProperty(*property.first, property.second);
1981 }
1982
1983 const QQmlContext *context = qmlContext(this);
1984 QByteArray vertex;
1985 QByteArray fragment;
1986 QByteArray vertexProcessed[2];
1987 QSSGCustomShaderMetaData vertexMeta;
1988 QByteArray fragmentProcessed[2];
1989 QSSGCustomShaderMetaData fragmentMeta;
1990 QByteArray shaderPathKey("custom material --");
1991
1992 customMaterial->m_renderFlags = {};
1993
1994 if (!m_vertexShader.isEmpty())
1995 vertex = QSSGShaderUtils::resolveShader(m_vertexShader, context, shaderPathKey);
1996 else if (!m_vertexShaderCode.isEmpty())
1997 vertex = m_vertexShaderCode.toLatin1();
1998
1999 if (!m_fragmentShader.isEmpty())
2000 fragment = QSSGShaderUtils::resolveShader(m_fragmentShader, context, shaderPathKey);
2001 else if (!m_fragmentShaderCode.isEmpty())
2002 fragment = m_fragmentShaderCode.toLatin1();
2003
2004 // Multiview is a problem, because we will get a dedicated snippet after
2005 // preparation (the one that has [qt_viewIndex] added where it matters).
2006 // But at least the view count plays no role here on this level. So one
2007 // normal and one multiview "variant" is good enough.
2008
2009 vertexProcessed[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex] =
2010 prepareCustomShader(customMaterial, uniforms, vertex, QSSGShaderCache::ShaderType::Vertex, vertexMeta, false);
2011 fragmentProcessed[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex] =
2012 prepareCustomShader(customMaterial, uniforms, fragment, QSSGShaderCache::ShaderType::Fragment, fragmentMeta, false);
2013
2014 vertexProcessed[QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex] =
2015 prepareCustomShader(customMaterial, uniforms, vertex, QSSGShaderCache::ShaderType::Vertex, vertexMeta, true);
2016 fragmentProcessed[QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex] =
2017 prepareCustomShader(customMaterial, uniforms, fragment, QSSGShaderCache::ShaderType::Fragment, fragmentMeta, true);
2018
2019 // At this point we have snippets that look like this:
2020 // - the original code, with VARYING ... lines removed
2021 // - followed by QQ3D_SHADER_META block for uniforms
2022 // - followed by QQ3D_SHADER_META block for inputs/outputs
2023
2024 customMaterial->m_customShaderPresence = {};
2025 for (int i : { QSSGRenderCustomMaterial::RegularShaderPathKeyIndex, QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex }) {
2026 if (vertexProcessed[i].isEmpty() && fragmentProcessed[i].isEmpty())
2027 continue;
2028
2029 const QByteArray key = shaderPathKey + ':' + QCryptographicHash::hash(QByteArray(vertexProcessed[i] + fragmentProcessed[i]), QCryptographicHash::Algorithm::Sha1).toHex();
2030 // the processed snippet code is different for regular and multiview, so 'key' reflects that already
2031 customMaterial->m_shaderPathKey[i] = key;
2032 if (!vertexProcessed[i].isEmpty()) {
2033 customMaterial->m_customShaderPresence.setFlag(QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Vertex);
2034 renderContext->shaderLibraryManager()->setShaderSource(key, QSSGShaderCache::ShaderType::Vertex, vertexProcessed[i], vertexMeta);
2035 }
2036 if (!fragmentProcessed[i].isEmpty()) {
2037 customMaterial->m_customShaderPresence.setFlag(QSSGRenderCustomMaterial::CustomShaderPresenceFlag::Fragment);
2038 renderContext->shaderLibraryManager()->setShaderSource(key, QSSGShaderCache::ShaderType::Fragment, fragmentProcessed[i], fragmentMeta);
2039 }
2040 }
2041 }
2042
2043 customMaterial->setAlwaysDirty(m_alwaysDirty);
2044 if (m_srcBlend != BlendMode::NoBlend && m_dstBlend != BlendMode::NoBlend) { // both must be set to something other than NoBlend
2045 customMaterial->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Blending, true);
2046 customMaterial->m_srcBlend = toRhiBlendFactor(m_srcBlend);
2047 customMaterial->m_dstBlend = toRhiBlendFactor(m_dstBlend);
2048 // alpha blending is only active if rgb blending is
2049 if (m_srcAlphaBlend != BlendMode::NoBlend && m_dstAlphaBlend != BlendMode::NoBlend) {
2050 customMaterial->m_srcAlphaBlend = toRhiBlendFactor(m_srcAlphaBlend);
2051 customMaterial->m_dstAlphaBlend = toRhiBlendFactor(m_dstAlphaBlend);
2052 } else {
2053 customMaterial->m_srcAlphaBlend = customMaterial->m_srcBlend;
2054 customMaterial->m_dstAlphaBlend = customMaterial->m_dstBlend;
2055 }
2056 } else {
2057 customMaterial->m_renderFlags.setFlag(QSSGRenderCustomMaterial::RenderFlag::Blending, false);
2058 }
2059 customMaterial->m_lineWidth = m_lineWidth;
2060
2061 QQuick3DMaterial::updateSpatialNode(customMaterial);
2062
2063 if (m_dirtyAttributes & Dirty::PropertyDirty) {
2064 for (auto &prop : customMaterial->m_properties) {
2065 auto p = metaObject()->property(prop.pid);
2066 if (Q_LIKELY(p.isValid()))
2067 prop.value = p.read(this);
2068 }
2069 }
2070
2071 if (m_dirtyAttributes & Dirty::TextureDirty) {
2072 for (QSSGRenderCustomMaterial::TextureProperty &prop : customMaterial->m_textureProperties) {
2073 QQuick3DTexture *tex = prop.texInput->texture();
2074 if (tex) {
2075 if (prop.texInput->enabled)
2076 prop.texImage = tex->getRenderImage();
2077 else
2078 prop.texImage = nullptr;
2079 prop.minFilterType = tex->minFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
2080 : QSSGRenderTextureFilterOp::Linear;
2081 prop.magFilterType = tex->magFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
2082 : QSSGRenderTextureFilterOp::Linear;
2083 prop.mipFilterType = tex->generateMipmaps() ? (tex->mipFilter() == QQuick3DTexture::Nearest ? QSSGRenderTextureFilterOp::Nearest
2084 : QSSGRenderTextureFilterOp::Linear)
2085 : QSSGRenderTextureFilterOp::None;
2086 prop.horizontalClampType = tex->horizontalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
2087 : (tex->horizontalTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
2088 : QSSGRenderTextureCoordOp::MirroredRepeat;
2089 prop.verticalClampType = tex->verticalTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
2090 : (tex->verticalTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
2091 : QSSGRenderTextureCoordOp::MirroredRepeat;
2092 prop.zClampType = tex->depthTiling() == QQuick3DTexture::Repeat ? QSSGRenderTextureCoordOp::Repeat
2093 : (tex->depthTiling() == QQuick3DTexture::ClampToEdge) ? QSSGRenderTextureCoordOp::ClampToEdge
2094 : QSSGRenderTextureCoordOp::MirroredRepeat;
2095 } else {
2096 prop.texImage = nullptr;
2097 }
2098
2099 if (tex != prop.lastConnectedTexture) {
2100 prop.lastConnectedTexture = tex;
2101 disconnect(prop.minFilterChangedConn);
2102 disconnect(prop.magFilterChangedConn);
2103 disconnect(prop.mipFilterChangedConn);
2104 disconnect(prop.horizontalTilingChangedConn);
2105 disconnect(prop.verticalTilingChangedConn);
2106 disconnect(prop.depthTilingChangedConn);
2107 if (tex) {
2108 prop.minFilterChangedConn = connect(tex, &QQuick3DTexture::minFilterChanged, this, &QQuick3DCustomMaterial::onTextureDirty);
2109 prop.magFilterChangedConn = connect(tex, &QQuick3DTexture::magFilterChanged, this, &QQuick3DCustomMaterial::onTextureDirty);
2110 prop.mipFilterChangedConn = connect(tex, &QQuick3DTexture::mipFilterChanged, this, &QQuick3DCustomMaterial::onTextureDirty);
2111 prop.horizontalTilingChangedConn = connect(tex, &QQuick3DTexture::horizontalTilingChanged, this, &QQuick3DCustomMaterial::onTextureDirty);
2112 prop.verticalTilingChangedConn = connect(tex, &QQuick3DTexture::verticalTilingChanged, this, &QQuick3DCustomMaterial::onTextureDirty);
2113 prop.depthTilingChangedConn = connect(tex, &QQuick3DTexture::depthTilingChanged, this, &QQuick3DCustomMaterial::onTextureDirty);
2114 }
2115 }
2116 }
2117 }
2118
2119 m_dirtyAttributes = 0;
2120
2121 return customMaterial;
2122}
2123
2124void QQuick3DCustomMaterial::itemChange(QQuick3DObject::ItemChange change, const QQuick3DObject::ItemChangeData &value)
2125{
2126 QQuick3DMaterial::itemChange(change, value);
2127 if (change == QQuick3DObject::ItemSceneChange) {
2128 if (auto sceneManager = value.sceneManager) {
2129 for (const auto &it : std::as_const(m_dynamicTextureMaps)) {
2130 if (auto tex = it->texture())
2131 QQuick3DObjectPrivate::refSceneManager(tex, *sceneManager);
2132 }
2133 } else {
2134 for (const auto &it : std::as_const(m_dynamicTextureMaps)) {
2135 if (auto tex = it->texture())
2136 QQuick3DObjectPrivate::derefSceneManager(tex);
2137 }
2138 }
2139 }
2140}
2141
2142void QQuick3DCustomMaterial::onPropertyDirty()
2143{
2144 markDirty(*this, Dirty::PropertyDirty);
2145 update();
2146}
2147
2148void QQuick3DCustomMaterial::onTextureDirty()
2149{
2150 markDirty(*this, Dirty::TextureDirty);
2151 update();
2152}
2153
2154void QQuick3DCustomMaterial::setDynamicTextureMap(QQuick3DShaderUtilsTextureInput *textureMap)
2155{
2156 // There can only be one texture input per property, as the texture input is a combination
2157 // of the texture used and the uniform name!
2158 auto it = m_dynamicTextureMaps.constFind(textureMap);
2159
2160 if (it == m_dynamicTextureMaps.constEnd()) {
2161 // Track the object, if it's destroyed we need to remove it from our table.
2162 connect(textureMap, &QQuick3DShaderUtilsTextureInput::destroyed, this, [this, textureMap]() {
2163 auto it = m_dynamicTextureMaps.constFind(textureMap);
2164 if (it != m_dynamicTextureMaps.constEnd())
2165 m_dynamicTextureMaps.erase(it);
2166 });
2167 m_dynamicTextureMaps.insert(textureMap);
2168
2169 update();
2170 }
2171}
2172
2173QT_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)