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

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