Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qssgrenderdefaultmaterialshadergenerator.cpp
Go to the documentation of this file.
1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2019 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5/* clang-format off */
6
7#include <QtQuick3DUtils/private/qssgutils_p.h>
8
9#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
10#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgrendershadercodegenerator_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
13#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
15#include <QtQuick3DRuntimeRender/private/qssgrendershadowmap_p.h>
16#include <QtQuick3DRuntimeRender/private/qssgrendercustommaterial_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrendershaderlibrarymanager_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrendershaderkeys_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h>
20
21#include <QtCore/QByteArray>
22
24
25namespace {
27template<Type> struct ImageStrings {};
28#define DefineImageStrings(V) template<> struct ImageStrings<Type::V> \
29{\
30 static constexpr const char* sampler() { return "qt_"#V"Map_sampler"; }\
31 static constexpr const char* offsets() { return "qt_"#V"Map_offsets"; }\
32 static constexpr const char* rotations() { return "qt_"#V"Map_rotations"; }\
33 static constexpr const char* fragCoords1() { return "qt_"#V"Map_uv_coords1"; }\
34 static constexpr const char* fragCoords2() { return "qt_"#V"Map_uv_coords2"; }\
35 static constexpr const char* samplerSize() { return "qt_"#V"Map_size"; }\
36}
37
44DefineImageStrings(SpecularAmountMap);
46DefineImageStrings(Translucency);
53DefineImageStrings(ClearcoatRoughness);
54DefineImageStrings(ClearcoatNormal);
55DefineImageStrings(Transmission);
57
59{
60 const char *imageSampler;
61 const char *imageFragCoords;
63 const char *imageOffsets;
64 const char *imageRotations;
65 const char *imageSamplerSize;
66};
67
68#define DefineImageStringTableEntry(V) \
69 { ImageStrings<Type::V>::sampler(), ImageStrings<Type::V>::fragCoords1(), ImageStrings<Type::V>::fragCoords2(), \
70 ImageStrings<Type::V>::offsets(), ImageStrings<Type::V>::rotations(), ImageStrings<Type::V>::samplerSize() }
71
79 DefineImageStringTableEntry(SpecularAmountMap),
81 DefineImageStringTableEntry(Translucency),
88 DefineImageStringTableEntry(ClearcoatRoughness),
89 DefineImageStringTableEntry(ClearcoatNormal),
90 DefineImageStringTableEntry(Transmission),
92};
93
94const int TEXCOORD_VAR_LEN = 16;
95
96void textureCoordVaryingName(char (&outString)[TEXCOORD_VAR_LEN], quint8 uvSet)
97{
98 // For now, uvSet will be less than 2.
99 // But this value will be verified in the setProperty function.
100 Q_ASSERT(uvSet < 9);
101 qstrncpy(outString, "qt_varTexCoordX", TEXCOORD_VAR_LEN);
102 outString[14] = '0' + uvSet;
103}
104
105void textureCoordVariableName(char (&outString)[TEXCOORD_VAR_LEN], quint8 uvSet)
106{
107 // For now, uvSet will be less than 2.
108 // But this value will be verified in the setProperty function.
109 Q_ASSERT(uvSet < 9);
110 qstrncpy(outString, "qt_texCoordX", TEXCOORD_VAR_LEN);
111 outString[11] = '0' + uvSet;
112}
113
114}
115
117{
118 return imageStringTable[int(type)].imageSampler;
119}
120
121static void addLocalVariable(QSSGStageGeneratorBase &inGenerator, const QByteArray &inName, const QByteArray &inType)
122{
123 inGenerator << " " << inType << " " << inName << ";\n";
124}
125
126static QByteArray uvTransform(const QByteArray& imageRotations, const QByteArray& imageOffsets)
127{
129 transform = " qt_uTransform = vec3(" + imageRotations + ".x, " + imageRotations + ".y, " + imageOffsets + ".x);\n";
130 transform += " qt_vTransform = vec3(" + imageRotations + ".z, " + imageRotations + ".w, " + imageOffsets + ".y);\n";
131 return transform;
132}
133
134static void sanityCheckImageForSampler(const QSSGRenderableImage &image, const char *samplerName)
135{
136 if (image.m_imageNode.type == QSSGRenderGraphObject::Type::ImageCube) {
137 qWarning("Sampler %s expects a 2D texture but the associated texture is a cube map. "
138 "This will lead to problems.",
139 samplerName);
140 }
141}
142
144 QSSGStageGeneratorBase &fragmentShader,
147 bool forceFragmentShader = false,
148 quint32 uvSet = 0,
149 bool reuseImageCoords = false)
150{
151 const auto &names = imageStringTable[int(image.m_mapType)];
152 char textureCoordName[TEXCOORD_VAR_LEN];
154 fragmentShader.addUniform(names.imageSampler, "sampler2D");
155 if (!forceFragmentShader) {
156 vertexShader.addUniform(names.imageOffsets, "vec3");
157 vertexShader.addUniform(names.imageRotations, "vec4");
158 } else {
159 fragmentShader.addUniform(names.imageOffsets, "vec3");
160 fragmentShader.addUniform(names.imageRotations, "vec4");
161 }
162 QByteArray uvTrans = uvTransform(names.imageRotations, names.imageOffsets);
163 if (image.m_imageNode.m_mappingMode == QSSGRenderImage::MappingModes::Normal) {
164 if (!forceFragmentShader) {
165 vertexShader << uvTrans;
166 vertexShader.addOutgoing(names.imageFragCoords, "vec2");
167 vertexShader.addFunction("getTransformedUVCoords");
168 } else {
169 fragmentShader << uvTrans;
170 fragmentShader.addFunction("getTransformedUVCoords");
171 }
172 vertexShader.generateUVCoords(uvSet, key);
173 if (!forceFragmentShader) {
174 textureCoordVaryingName(textureCoordName, uvSet);
175 vertexShader << " vec2 " << names.imageFragCoordsTemp << " = qt_getTransformedUVCoords(vec3(" << textureCoordName << ", 1.0), qt_uTransform, qt_vTransform);\n";
176 vertexShader.assignOutput(names.imageFragCoords, names.imageFragCoordsTemp);
177 } else {
178 textureCoordVariableName(textureCoordName, uvSet);
179 if (reuseImageCoords)
180 fragmentShader << " ";
181 else
182 fragmentShader << " vec2 ";
183 fragmentShader << names.imageFragCoords << " = qt_getTransformedUVCoords(vec3(" << textureCoordName << ", 1.0), qt_uTransform, qt_vTransform);\n";
184 }
185 } else {
186 fragmentShader.addUniform(names.imageOffsets, "vec3");
187 fragmentShader.addUniform(names.imageRotations, "vec4");
188 fragmentShader << uvTrans;
189 vertexShader.generateEnvMapReflection(key);
190 fragmentShader.addFunction("getTransformedUVCoords");
191 if (reuseImageCoords)
192 fragmentShader << " ";
193 else
194 fragmentShader << " vec2 ";
195 fragmentShader << names.imageFragCoords << " = qt_getTransformedUVCoords(environment_map_reflection, qt_uTransform, qt_vTransform);\n";
196 }
197}
198
200 QSSGStageGeneratorBase &fragmentShader,
203 char (&outString)[TEXCOORD_VAR_LEN],
204 quint8 uvSet = 0)
205{
206 const auto &names = imageStringTable[int(image.m_mapType)];
208 fragmentShader.addUniform(names.imageSampler, "sampler2D");
209 // NOTE: Actually update the uniform name here
210 textureCoordVariableName(outString, uvSet);
211 vertexGenerator.generateUVCoords(uvSet, key);
212}
213
215 QSSGStageGeneratorBase &fragmentShader,
216 const QByteArray &inLightDir,
217 const QByteArray &inLightSpecColor)
218{
220 fragmentShader.addInclude("physGlossyBSDF.glsllib");
221 fragmentShader << " global_specular_light += qt_lightAttenuation * qt_shadow_map_occl * qt_specularAmount"
222 " * qt_kggxGlossyDefaultMtl(qt_world_normal, qt_tangent, -" << inLightDir << ".xyz, qt_view_vector, " << inLightSpecColor << ".rgb, qt_specularTint, qt_roughnessAmount).rgb;\n";
223 } else {
224 fragmentShader.addFunction("specularBSDF");
225 fragmentShader << " global_specular_light += qt_lightAttenuation * qt_shadow_map_occl * qt_specularAmount"
226 " * qt_specularBSDF(qt_world_normal, -" << inLightDir << ".xyz, qt_view_vector, " << inLightSpecColor << ".rgb, 2.56 / (qt_roughnessAmount + 0.01)).rgb;\n";
227 }
228}
229
233{
234 if (image == nullptr)
235 return;
236
237 infragmentShader.addFunction("diffuseReflectionWrapBSDF");
238 infragmentShader << " tmp_light_color = " << lightVarNames.lightColor << ".rgb * (1.0 - qt_metalnessAmount);\n";
239 infragmentShader << " global_diffuse_light.rgb += qt_lightAttenuation * qt_shadow_map_occl * qt_translucent_thickness_exp * qt_diffuseReflectionWrapBSDF(-qt_world_normal, -"
240 << lightVarNames.normalizedDirection << ", tmp_light_color, qt_material_properties2.w).rgb;\n";
241}
242
244
246{
247 if (lightIdx >= q3ds_shadowMapVariableNames.size())
248 q3ds_shadowMapVariableNames.resize(lightIdx + 1);
249
251 if (names.shadowMapStem.isEmpty()) {
252 names.shadowMapStem = QByteArrayLiteral("qt_shadowmap");
253 names.shadowCubeStem = QByteArrayLiteral("qt_shadowcube");
254 char buf[16];
255 qsnprintf(buf, 16, "%d", int(lightIdx));
256 names.shadowCubeStem.append(buf);
257 names.shadowMapStem.append(buf);
258 names.shadowMatrixStem = names.shadowMapStem;
259 names.shadowMatrixStem.append("_matrix");
260 names.shadowCoordStem = names.shadowMapStem;
261 names.shadowCoordStem.append("_coord");
262 names.shadowControlStem = names.shadowMapStem;
263 names.shadowControlStem.append("_control");
264 }
265
266 return names;
267}
268
269// this is for DefaultMaterial only
273 bool hasMetalness)
274{
275 if (keyProps.m_fresnelEnabled.getValue(inKey)) {
276 fragmentShader.addInclude("defaultMaterialFresnel.glsllib");
277 fragmentShader << " // Add fresnel ratio\n";
278 if (hasMetalness) { // this won't be hit in practice since DefaultMaterial does not offer metalness as a property
279 fragmentShader << " qt_specularAmount *= qt_defaultMaterialSimpleFresnel(qt_specularBase, qt_metalnessAmount, qt_world_normal, qt_view_vector, "
280 "qt_dielectricSpecular(qt_material_specular.w), qt_material_properties2.x);\n";
281 } else {
282 fragmentShader << " qt_specularAmount *= qt_defaultMaterialSimpleFresnelNoMetalness(qt_world_normal, qt_view_vector, "
283 "qt_dielectricSpecular(qt_material_specular.w), qt_material_properties2.x);\n";
284 }
285 }
286}
287
289{
290 Q_ASSERT(lightIdx > -1);
292
293 // See funcsampleLightVars.glsllib. Using an instance name (ubLights) is
294 // intentional. The only uniform block that does not have an instance name
295 // is cbMain (the main block with all default and custom material
296 // uniforms). Any other uniform block must have an instance name in order
297 // to avoid trouble with the OpenGL-targeted shaders generated by the
298 // shader pipeline (as those do not use uniform blocks, and in absence of a
299 // block instance name SPIR-Cross generates a struct uniform name based on
300 // whatever SPIR-V ID glslang made up for the variable - this can lead to
301 // clashes between the vertex and fragment shaders if there are blocks with
302 // different names (but no instance names) that are only present in one of
303 // the shaders). For cbMain the issue cannot happen since the exact same
304 // block is present in both shaders. For cbLights it is simple enough to
305 // use the correct prefix right here, so there is no reason not to use an
306 // instance name.
307 QByteArray lightStem = "ubLights.lights";
308 char buf[16];
309 qsnprintf(buf, 16, "[%d].", int(lightIdx));
310 lightStem.append(buf);
311
312 names.lightColor = lightStem;
313 names.lightColor.append("diffuse");
314 names.lightDirection = lightStem;
315 names.lightDirection.append("direction");
316 names.lightSpecularColor = lightStem;
317 names.lightSpecularColor.append("specular");
318 if (inLight.type == QSSGRenderLight::Type::PointLight) {
319 names.lightPos = lightStem;
320 names.lightPos.append("position");
321 names.lightConstantAttenuation = lightStem;
322 names.lightConstantAttenuation.append("constantAttenuation");
323 names.lightLinearAttenuation = lightStem;
324 names.lightLinearAttenuation.append("linearAttenuation");
325 names.lightQuadraticAttenuation = lightStem;
326 names.lightQuadraticAttenuation.append("quadraticAttenuation");
327 } else if (inLight.type == QSSGRenderLight::Type::SpotLight) {
328 names.lightPos = lightStem;
329 names.lightPos.append("position");
330 names.lightConstantAttenuation = lightStem;
331 names.lightConstantAttenuation.append("constantAttenuation");
332 names.lightLinearAttenuation = lightStem;
333 names.lightLinearAttenuation.append("linearAttenuation");
334 names.lightQuadraticAttenuation = lightStem;
335 names.lightQuadraticAttenuation.append("quadraticAttenuation");
336 names.lightConeAngle = lightStem;
337 names.lightConeAngle.append("coneAngle");
338 names.lightInnerConeAngle = lightStem;
339 names.lightInnerConeAngle.append("innerConeAngle");
340 }
341
342 return names;
343}
344
346 QSSGMaterialVertexPipeline &vertexShader,
347 quint32 lightIdx,
348 bool inShadowEnabled,
351 const QSSGShaderDefaultMaterialKey &inKey)
352{
353 if (inShadowEnabled) {
354 vertexShader.generateWorldPosition(inKey);
355 const auto names = setupShadowMapVariableNames(lightIdx);
356 fragmentShader.addInclude("shadowMapping.glsllib");
357 if (inType == QSSGRenderLight::Type::DirectionalLight) {
358 fragmentShader.addUniform(names.shadowMapStem, "sampler2D");
359 } else {
360 fragmentShader.addUniform(names.shadowCubeStem, "samplerCube");
361 }
362 fragmentShader.addUniform(names.shadowControlStem, "vec4");
363 fragmentShader.addUniform(names.shadowMatrixStem, "mat4");
364
365 if (inType != QSSGRenderLight::Type::DirectionalLight) {
366 fragmentShader << " qt_shadow_map_occl = qt_sampleCubemap(" << names.shadowCubeStem << ", " << names.shadowControlStem << ", " << names.shadowMatrixStem << ", " << lightVarNames.lightPos << ".xyz, qt_varWorldPos, vec2(1.0, " << names.shadowControlStem << ".z));\n";
367 } else {
368 fragmentShader << " qt_shadow_map_occl = qt_sampleOrthographic(" << names.shadowMapStem << ", " << names.shadowControlStem << ", " << names.shadowMatrixStem << ", qt_varWorldPos, vec2(1.0, " << names.shadowControlStem << ".z));\n";
369 }
370 } else {
371 fragmentShader << " qt_shadow_map_occl = 1.0;\n";
372 }
373}
374
376{
377 switch (inMaterial.type) {
378 case QSSGRenderGraphObject::Type::DefaultMaterial:
379 case QSSGRenderGraphObject::Type::PrincipledMaterial:
380 case QSSGRenderGraphObject::Type::SpecularGlossyMaterial:
381 return static_cast<const QSSGRenderDefaultMaterial &>(inMaterial).adapter;
382 case QSSGRenderGraphObject::Type::CustomMaterial:
383 return static_cast<const QSSGRenderCustomMaterial &>(inMaterial).adapter;
384 default:
385 break;
386 }
387 return nullptr;
388}
389
390// NOTE!!!: PLEASE ADD NEW VARS HERE!
392 { "DIFFUSE" },
393 { "BASE_COLOR" },
394 { "METALNESS" },
395 { "ROUGHNESS" },
396 { "EMISSIVE" },
397 { "SPECULAR_AMOUNT" },
398 { "EMISSIVE_COLOR" },
399 { "LIGHT_COLOR" },
400 { "LIGHT_ATTENUATION" },
401 { "SPOT_FACTOR" },
402 { "SHADOW_CONTRIB" },
403 { "FRESNEL_CONTRIB" },
404 { "TO_LIGHT_DIR" },
405 { "NORMAL" },
406 { "VIEW_VECTOR" },
407 { "TOTAL_AMBIENT_COLOR" },
408 { "COLOR_SUM" },
409 { "BINORMAL" },
410 { "TANGENT" },
411 { "FRESNEL_POWER" },
412 { "INSTANCE_MODEL_MATRIX" },
413 { "INSTANCE_MODELVIEWPROJECTION_MATRIX" },
414 { "UV0" },
415 { "UV1" },
416 { "VERTEX" }
417};
418
420{
421 return "inout vec3 DIFFUSE, in vec3 LIGHT_COLOR, in float SHADOW_CONTRIB, in vec3 TO_LIGHT_DIR, in vec3 NORMAL, in vec4 BASE_COLOR, in float METALNESS, in float ROUGHNESS, in vec3 VIEW_VECTOR";
422}
423
425{
426 return "inout vec3 DIFFUSE, in vec3 LIGHT_COLOR, in float LIGHT_ATTENUATION, in float SHADOW_CONTRIB, in vec3 TO_LIGHT_DIR, in vec3 NORMAL, in vec4 BASE_COLOR, in float METALNESS, in float ROUGHNESS, in vec3 VIEW_VECTOR";
427}
428
430{
431 return "inout vec3 DIFFUSE, in vec3 LIGHT_COLOR, in float LIGHT_ATTENUATION, float SPOT_FACTOR, in float SHADOW_CONTRIB, in vec3 TO_LIGHT_DIR, in vec3 NORMAL, in vec4 BASE_COLOR, in float METALNESS, in float ROUGHNESS, in vec3 VIEW_VECTOR";
432}
433
435{
436 return "inout vec3 DIFFUSE, in vec3 TOTAL_AMBIENT_COLOR, in vec3 NORMAL, in vec3 VIEW_VECTOR";
437}
438
440{
441 return "inout vec3 SPECULAR, in vec3 LIGHT_COLOR, in float LIGHT_ATTENUATION, in float SHADOW_CONTRIB, in vec3 FRESNEL_CONTRIB, in vec3 TO_LIGHT_DIR, in vec3 NORMAL, in vec4 BASE_COLOR, in float METALNESS, in float ROUGHNESS, in float SPECULAR_AMOUNT, in vec3 VIEW_VECTOR";
442}
443
445{
446 return "inout vec4 BASE_COLOR, inout vec3 EMISSIVE_COLOR, inout float METALNESS, inout float ROUGHNESS, inout float SPECULAR_AMOUNT, inout float FRESNEL_POWER, inout vec3 NORMAL, inout vec3 TANGENT, inout vec3 BINORMAL, in vec2 UV0, in vec2 UV1, in vec3 VIEW_VECTOR";
447}
448
450{
451 return "inout vec4 COLOR_SUM, in vec4 DIFFUSE, in vec3 SPECULAR, in vec3 EMISSIVE, in vec2 UV0, in vec2 UV1";
452}
453
455{
456 return "inout vec3 DIFFUSE, inout vec3 SPECULAR, in vec4 BASE_COLOR, in float AO_FACTOR, in float SPECULAR_AMOUNT, in float ROUGHNESS, in vec3 NORMAL, in vec3 VIEW_VECTOR, in mat3 IBL_ORIENTATION";
457}
458
460{
461 return "inout vec3 VERTEX, inout vec3 NORMAL, inout vec2 UV0, inout vec2 UV1, inout vec3 TANGENT, inout vec3 BINORMAL, inout ivec4 JOINTS, inout vec4 WEIGHTS, inout vec4 COLOR";
462}
463
465{
466 return "inout vec3 VERTEX, inout vec3 NORMAL, inout vec2 UV0, inout vec2 UV1, inout vec3 TANGENT, inout vec3 BINORMAL, inout ivec4 JOINTS, inout vec4 WEIGHTS, inout vec4 COLOR, inout mat4 INSTANCE_MODEL_MATRIX, inout mat4 INSTANCE_MODELVIEWPROJECTION_MATRIX";
467}
468
469#define MAX_MORPH_TARGET 8
470
471static bool hasCustomFunction(const QByteArray &funcName,
472 QSSGShaderMaterialAdapter *materialAdapter,
473 QSSGShaderLibraryManager &shaderLibraryManager)
474{
475 return materialAdapter->hasCustomShaderFunction(QSSGShaderCache::ShaderType::Fragment, funcName, shaderLibraryManager);
476}
477
480 QSSGShaderMaterialAdapter *materialAdapter)
481{
482 if (materialAdapter->isSpecularGlossy())
483 fragmentShader << " tmp_light_color = " << lightVarNames.lightColor << ".rgb;\n";
484 else
485 fragmentShader << " tmp_light_color = " << lightVarNames.lightColor << ".rgb * (1.0 - qt_metalnessAmount);\n";
486}
487
488static void handleSpecularLight(QSSGStageGeneratorBase &fragmentShader,
490 QSSGShaderMaterialAdapter *materialAdapter,
491 QSSGShaderLibraryManager &shaderLibraryManager,
492 bool usesSharedVar,
493 bool hasCustomFrag,
494 bool specularLightingEnabled,
495 bool enableClearcoat,
496 bool enableTransmission,
497 bool useNormalizedDirection)
498{
499 QByteArray directionToUse = useNormalizedDirection ? lightVarNames.normalizedDirection : lightVarNames.lightDirection;
500
501 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_specularLightProcessor"), materialAdapter, shaderLibraryManager))
502 {
503 // SPECULAR, LIGHT_COLOR, LIGHT_ATTENUATION, SHADOW_CONTRIB, FRESNEL_CONTRIB, TO_LIGHT_DIR, NORMAL, BASE_COLOR, METALNESS, ROUGHNESS, SPECULAR_AMOUNT, VIEW_VECTOR(, SHARED)
504 fragmentShader << " qt_specularLightProcessor(global_specular_light, " << lightVarNames.lightSpecularColor << ".rgb, qt_lightAttenuation, qt_shadow_map_occl, "
505 << "qt_specularAmount, -" << directionToUse << ".xyz, qt_world_normal, qt_customBaseColor, "
506 << "qt_metalnessAmount, qt_roughnessAmount, qt_customSpecularAmount, qt_view_vector";
507 if (usesSharedVar)
508 fragmentShader << ", qt_customShared);\n";
509 else
510 fragmentShader << ");\n";
511 }
512 else
513 {
514 if (specularLightingEnabled)
515 {
516 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy())
517 {
518 // Principled materials (and Custom without a specular processor function) always use GGX SpecularModel
519 fragmentShader.addFunction("specularGGXBSDF");
520 fragmentShader << " global_specular_light += qt_lightAttenuation * qt_shadow_map_occl * qt_specularTint"
521 " * qt_specularGGXBSDF(qt_world_normal, -"
522 << directionToUse << ".xyz, qt_view_vector, "
523 << lightVarNames.lightSpecularColor << ".rgb, qt_f0, qt_f90, qt_roughnessAmount).rgb;\n";
524 }
525 else
526 {
527 outputSpecularEquation(materialAdapter->specularModel(), fragmentShader, directionToUse, lightVarNames.lightSpecularColor);
528 }
529
530 if (enableClearcoat)
531 {
532 fragmentShader.addFunction("specularGGXBSDF");
533 fragmentShader << " qt_global_clearcoat += qt_lightAttenuation * qt_shadow_map_occl"
534 " * qt_specularGGXBSDF(qt_clearcoatNormal, -"
535 << directionToUse << ".xyz, qt_view_vector, "
536 << lightVarNames.lightSpecularColor << ".rgb, qt_clearcoatF0, qt_clearcoatF90, qt_clearcoatRoughness).rgb;\n";
537 }
538
539 if (enableTransmission)
540 {
541 fragmentShader << " {\n";
542 fragmentShader << " vec3 transmissionRay = qt_getVolumeTransmissionRay(qt_world_normal, qt_view_vector, qt_thicknessFactor, qt_material_specular.w);\n";
543 fragmentShader << " vec3 pointToLight = -" << directionToUse << ".xyz;\n";
544 fragmentShader << " pointToLight -= transmissionRay;\n";
545 fragmentShader << " vec3 l = normalize(pointToLight);\n";
546 fragmentShader << " vec3 intensity = vec3(1.0);\n"; // Directional light is always 1.0
547 fragmentShader << " vec3 transmittedLight = intensity * qt_getPunctualRadianceTransmission(qt_world_normal, "
548 "qt_view_vector, l, qt_roughnessAmount, qt_f0, qt_f90, qt_diffuseColor.rgb, qt_material_specular.w);\n";
549 fragmentShader << " transmittedLight = qt_applyVolumeAttenuation(transmittedLight, length(transmissionRay), "
550 "qt_attenuationColor, qt_attenuationDistance);\n";
551 fragmentShader << " qt_global_transmission += qt_transmissionFactor * transmittedLight;\n";
552 fragmentShader << " }\n";
553 }
554 }
555 }
556}
557
560 bool usesSharedVar,
561 bool hasCustomFrag,
562 QSSGShaderMaterialAdapter *materialAdapter,
563 QSSGShaderLibraryManager &shaderLibraryManager,
564 bool specularLightingEnabled,
565 bool enableClearcoat,
566 bool enableTransmission)
567{
568 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_directionalLightProcessor"), materialAdapter, shaderLibraryManager)) {
569 // DIFFUSE, LIGHT_COLOR, SHADOW_CONTRIB, TO_LIGHT_DIR, NORMAL, BASE_COLOR, METALNESS, ROUGHNESS, VIEW_VECTOR(, SHARED)
570 fragmentShader << " qt_directionalLightProcessor(global_diffuse_light.rgb, tmp_light_color, qt_shadow_map_occl, -"
571 << lightVarNames.lightDirection << ".xyz, qt_world_normal, qt_customBaseColor, "
572 << "qt_metalnessAmount, qt_roughnessAmount, qt_view_vector";
573 if (usesSharedVar)
574 fragmentShader << ", qt_customShared);\n";
575 else
576 fragmentShader << ");\n";
577 } else {
578 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
579 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * qt_shadow_map_occl * "
580 << "qt_diffuseBurleyBSDF(qt_world_normal, -" << lightVarNames.lightDirection << ".xyz, "
581 << "qt_view_vector, tmp_light_color, qt_roughnessAmount).rgb;\n";
582 } else {
583 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * qt_shadow_map_occl * qt_diffuseReflectionBSDF(qt_world_normal, -"
584 << lightVarNames.lightDirection << ".xyz, tmp_light_color).rgb;\n";
585 }
586 }
587
588 handleSpecularLight(fragmentShader,
589 lightVarNames,
590 materialAdapter,
591 shaderLibraryManager,
592 usesSharedVar,
593 hasCustomFrag,
594 specularLightingEnabled,
595 enableClearcoat,
596 enableTransmission,
597 false);
598}
599
600static void generateDirections(QSSGStageGeneratorBase &fragmentShader,
602 const QByteArray& lightVarPrefix,
603 QSSGMaterialVertexPipeline &vertexShader,
604 const QSSGShaderDefaultMaterialKey &inKey)
605{
606 vertexShader.generateWorldPosition(inKey);
607
608 lightVarNames.relativeDirection = lightVarPrefix;
609 lightVarNames.relativeDirection.append("relativeDirection");
610
611 lightVarNames.normalizedDirection = lightVarNames.relativeDirection;
612 lightVarNames.normalizedDirection.append("_normalized");
613
614 lightVarNames.relativeDistance = lightVarPrefix;
615 lightVarNames.relativeDistance.append("distance");
616
617 fragmentShader << " vec3 " << lightVarNames.relativeDirection << " = qt_varWorldPos - " << lightVarNames.lightPos << ".xyz;\n"
618 << " float " << lightVarNames.relativeDistance << " = length(" << lightVarNames.relativeDirection << ");\n"
619 << " vec3 " << lightVarNames.normalizedDirection << " = " << lightVarNames.relativeDirection << " / " << lightVarNames.relativeDistance << ";\n";
620
621}
622
623static void handlePointLight(QSSGStageGeneratorBase &fragmentShader,
625 QSSGShaderMaterialAdapter *materialAdapter,
626 QSSGShaderLibraryManager &shaderLibraryManager,
627 bool usesSharedVar,
628 bool hasCustomFrag,
629 bool specularLightingEnabled,
630 bool enableClearcoat,
631 bool enableTransmission)
632{
633 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_pointLightProcessor"), materialAdapter, shaderLibraryManager)) {
634 // DIFFUSE, LIGHT_COLOR, LIGHT_ATTENUATION, SHADOW_CONTRIB, TO_LIGHT_DIR, NORMAL, BASE_COLOR, METALNESS, ROUGHNESS, VIEW_VECTOR(, SHARED)
635 fragmentShader << " qt_pointLightProcessor(global_diffuse_light.rgb, tmp_light_color, qt_lightAttenuation, qt_shadow_map_occl, -"
636 << lightVarNames.normalizedDirection << ".xyz, qt_world_normal, qt_customBaseColor, "
637 << "qt_metalnessAmount, qt_roughnessAmount, qt_view_vector";
638 if (usesSharedVar)
639 fragmentShader << ", qt_customShared);\n";
640 else
641 fragmentShader << ");\n";
642 } else {
643 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
644 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * qt_lightAttenuation * qt_shadow_map_occl * "
645 << "qt_diffuseBurleyBSDF(qt_world_normal, -" << lightVarNames.normalizedDirection << ".xyz, qt_view_vector, "
646 << "tmp_light_color, qt_roughnessAmount).rgb;\n";
647 } else {
648 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * qt_lightAttenuation * qt_shadow_map_occl * "
649 << "qt_diffuseReflectionBSDF(qt_world_normal, -" << lightVarNames.normalizedDirection << ".xyz, tmp_light_color).rgb;\n";
650 }
651 }
652
653 handleSpecularLight(fragmentShader,
654 lightVarNames,
655 materialAdapter,
656 shaderLibraryManager,
657 usesSharedVar,
658 hasCustomFrag,
659 specularLightingEnabled,
660 enableClearcoat,
661 enableTransmission,
662 true);
663}
664
665static void handleSpotLight(QSSGStageGeneratorBase &fragmentShader,
667 const QByteArray& lightVarPrefix,
668 QSSGShaderMaterialAdapter *materialAdapter,
669 QSSGShaderLibraryManager &shaderLibraryManager,
670 bool usesSharedVar,
671 bool hasCustomFrag,
672 bool specularLightingEnabled,
673 bool enableClearcoat,
674 bool enableTransmission)
675{
676 lightVarNames.spotAngle = lightVarPrefix;
677 lightVarNames.spotAngle.append("spotAngle");
678
679 fragmentShader << " float " << lightVarNames.spotAngle << " = dot(" << lightVarNames.normalizedDirection
680 << ", normalize(vec3(" << lightVarNames.lightDirection << ")));\n";
681 fragmentShader << " if (" << lightVarNames.spotAngle << " > " << lightVarNames.lightConeAngle << ") {\n";
682 fragmentShader << " float spotFactor = smoothstep(" << lightVarNames.lightConeAngle
683 << ", " << lightVarNames.lightInnerConeAngle << ", " << lightVarNames.spotAngle
684 << ");\n";
685
686 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_spotLightProcessor"), materialAdapter, shaderLibraryManager)) {
687 // DIFFUSE, LIGHT_COLOR, LIGHT_ATTENUATION, SPOT_FACTOR, SHADOW_CONTRIB, TO_LIGHT_DIR, NORMAL, BASE_COLOR, METALNESS, ROUGHNESS, VIEW_VECTOR(, SHARED)
688 fragmentShader << " qt_spotLightProcessor(global_diffuse_light.rgb, tmp_light_color, qt_lightAttenuation, spotFactor, qt_shadow_map_occl, -"
689 << lightVarNames.normalizedDirection << ".xyz, qt_world_normal, qt_customBaseColor, "
690 << "qt_metalnessAmount, qt_roughnessAmount, qt_view_vector";
691 if (usesSharedVar)
692 fragmentShader << ", qt_customShared);\n";
693 else
694 fragmentShader << ");\n";
695 } else {
696 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
697 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * spotFactor * qt_lightAttenuation * qt_shadow_map_occl * "
698 << "qt_diffuseBurleyBSDF(qt_world_normal, -" << lightVarNames.normalizedDirection << ".xyz, qt_view_vector, "
699 << "tmp_light_color, qt_roughnessAmount).rgb;\n";
700 } else {
701 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * spotFactor * qt_lightAttenuation * qt_shadow_map_occl * "
702 << "qt_diffuseReflectionBSDF(qt_world_normal, -" << lightVarNames.normalizedDirection << ".xyz, tmp_light_color).rgb;\n";
703 }
704 }
705
706 // spotFactor is multipled to qt_lightAttenuation and have an effect on the specularLight.
707 fragmentShader << " qt_lightAttenuation *= spotFactor;\n";
708
709 handleSpecularLight(fragmentShader,
710 lightVarNames,
711 materialAdapter,
712 shaderLibraryManager,
713 usesSharedVar,
714 hasCustomFrag,
715 specularLightingEnabled,
716 enableClearcoat,
717 enableTransmission,
718 true);
719
720 fragmentShader << " }\n";
721}
722
725{
726 fragmentShader.addFunction("calculatePointLightAttenuation");
727
728 fragmentShader << " qt_lightAttenuation = qt_calculatePointLightAttenuation(vec3("
729 << lightVarNames.lightConstantAttenuation << ", " << lightVarNames.lightLinearAttenuation << ", "
730 << lightVarNames.lightQuadraticAttenuation << "), " << lightVarNames.relativeDistance << ");\n";
731}
732
734 QSSGMaterialVertexPipeline &vertexShader,
735 const QSSGShaderDefaultMaterialKey &inKey,
736 const QSSGRenderGraphObject &inMaterial,
737 const QSSGShaderLightListView &lights,
738 QSSGShaderLibraryManager &shaderLibraryManager,
739 QSSGRenderableImage *translucencyImage,
740 bool hasCustomFrag,
741 bool usesSharedVar,
742 bool enableLightmap,
743 bool enableShadowMaps,
744 bool specularLightingEnabled,
745 bool enableClearcoat,
746 bool enableTransmission)
747{
748 QSSGShaderMaterialAdapter *materialAdapter = getMaterialAdapter(inMaterial);
749
750 // Iterate through all lights
751 Q_ASSERT(lights.size() < INT32_MAX);
752
753 int shadowMapCount = 0;
754
755 for (qint32 lightIdx = 0; lightIdx < lights.size(); ++lightIdx) {
756 auto &shaderLight = lights[lightIdx];
757 QSSGRenderLight *lightNode = shaderLight.light;
758
759 if (enableLightmap && lightNode->m_fullyBaked)
760 continue;
761
762 auto lightVarNames = setupLightVariableNames(lightIdx, *lightNode);
763
764 const bool isDirectional = lightNode->type == QSSGRenderLight::Type::DirectionalLight;
765 const bool isSpot = lightNode->type == QSSGRenderLight::Type::SpotLight;
766 bool castsShadow = enableShadowMaps && lightNode->m_castShadow && shadowMapCount < QSSG_MAX_NUM_SHADOW_MAPS;
767 if (castsShadow)
768 ++shadowMapCount;
769
770 fragmentShader.append("");
771 char lightIdxStr[11];
772 snprintf(lightIdxStr, 11, "%d", lightIdx);
773
774 QByteArray lightVarPrefix = "light";
775 lightVarPrefix.append(lightIdxStr);
776
777 fragmentShader << " //Light " << lightIdxStr << (isDirectional ? " [directional]" : isSpot ? " [spot]" : " [point]") << "\n";
778
779 lightVarPrefix.append("_");
780
781 generateShadowMapOcclusion(fragmentShader, vertexShader, lightIdx, castsShadow, lightNode->type, lightVarNames, inKey);
782
783 generateTempLightColor(fragmentShader, lightVarNames, materialAdapter);
784
785 if (isDirectional) {
786 handleDirectionalLight(fragmentShader,
787 lightVarNames,
788 usesSharedVar,
789 hasCustomFrag,
790 materialAdapter,
791 shaderLibraryManager,
792 specularLightingEnabled,
793 enableClearcoat,
794 enableTransmission);
795 } else {
796 generateDirections(fragmentShader, lightVarNames, lightVarPrefix, vertexShader, inKey);
797
798 calculatePointLightAttenuation(fragmentShader, lightVarNames);
799
800 addTranslucencyIrradiance(fragmentShader, translucencyImage, lightVarNames);
801
802 if (isSpot) {
803 handleSpotLight(fragmentShader,
804 lightVarNames,
805 lightVarPrefix,
806 materialAdapter,
807 shaderLibraryManager,
808 usesSharedVar,
809 hasCustomFrag,
810 specularLightingEnabled,
811 enableClearcoat,
812 enableTransmission);
813 } else {
814 handlePointLight(fragmentShader,
815 lightVarNames,
816 materialAdapter,
817 shaderLibraryManager,
818 usesSharedVar,
819 hasCustomFrag,
820 specularLightingEnabled,
821 enableClearcoat,
822 enableTransmission);
823 }
824 }
825 }
826
827 fragmentShader.append("");
828}
829
831 QSSGMaterialVertexPipeline &vertexShader,
832 const QSSGShaderDefaultMaterialKey &inKey,
834 const QSSGShaderFeatures &featureSet,
835 const QSSGRenderGraphObject &inMaterial,
836 const QSSGShaderLightListView &lights,
837 QSSGRenderableImage *firstImage,
838 QSSGShaderLibraryManager &shaderLibraryManager)
839{
840 QSSGShaderMaterialAdapter *materialAdapter = getMaterialAdapter(inMaterial);
841 auto hasCustomFunction = [&shaderLibraryManager, materialAdapter](const QByteArray &funcName) {
843 funcName,
844 shaderLibraryManager);
845 };
846
847 bool metalnessEnabled = materialAdapter->isMetalnessEnabled(); // always true for Custom, true if > 0 with Principled
848
849 // alwayas true for Custom,
850 // true if vertexColorsEnabled, usesInstancing and blendParticles for others
851 bool vertexColorsEnabled = materialAdapter->isVertexColorsEnabled()
852 || keyProps.m_usesInstancing.getValue(inKey)
853 || keyProps.m_blendParticles.getValue(inKey);
854
855 bool hasLighting = materialAdapter->hasLighting();
856 bool isDoubleSided = keyProps.m_isDoubleSided.getValue(inKey);
857 bool hasImage = firstImage != nullptr;
858
859 bool hasIblProbe = keyProps.m_hasIbl.getValue(inKey);
860 bool specularLightingEnabled = metalnessEnabled || materialAdapter->isSpecularEnabled() || hasIblProbe; // always true for Custom, depends for others
861 bool specularAAEnabled = keyProps.m_specularAAEnabled.getValue(inKey);
862 quint32 numMorphTargets = keyProps.m_targetCount.getValue(inKey);
863 // Pull the bump out as
864 QSSGRenderableImage *bumpImage = nullptr;
865 quint32 imageIdx = 0;
866 QSSGRenderableImage *specularAmountImage = nullptr;
867 QSSGRenderableImage *roughnessImage = nullptr;
868 QSSGRenderableImage *metalnessImage = nullptr;
869 QSSGRenderableImage *occlusionImage = nullptr;
870 // normal mapping
871 QSSGRenderableImage *normalImage = nullptr;
872 // translucency map
873 QSSGRenderableImage *translucencyImage = nullptr;
874 // opacity map
875 QSSGRenderableImage *opacityImage = nullptr;
876 // height map
877 QSSGRenderableImage *heightImage = nullptr;
878 // clearcoat maps
879 QSSGRenderableImage *clearcoatImage = nullptr;
880 QSSGRenderableImage *clearcoatRoughnessImage = nullptr;
881 QSSGRenderableImage *clearcoatNormalImage = nullptr;
882 // transmission map
883 QSSGRenderableImage *transmissionImage = nullptr;
884 // thickness
885 QSSGRenderableImage *thicknessImage = nullptr;
886
887 QSSGRenderableImage *baseImage = nullptr;
888
889 // Use shared texcoord when transforms are identity
890 QVector<QSSGRenderableImage *> identityImages;
891 char imageFragCoords[TEXCOORD_VAR_LEN];
892
893 auto channelStr = [](const QSSGShaderKeyTextureChannel &chProp, const QSSGShaderDefaultMaterialKey &inKey) -> QByteArray {
895 switch (chProp.getTextureChannel(inKey)) {
897 ret.append(".r");
898 break;
900 ret.append(".g");
901 break;
903 ret.append(".b");
904 break;
906 ret.append(".a");
907 break;
908 }
909 return ret;
910 };
911
912 for (QSSGRenderableImage *img = firstImage; img != nullptr; img = img->m_nextImage, ++imageIdx) {
913 if (img->m_imageNode.isImageTransformIdentity())
914 identityImages.push_back(img);
915 if (img->m_mapType == QSSGRenderableImage::Type::BaseColor || img->m_mapType == QSSGRenderableImage::Type::Diffuse) {
916 baseImage = img;
917 } else if (img->m_mapType == QSSGRenderableImage::Type::Bump) {
918 bumpImage = img;
919 } else if (img->m_mapType == QSSGRenderableImage::Type::SpecularAmountMap) {
920 specularAmountImage = img;
921 } else if (img->m_mapType == QSSGRenderableImage::Type::Roughness) {
922 roughnessImage = img;
923 } else if (img->m_mapType == QSSGRenderableImage::Type::Metalness) {
924 metalnessImage = img;
925 } else if (img->m_mapType == QSSGRenderableImage::Type::Occlusion) {
926 occlusionImage = img;
927 } else if (img->m_mapType == QSSGRenderableImage::Type::Normal) {
928 normalImage = img;
929 } else if (img->m_mapType == QSSGRenderableImage::Type::Translucency) {
930 translucencyImage = img;
931 } else if (img->m_mapType == QSSGRenderableImage::Type::Opacity) {
932 opacityImage = img;
933 } else if (img->m_mapType == QSSGRenderableImage::Type::Height) {
934 heightImage = img;
935 } else if (img->m_mapType == QSSGRenderableImage::Type::Clearcoat) {
936 clearcoatImage = img;
937 } else if (img->m_mapType == QSSGRenderableImage::Type::ClearcoatRoughness) {
938 clearcoatRoughnessImage = img;
939 } else if (img->m_mapType == QSSGRenderableImage::Type::ClearcoatNormal) {
940 clearcoatNormalImage = img;
941 } else if (img->m_mapType == QSSGRenderableImage::Type::Transmission) {
942 transmissionImage = img;
943 } else if (img->m_mapType == QSSGRenderableImage::Type::Thickness) {
944 thicknessImage = img;
945 }
946 }
947
948 const bool isDepthPass = featureSet.isSet(QSSGShaderFeatures::Feature::DepthPass);
949 const bool isOrthoShadowPass = featureSet.isSet(QSSGShaderFeatures::Feature::OrthoShadowPass);
950 const bool isCubeShadowPass = featureSet.isSet(QSSGShaderFeatures::Feature::CubeShadowPass);
951 const bool isOpaqueDepthPrePass = featureSet.isSet(QSSGShaderFeatures::Feature::OpaqueDepthPrePass);
952 const bool hasIblOrientation = featureSet.isSet(QSSGShaderFeatures::Feature::IblOrientation);
953 bool enableShadowMaps = featureSet.isSet(QSSGShaderFeatures::Feature::Ssm);
954 bool enableSSAO = featureSet.isSet(QSSGShaderFeatures::Feature::Ssao);
955 bool enableLightmap = featureSet.isSet(QSSGShaderFeatures::Feature::Lightmap);
956 bool hasReflectionProbe = featureSet.isSet(QSSGShaderFeatures::Feature::ReflectionProbe);
957 bool enableBumpNormal = normalImage || bumpImage;
958 bool genBumpNormalImageCoords = false;
959 bool enableParallaxMapping = heightImage != nullptr;
960 const bool enableClearcoat = materialAdapter->isClearcoatEnabled();
961 const bool enableTransmission = materialAdapter->isTransmissionEnabled();
962
963 specularLightingEnabled |= specularAmountImage != nullptr;
964 specularLightingEnabled |= hasReflectionProbe;
965
966 const bool hasCustomVert = materialAdapter->hasCustomShaderSnippet(QSSGShaderCache::ShaderType::Vertex);
967 auto debugMode = QSSGRenderLayer::MaterialDebugMode(keyProps.m_debugMode.getValue(inKey));
968 const bool enableFog = keyProps.m_fogEnabled.getValue(inKey);
969
970 // Morphing
971 if (numMorphTargets > 0 || hasCustomVert) {
972 vertexShader.addDefinition(QByteArrayLiteral("QT_MORPH_MAX_COUNT"),
973 QByteArray::number(numMorphTargets));
975 if ((offset = keyProps.m_targetPositionOffset.getValue(inKey)) < UINT8_MAX) {
976 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_POSITION_OFFSET"),
978 }
979 if ((offset = keyProps.m_targetNormalOffset.getValue(inKey)) < UINT8_MAX) {
980 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_NORMAL_OFFSET"),
982 }
983 if ((offset = keyProps.m_targetTangentOffset.getValue(inKey)) < UINT8_MAX) {
984 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_TANGENT_OFFSET"),
986 }
987 if ((offset = keyProps.m_targetBinormalOffset.getValue(inKey)) < UINT8_MAX) {
988 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_BINORMAL_OFFSET"),
990 }
991 if ((offset = keyProps.m_targetTexCoord0Offset.getValue(inKey)) < UINT8_MAX) {
992 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_TEX0_OFFSET"),
994 }
995 if ((offset = keyProps.m_targetTexCoord1Offset.getValue(inKey)) < UINT8_MAX) {
996 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_TEX1_OFFSET"),
998 }
999 if ((offset = keyProps.m_targetColorOffset.getValue(inKey)) < UINT8_MAX) {
1000 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_COLOR_OFFSET"),
1002 }
1003 }
1004
1005 bool includeCustomFragmentMain = true;
1006 if (isDepthPass || isOrthoShadowPass || isCubeShadowPass) {
1007 hasLighting = false;
1008 enableSSAO = false;
1009 enableShadowMaps = false;
1010 enableLightmap = false;
1011
1012 metalnessEnabled = false;
1013 specularLightingEnabled = false;
1014
1015 if (!isOpaqueDepthPrePass) {
1016 vertexColorsEnabled = false;
1017 baseImage = nullptr;
1018 includeCustomFragmentMain = false;
1019 }
1020 }
1021
1022 bool includeSSAOVars = enableSSAO || enableShadowMaps;
1023
1024 vertexShader.beginFragmentGeneration(shaderLibraryManager);
1025
1026 // Unshaded custom materials need no code in main (apart from calling qt_customMain)
1027 const bool hasCustomFrag = materialAdapter->hasCustomShaderSnippet(QSSGShaderCache::ShaderType::Fragment);
1028 const bool usesSharedVar = materialAdapter->usesSharedVariables();
1029 if (hasCustomFrag && materialAdapter->isUnshaded())
1030 return;
1031
1032 // hasCustomFrag == Shaded custom material from this point on, for Unshaded we returned above
1033
1034 // The fragment or vertex shaders may not use the material_properties or diffuse
1035 // uniforms in all cases but it is simpler to just add them and let the linker strip them.
1036 fragmentShader.addUniform("qt_material_emissive_color", "vec3");
1037 fragmentShader.addUniform("qt_material_base_color", "vec4");
1038 fragmentShader.addUniform("qt_material_properties", "vec4");
1039 fragmentShader.addUniform("qt_material_properties2", "vec4");
1040 fragmentShader.addUniform("qt_material_properties3", "vec4");
1041 if (enableParallaxMapping || enableTransmission)
1042 fragmentShader.addUniform("qt_material_properties4", "vec4");
1043 if (enableTransmission) {
1044 fragmentShader.addUniform("qt_material_attenuation", "vec4");
1045 fragmentShader.addUniform("qt_material_thickness", "float");
1046 }
1047
1048 if (vertexColorsEnabled)
1049 vertexShader.generateVertexColor(inKey);
1050 else
1051 fragmentShader.append(" vec4 qt_vertColor = vec4(1.0);");
1052
1053 if (hasImage && ((!isDepthPass && !isOrthoShadowPass && !isCubeShadowPass) || isOpaqueDepthPrePass)) {
1054 fragmentShader.append(" vec3 qt_uTransform;");
1055 fragmentShader.append(" vec3 qt_vTransform;");
1056 }
1057
1058 if (hasLighting || hasCustomFrag) {
1059 // Do not move these three. These varyings are exposed to custom material shaders too.
1060 vertexShader.generateViewVector(inKey);
1061 if (keyProps.m_usesProjectionMatrix.getValue(inKey))
1062 fragmentShader.addUniform("qt_projectionMatrix", "mat4");
1063 if (keyProps.m_usesInverseProjectionMatrix.getValue(inKey))
1064 fragmentShader.addUniform("qt_inverseProjectionMatrix", "mat4");
1065 vertexShader.generateWorldNormal(inKey);
1066 vertexShader.generateWorldPosition(inKey);
1067
1068 const bool usingDefaultMaterialSpecularGGX = !(materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) && materialAdapter->specularModel() == QSSGRenderDefaultMaterial::MaterialSpecularModel::KGGX;
1069 // Note: tangetOrBinormalDebugMode doesn't force generation, it just makes sure that qt_tangent and qt_binormal variables exist
1070 const bool tangentOrBinormalDebugMode = (debugMode == QSSGRenderLayer::MaterialDebugMode::Tangent) || (debugMode == QSSGRenderLayer::MaterialDebugMode::Binormal);
1071 const bool needsTangentAndBinormal = hasCustomFrag || enableParallaxMapping || clearcoatNormalImage || enableBumpNormal || usingDefaultMaterialSpecularGGX || tangentOrBinormalDebugMode;
1072
1073
1074 if (needsTangentAndBinormal) {
1075 bool genTangent = false;
1076 bool genBinormal = false;
1077 vertexShader.generateVarTangentAndBinormal(inKey, genTangent, genBinormal);
1078
1079 if (enableBumpNormal && !genTangent) {
1080 // Generate imageCoords for bump/normal map first.
1081 // Some operations needs to use the TBN transform and if the
1082 // tangent vector is not provided, it is necessary.
1083 auto *bumpNormalImage = bumpImage != nullptr ? bumpImage : normalImage;
1084 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *bumpNormalImage, true, bumpNormalImage->m_imageNode.m_indexUV);
1085 genBumpNormalImageCoords = true;
1086
1087 int id = (bumpImage != nullptr) ? int(QSSGRenderableImage::Type::Bump) : int(QSSGRenderableImage::Type::Normal);
1088 const auto &names = imageStringTable[id];
1089 fragmentShader << " vec2 dUVdx = dFdx(" << names.imageFragCoords << ");\n"
1090 << " vec2 dUVdy = dFdy(" << names.imageFragCoords << ");\n";
1091 fragmentShader << " qt_tangent = (dUVdy.y * dFdx(qt_varWorldPos) - dUVdx.y * dFdy(qt_varWorldPos)) / (dUVdx.x * dUVdy.y - dUVdx.y * dUVdy.x);\n"
1092 << " qt_tangent = qt_tangent - dot(qt_world_normal, qt_tangent) * qt_world_normal;\n"
1093 << " qt_tangent = normalize(qt_tangent);\n";
1094 }
1095 if (!genBinormal)
1096 fragmentShader << " qt_binormal = cross(qt_world_normal, qt_tangent);\n";
1097 }
1098
1099 if (isDoubleSided) {
1100 fragmentShader.append(" const float qt_facing = gl_FrontFacing ? 1.0 : -1.0;\n");
1101 fragmentShader.append(" qt_world_normal *= qt_facing;\n");
1102 if (needsTangentAndBinormal) {
1103 fragmentShader.append(" qt_tangent *= qt_facing;");
1104 fragmentShader.append(" qt_binormal *= qt_facing;");
1105 }
1106 }
1107 }
1108
1109 if (hasCustomFrag) {
1110 // A custom shaded material is effectively a principled material for
1111 // our purposes here. The defaults are different from a
1112 // PrincipledMaterial however, since this is more sensible here.
1113 // (because the shader has to state it to get things)
1114
1115 if (usesSharedVar)
1116 fragmentShader << " QT_SHARED_VARS qt_customShared;\n";
1117 // These should match the defaults of PrincipledMaterial.
1118 fragmentShader << " float qt_customSpecularAmount = 0.5;\n"; // overrides qt_material_properties.x
1119 fragmentShader << " float qt_customSpecularRoughness = 0.0;\n"; // overrides qt_material_properties.y
1120 fragmentShader << " float qt_customMetalnessAmount = 0.0;\n"; // overrides qt_material_properties.z
1121 fragmentShader << " float qt_customFresnelPower = 5.0;\n"; // overrides qt_material_properties2.x
1122 fragmentShader << " vec4 qt_customBaseColor = vec4(1.0);\n"; // overrides qt_material_base_color
1123 fragmentShader << " vec3 qt_customEmissiveColor = vec3(0.0);\n"; // overrides qt_material_emissive_color
1124 // Generate the varyings for UV0 and UV1 since customer materials don't use image
1125 // properties directly.
1126 vertexShader.generateUVCoords(0, inKey);
1127 vertexShader.generateUVCoords(1, inKey);
1128 if (includeCustomFragmentMain && hasCustomFunction(QByteArrayLiteral("qt_customMain"))) {
1129 fragmentShader << " qt_customMain(qt_customBaseColor, qt_customEmissiveColor, qt_customMetalnessAmount, qt_customSpecularRoughness,"
1130 " qt_customSpecularAmount, qt_customFresnelPower, qt_world_normal, qt_tangent, qt_binormal,"
1131 " qt_texCoord0, qt_texCoord1, qt_view_vector";
1132 if (usesSharedVar)
1133 fragmentShader << ", qt_customShared);\n";
1134 else
1135 fragmentShader << ");\n";
1136 }
1137 fragmentShader << " vec4 qt_diffuseColor = qt_customBaseColor * qt_vertColor;\n";
1138 fragmentShader << " vec3 qt_global_emission = qt_customEmissiveColor;\n";
1139 } else {
1140 fragmentShader << " vec4 qt_diffuseColor = qt_material_base_color * qt_vertColor;\n";
1141 fragmentShader << " vec3 qt_global_emission = qt_material_emissive_color;\n";
1142 }
1143 const bool hasCustomIblProbe = hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_iblProbeProcessor"));
1144
1145 if (isDepthPass)
1146 fragmentShader << " vec4 fragOutput = vec4(0.0);\n";
1147
1148 if (isOrthoShadowPass)
1149 vertexShader.generateDepth();
1150
1151 if (isCubeShadowPass)
1152 vertexShader.generateShadowWorldPosition(inKey);
1153
1154 // !hasLighting does not mean 'no light source'
1155 // it should be KHR_materials_unlit
1156 // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
1157 if (hasLighting) {
1158 if (includeSSAOVars)
1159 fragmentShader.addInclude("ssao.glsllib");
1160
1161 if (enableLightmap) {
1162 vertexShader.generateLightmapUVCoords(inKey);
1163 fragmentShader.addFunction("lightmap");
1164 }
1165
1166 fragmentShader.addFunction("sampleLightVars");
1167 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy())
1168 fragmentShader.addFunction("diffuseBurleyBSDF");
1169 else
1170 fragmentShader.addFunction("diffuseReflectionBSDF");
1171
1172 if (enableParallaxMapping) {
1173 // Adjust UV coordinates to account for parallaxMapping before
1174 // reading any other texture.
1175 const bool hasIdentityMap = identityImages.contains(heightImage);
1176 if (hasIdentityMap)
1177 generateImageUVSampler(vertexShader, fragmentShader, inKey, *heightImage, imageFragCoords, heightImage->m_imageNode.m_indexUV);
1178 else
1179 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *heightImage, true, heightImage->m_imageNode.m_indexUV);
1180 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Height)];
1181 fragmentShader.addInclude("parallaxMapping.glsllib");
1182 fragmentShader << " qt_texCoord0 = qt_parallaxMapping(" << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ", " << names.imageSampler <<", qt_tangent, qt_binormal, qt_world_normal, qt_varWorldPos, qt_cameraPosition, qt_material_properties4.x, qt_material_properties4.y, qt_material_properties4.z);\n";
1183 }
1184
1185 // Clearcoat Setup (before normalImage code has a change to overwrite qt_world_normal)
1186 if (enableClearcoat) {
1187 addLocalVariable(fragmentShader, "qt_clearcoatNormal", "vec3");
1188 // Clearcoat normal should be calculated not considering the normalImage for the base material
1189 // If both are to be the same then just set the same normalImage for the base and clearcoat
1190 // This does mean that this value should be calculated before qt_world_normal is overwritten by
1191 // the normalMap.
1192 if (clearcoatNormalImage) {
1193 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *clearcoatNormalImage, enableParallaxMapping, clearcoatNormalImage->m_imageNode.m_indexUV);
1194 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::ClearcoatNormal)];
1195 fragmentShader.addFunction("sampleNormalTexture");
1196 // no special normal scaling, assume 1.0
1197 fragmentShader << " qt_clearcoatNormal = qt_sampleNormalTexture3(" << names.imageSampler << ", 1.0, " << names.imageFragCoords << ", qt_tangent, qt_binormal, qt_world_normal);\n";
1198
1199 } else {
1200 // same as qt_world_normal then
1201 fragmentShader << " qt_clearcoatNormal = qt_world_normal;\n";
1202 }
1203 }
1204
1205 if (bumpImage != nullptr) {
1206 if (enableParallaxMapping || !genBumpNormalImageCoords) {
1207 generateImageUVCoordinates(vertexShader, fragmentShader, inKey,
1208 *bumpImage, enableParallaxMapping,
1209 bumpImage->m_imageNode.m_indexUV,
1210 genBumpNormalImageCoords);
1211 }
1212 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Bump)];
1213 fragmentShader.addUniform(names.imageSamplerSize, "vec2");
1214 fragmentShader.append(" float bumpAmount = qt_material_properties2.y;\n");
1215 fragmentShader.addInclude("defaultMaterialBumpNoLod.glsllib");
1216 fragmentShader << " qt_world_normal = qt_defaultMaterialBumpNoLod(" << names.imageSampler << ", bumpAmount, " << names.imageFragCoords << ", qt_tangent, qt_binormal, qt_world_normal, " << names.imageSamplerSize << ");\n";
1217 } else if (normalImage != nullptr) {
1218 if (enableParallaxMapping || !genBumpNormalImageCoords) {
1219 generateImageUVCoordinates(vertexShader, fragmentShader, inKey,
1220 *normalImage, enableParallaxMapping,
1221 normalImage->m_imageNode.m_indexUV,
1222 genBumpNormalImageCoords);
1223 }
1224 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Normal)];
1225 fragmentShader.append(" float normalStrength = qt_material_properties2.y;\n");
1226 fragmentShader.addFunction("sampleNormalTexture");
1227 fragmentShader << " qt_world_normal = qt_sampleNormalTexture3(" << names.imageSampler << ", normalStrength, " << names.imageFragCoords << ", qt_tangent, qt_binormal, qt_world_normal);\n";
1228 }
1229
1230 fragmentShader.append(" vec3 tmp_light_color;");
1231 }
1232
1233 if (specularLightingEnabled || hasImage) {
1234 fragmentShader.append(" vec3 qt_specularBase;");
1235 fragmentShader.addUniform("qt_material_specular", "vec4");
1236 if (hasCustomFrag)
1237 fragmentShader.append(" vec3 qt_specularTint = vec3(1.0);");
1238 else
1239 fragmentShader.append(" vec3 qt_specularTint = qt_material_specular.rgb;");
1240 }
1241
1242 if (baseImage) {
1243 const bool hasIdentityMap = identityImages.contains(baseImage);
1244 if (hasIdentityMap)
1245 generateImageUVSampler(vertexShader, fragmentShader, inKey, *baseImage, imageFragCoords, baseImage->m_imageNode.m_indexUV);
1246 else
1247 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *baseImage, enableParallaxMapping, baseImage->m_imageNode.m_indexUV);
1248
1249 // NOTE: The base image hande is used for both the diffuse map and the base color map, so we can't hard-code the type here...
1250 const auto &names = imageStringTable[int(baseImage->m_mapType)];
1251 // Diffuse and BaseColor maps need to converted to linear color space
1252 fragmentShader.addInclude("tonemapping.glsllib");
1253 fragmentShader << " vec4 qt_base_texture_color = qt_sRGBToLinear(texture2D(" << names.imageSampler << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << "));\n";
1254 fragmentShader << " qt_diffuseColor *= qt_base_texture_color;\n";
1255 }
1256
1257 // alpha cutoff
1259 // The Implementation Notes from
1260 // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#alpha-coverage
1261 // must be met. Hence the discard.
1262 fragmentShader << " if (qt_diffuseColor.a < qt_material_properties3.y) {\n"
1263 << " qt_diffuseColor = vec4(0.0);\n"
1264 << " discard;\n"
1265 << " } else {\n"
1266 << " qt_diffuseColor.a = 1.0;\n"
1267 << " }\n";
1268 } else if (materialAdapter->alphaMode() == QSSGRenderDefaultMaterial::MaterialAlphaMode::Opaque) {
1269 fragmentShader << " qt_diffuseColor.a = 1.0;\n";
1270 }
1271
1272 if (opacityImage) {
1273 const bool hasIdentityMap = identityImages.contains(opacityImage);
1274 if (hasIdentityMap)
1275 generateImageUVSampler(vertexShader, fragmentShader, inKey, *opacityImage, imageFragCoords, opacityImage->m_imageNode.m_indexUV);
1276 else
1277 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *opacityImage, enableParallaxMapping, opacityImage->m_imageNode.m_indexUV);
1278
1279 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Opacity)];
1281 fragmentShader << " qt_objectOpacity *= texture2D(" << names.imageSampler << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1282 }
1283
1284 if (hasLighting) {
1285 if (specularLightingEnabled) {
1286 vertexShader.generateViewVector(inKey);
1287 fragmentShader.addUniform("qt_material_properties", "vec4");
1288
1289 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy())
1290 fragmentShader << " qt_specularBase = vec3(1.0);\n";
1291 else
1292 fragmentShader << " qt_specularBase = qt_diffuseColor.rgb;\n";
1293 if (hasCustomFrag)
1294 fragmentShader << " float qt_specularFactor = qt_customSpecularAmount;\n";
1295 else
1296 fragmentShader << " float qt_specularFactor = qt_material_properties.x;\n";
1297 }
1298
1299 // Metalness must be setup fairly earily since so many factors depend on the runtime value
1300 if (hasCustomFrag)
1301 fragmentShader << " float qt_metalnessAmount = qt_customMetalnessAmount;\n";
1302 else if (!materialAdapter->isSpecularGlossy())
1303 fragmentShader << " float qt_metalnessAmount = qt_material_properties.z;\n";
1304 else
1305 fragmentShader << " float qt_metalnessAmount = 0.0;\n";
1306
1307 if (specularLightingEnabled && metalnessImage) {
1309 const bool hasIdentityMap = identityImages.contains(metalnessImage);
1310 if (hasIdentityMap)
1311 generateImageUVSampler(vertexShader, fragmentShader, inKey, *metalnessImage, imageFragCoords, metalnessImage->m_imageNode.m_indexUV);
1312 else
1313 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *metalnessImage, enableParallaxMapping, metalnessImage->m_imageNode.m_indexUV);
1314
1315 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Metalness)];
1316 fragmentShader << " float qt_sampledMetalness = texture2D(" << names.imageSampler << ", "
1317 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1318 fragmentShader << " qt_metalnessAmount = clamp(qt_metalnessAmount * qt_sampledMetalness, 0.0, 1.0);\n";
1319 }
1320
1321 fragmentShader.addUniform("qt_light_ambient_total", "vec3");
1322
1323 fragmentShader.append(" vec4 global_diffuse_light = vec4(0.0);");
1324
1325 if (enableLightmap) {
1326 fragmentShader << " global_diffuse_light.rgb = qt_lightmap_color(qt_texCoordLightmap) * (1.0 - qt_metalnessAmount) * qt_diffuseColor.rgb;\n";
1327 } else {
1328 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_ambientLightProcessor"))) {
1329 // DIFFUSE, TOTAL_AMBIENT_COLOR, NORMAL, VIEW_VECTOR(, SHARED)
1330 fragmentShader.append(" qt_ambientLightProcessor(global_diffuse_light.rgb, qt_light_ambient_total.rgb * (1.0 - qt_metalnessAmount) * qt_diffuseColor.rgb, qt_world_normal, qt_view_vector");
1331 if (usesSharedVar)
1332 fragmentShader << ", qt_customShared);\n";
1333 else
1334 fragmentShader << ");\n";
1335 } else {
1336 fragmentShader.append(" global_diffuse_light = vec4(qt_light_ambient_total.rgb * (1.0 - qt_metalnessAmount) * qt_diffuseColor.rgb, 0.0);");
1337 }
1338 }
1339
1340 fragmentShader.append(" vec3 global_specular_light = vec3(0.0);");
1341
1342 if (!lights.isEmpty() || hasCustomFrag) {
1343 fragmentShader.append(" float qt_shadow_map_occl = 1.0;");
1344 fragmentShader.append(" float qt_lightAttenuation = 1.0;");
1345 }
1346
1347 // Fragment lighting means we can perhaps attenuate the specular amount by a texture
1348 // lookup.
1349 if (specularAmountImage) {
1350 const bool hasIdentityMap = identityImages.contains(specularAmountImage);
1351 if (hasIdentityMap)
1352 generateImageUVSampler(vertexShader, fragmentShader, inKey, *specularAmountImage, imageFragCoords, specularAmountImage->m_imageNode.m_indexUV);
1353 else
1354 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *specularAmountImage, enableParallaxMapping, specularAmountImage->m_imageNode.m_indexUV);
1355
1356 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::SpecularAmountMap)];
1357 fragmentShader << " qt_specularBase *= qt_sRGBToLinear(texture2D(" << names.imageSampler << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")).rgb;\n";
1358 }
1359
1360 if (specularLightingEnabled) {
1361 if (materialAdapter->isSpecularGlossy()) {
1362 fragmentShader << " qt_specularTint *= qt_specularBase;\n";
1363 fragmentShader << " vec3 qt_specularAmount = vec3(1.0);\n";
1364 } else {
1365 fragmentShader << " vec3 qt_specularAmount = qt_specularBase * vec3(qt_metalnessAmount + qt_specularFactor * (1.0 - qt_metalnessAmount));\n";
1366 }
1367 }
1368
1369 if (translucencyImage != nullptr) {
1370 const bool hasIdentityMap = identityImages.contains(translucencyImage);
1371 if (hasIdentityMap)
1372 generateImageUVSampler(vertexShader, fragmentShader, inKey, *translucencyImage, imageFragCoords);
1373 else
1374 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *translucencyImage, enableParallaxMapping);
1375
1376 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Translucency)];
1378 fragmentShader << " float qt_translucent_depth_range = texture2D(" << names.imageSampler
1379 << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1380 fragmentShader << " float qt_translucent_thickness = qt_translucent_depth_range * qt_translucent_depth_range;\n";
1381 fragmentShader << " float qt_translucent_thickness_exp = exp(qt_translucent_thickness * qt_material_properties2.z);\n";
1382 }
1383
1384 addLocalVariable(fragmentShader, "qt_aoFactor", "float");
1385
1386 if (enableSSAO)
1387 fragmentShader.append(" qt_aoFactor = qt_screenSpaceAmbientOcclusionFactor();");
1388 else
1389 fragmentShader.append(" qt_aoFactor = 1.0;");
1390
1391 if (hasCustomFrag)
1392 fragmentShader << " float qt_roughnessAmount = qt_customSpecularRoughness;\n";
1393 else
1394 fragmentShader << " float qt_roughnessAmount = qt_material_properties.y;\n";
1395
1396 // Occlusion Map
1397 if (occlusionImage) {
1398 addLocalVariable(fragmentShader, "qt_ao", "float");
1400 const bool hasIdentityMap = identityImages.contains(occlusionImage);
1401 if (hasIdentityMap)
1402 generateImageUVSampler(vertexShader, fragmentShader, inKey, *occlusionImage, imageFragCoords, occlusionImage->m_imageNode.m_indexUV);
1403 else
1404 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *occlusionImage, enableParallaxMapping, occlusionImage->m_imageNode.m_indexUV);
1405 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Occlusion)];
1406 fragmentShader << " qt_ao = texture2D(" << names.imageSampler << ", "
1407 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1408 fragmentShader << " qt_aoFactor *= qt_ao * qt_material_properties3.x;\n";
1409 }
1410
1411 if (specularLightingEnabled && roughnessImage) {
1413 const bool hasIdentityMap = identityImages.contains(roughnessImage);
1414 if (hasIdentityMap)
1415 generateImageUVSampler(vertexShader, fragmentShader, inKey, *roughnessImage, imageFragCoords, roughnessImage->m_imageNode.m_indexUV);
1416 else
1417 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *roughnessImage, enableParallaxMapping, roughnessImage->m_imageNode.m_indexUV);
1418
1419 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Roughness)];
1420 fragmentShader << " qt_roughnessAmount *= texture2D(" << names.imageSampler << ", "
1421 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1422 }
1423
1424 // Convert Glossy to Roughness
1425 if (materialAdapter->isSpecularGlossy())
1426 fragmentShader << " qt_roughnessAmount = clamp(1.0 - qt_roughnessAmount, 0.0, 1.0);\n";
1427
1428 if (enableClearcoat) {
1429 addLocalVariable(fragmentShader, "qt_clearcoatAmount", "float");
1430 addLocalVariable(fragmentShader, "qt_clearcoatRoughness", "float");
1431 addLocalVariable(fragmentShader, "qt_clearcoatF0", "vec3");
1432 addLocalVariable(fragmentShader, "qt_clearcoatF90", "vec3");
1433 addLocalVariable(fragmentShader, "qt_global_clearcoat", "vec3");
1434
1435 fragmentShader << " qt_clearcoatAmount = qt_material_properties3.z;\n";
1436 fragmentShader << " qt_clearcoatRoughness = qt_material_properties3.w;\n";
1437 fragmentShader << " qt_clearcoatF0 = vec3(((1.0-qt_material_specular.w) * (1.0-qt_material_specular.w)) / ((1.0+qt_material_specular.w) * (1.0+qt_material_specular.w)));\n";
1438 fragmentShader << " qt_clearcoatF90 = vec3(1.0);\n";
1439 fragmentShader << " qt_global_clearcoat = vec3(0.0);\n";
1440
1441 if (clearcoatImage) {
1443 const bool hasIdentityMap = identityImages.contains(clearcoatImage);
1444 if (hasIdentityMap)
1445 generateImageUVSampler(vertexShader, fragmentShader, inKey, *clearcoatImage, imageFragCoords, clearcoatImage->m_imageNode.m_indexUV);
1446 else
1447 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *clearcoatImage, enableParallaxMapping, clearcoatImage->m_imageNode.m_indexUV);
1448 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Clearcoat)];
1449 fragmentShader << " qt_clearcoatAmount *= texture2D(" << names.imageSampler << ", "
1450 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1451 }
1452
1453 if (clearcoatRoughnessImage) {
1455 const bool hasIdentityMap = identityImages.contains(clearcoatRoughnessImage);
1456 if (hasIdentityMap)
1457 generateImageUVSampler(vertexShader, fragmentShader, inKey, *clearcoatRoughnessImage, imageFragCoords, clearcoatRoughnessImage->m_imageNode.m_indexUV);
1458 else
1459 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *clearcoatRoughnessImage, enableParallaxMapping, clearcoatRoughnessImage->m_imageNode.m_indexUV);
1460 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::ClearcoatRoughness)];
1461 fragmentShader << " qt_clearcoatRoughness *= texture2D(" << names.imageSampler << ", "
1462 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1463 fragmentShader << " qt_clearcoatRoughness = clamp(qt_clearcoatRoughness, 0.0, 1.0);\n";
1464 }
1465 }
1466
1467 if (enableTransmission) {
1468 fragmentShader.addInclude("transmission.glsllib");
1469 addLocalVariable(fragmentShader, "qt_transmissionFactor", "float");
1470 addLocalVariable(fragmentShader, "qt_global_transmission", "vec3");
1471 fragmentShader << " qt_transmissionFactor = qt_material_properties4.w;\n";
1472 fragmentShader << " qt_global_transmission = vec3(0.0);\n";
1473
1474 if (transmissionImage) {
1476 const bool hasIdentityMap = identityImages.contains(transmissionImage);
1477 if (hasIdentityMap)
1478 generateImageUVSampler(vertexShader, fragmentShader, inKey, *transmissionImage, imageFragCoords, transmissionImage->m_imageNode.m_indexUV);
1479 else
1480 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *transmissionImage, enableParallaxMapping, transmissionImage->m_imageNode.m_indexUV);
1481 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Transmission)];
1482 fragmentShader << " qt_transmissionFactor *= texture2D(" << names.imageSampler << ", "
1483 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1484 }
1485
1486 // Volume
1487 addLocalVariable(fragmentShader, "qt_thicknessFactor", "float");
1488 addLocalVariable(fragmentShader, "qt_attenuationColor", "vec3");
1489 addLocalVariable(fragmentShader, "qt_attenuationDistance", "float");
1490
1491 fragmentShader << " qt_thicknessFactor = qt_material_thickness;\n";
1492 fragmentShader << " qt_attenuationColor = qt_material_attenuation.xyz;\n";
1493 fragmentShader << " qt_attenuationDistance = qt_material_attenuation.w;\n";
1494
1495 if (thicknessImage) {
1497 const bool hasIdentityMap = identityImages.contains(thicknessImage);
1498 if (hasIdentityMap)
1499 generateImageUVSampler(vertexShader, fragmentShader, inKey, *thicknessImage, imageFragCoords, thicknessImage->m_imageNode.m_indexUV);
1500 else
1501 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *thicknessImage, enableParallaxMapping, thicknessImage->m_imageNode.m_indexUV);
1502 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Thickness)];
1503 fragmentShader << " qt_thicknessFactor *= texture2D(" << names.imageSampler << ", "
1504 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1505 }
1506 }
1507
1508 if (specularLightingEnabled) {
1509 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
1510 fragmentShader.addInclude("principledMaterialFresnel.glsllib");
1511 const bool useF90 = !lights.isEmpty() || enableTransmission;
1512 addLocalVariable(fragmentShader, "qt_f0", "vec3");
1513 if (useF90)
1514 addLocalVariable(fragmentShader, "qt_f90", "vec3");
1515 if (materialAdapter->isPrincipled()) {
1516 fragmentShader << " qt_f0 = qt_F0_ior(qt_material_specular.w, qt_metalnessAmount, qt_diffuseColor.rgb);\n";
1517 if (useF90)
1518 fragmentShader << " qt_f90 = vec3(1.0);\n";
1519 } else {
1520 addLocalVariable(fragmentShader, "qt_reflectance", "float");
1521
1522 fragmentShader << " qt_reflectance = max(max(qt_specularTint.r, qt_specularTint.g), qt_specularTint.b);\n";
1523 fragmentShader << " qt_f0 = qt_specularTint;\n";
1524 fragmentShader << " qt_specularTint = vec3(1.0);\n";
1525 if (useF90)
1526 fragmentShader << " qt_f90 = vec3(clamp(qt_reflectance * 50.0, 0.0, 1.0));\n";
1527 fragmentShader << " qt_diffuseColor.rgb *= (1 - qt_reflectance);\n";
1528 }
1529
1530 if (specularAAEnabled) {
1531 fragmentShader.append(" vec3 vNormalWsDdx = dFdx(qt_world_normal.xyz);\n");
1532 fragmentShader.append(" vec3 vNormalWsDdy = dFdy(qt_world_normal.xyz);\n");
1533 fragmentShader.append(" float flGeometricRoughnessFactor = pow(clamp(max(dot(vNormalWsDdx, vNormalWsDdx), dot(vNormalWsDdy, vNormalWsDdy)), 0.0, 1.0), 0.333);\n");
1534 fragmentShader.append(" qt_roughnessAmount = max(flGeometricRoughnessFactor, qt_roughnessAmount);\n");
1535 }
1536
1537 if (hasCustomFrag)
1538 fragmentShader << " float qt_fresnelPower = qt_customFresnelPower;\n";
1539 else
1540 fragmentShader << " float qt_fresnelPower = qt_material_properties2.x;\n";
1541
1542 if (materialAdapter->isPrincipled()) {
1543 fragmentShader << " qt_specularAmount *= qt_principledMaterialFresnel(qt_world_normal, qt_view_vector, "
1544 << "qt_f0, qt_roughnessAmount, qt_fresnelPower);\n";
1545
1546 // Make sure that we scale the specularTint with repsect to metalness (no tint if qt_metalnessAmount == 1)
1547 // We actually need to do this here because we won't know the final metalness value until this point.
1548 fragmentShader << " qt_specularTint = mix(vec3(1.0), qt_specularTint, 1.0 - qt_metalnessAmount);\n";
1549 } else {
1550 fragmentShader << " qt_specularAmount *= qt_principledMaterialFresnel(qt_world_normal, qt_view_vector, "
1551 << "qt_f0, qt_roughnessAmount, qt_fresnelPower);\n";
1552 }
1553 } else {
1554 Q_ASSERT(!hasCustomFrag);
1555 fragmentShader.addInclude("defaultMaterialFresnel.glsllib");
1556 fragmentShader << " qt_diffuseColor.rgb *= (1.0 - qt_dielectricSpecular(qt_material_specular.w)) * (1.0 - qt_metalnessAmount);\n";
1557 maybeAddMaterialFresnel(fragmentShader, keyProps, inKey, metalnessEnabled);
1558 }
1559 }
1560
1561 if (!lights.isEmpty()) {
1562 generateMainLightCalculation(fragmentShader,
1563 vertexShader,
1564 inKey,
1565 inMaterial,
1566 lights,
1567 shaderLibraryManager,
1568 translucencyImage,
1569 hasCustomFrag,
1570 usesSharedVar,
1571 enableLightmap,
1572 enableShadowMaps,
1573 specularLightingEnabled,
1574 enableClearcoat,
1575 enableTransmission);
1576 }
1577
1578 // The color in rgb is ready, including shadowing, just need to apply
1579 // the ambient occlusion factor. The alpha is the model opacity
1580 // multiplied by the alpha from the material color and/or the vertex colors.
1581 fragmentShader << " global_diffuse_light = vec4(global_diffuse_light.rgb * qt_aoFactor, qt_objectOpacity * qt_diffuseColor.a);\n";
1582
1583 if (hasReflectionProbe) {
1584 vertexShader.generateWorldNormal(inKey);
1585 fragmentShader.addInclude("sampleReflectionProbe.glsllib");
1586
1587 fragmentShader << " vec3 qt_reflectionDiffuse = vec3(0.0);\n";
1588 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
1589 fragmentShader << " qt_reflectionDiffuse = qt_diffuseColor.rgb * (1.0 - qt_specularAmount) * qt_sampleDiffuseReflection(qt_reflectionMap, qt_world_normal).rgb;\n";
1590 } else {
1591 fragmentShader << " qt_reflectionDiffuse = qt_diffuseColor.rgb * qt_sampleDiffuseReflection(qt_reflectionMap, qt_world_normal).rgb;\n";
1592 }
1593
1594 if (specularLightingEnabled) {
1595 fragmentShader << " vec3 qt_reflectionSpecular = vec3(0.0);\n";
1596 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
1597 fragmentShader << " qt_reflectionSpecular = "
1598 << "qt_specularTint * qt_sampleGlossyReflectionPrincipled(qt_reflectionMap, qt_world_normal, qt_view_vector, qt_specularAmount, qt_roughnessAmount).rgb;\n";
1599 } else {
1600 fragmentShader << " qt_reflectionSpecular = qt_specularAmount * "
1601 << "qt_specularTint * qt_sampleGlossyReflection(qt_reflectionMap, qt_world_normal, qt_view_vector, qt_roughnessAmount).rgb;\n";
1602 }
1603 }
1604 if (enableClearcoat) {
1605 fragmentShader << " vec3 qt_iblClearcoat = qt_sampleGlossyReflectionPrincipled(qt_reflectionMap, qt_clearcoatNormal, qt_view_vector, qt_clearcoatF0, qt_clearcoatRoughness).rgb;\n";
1606 }
1607
1608 fragmentShader << " global_diffuse_light.rgb += qt_reflectionDiffuse;\n";
1609 if (specularLightingEnabled)
1610 fragmentShader << " global_specular_light += qt_reflectionSpecular;\n";
1611 if (enableClearcoat)
1612 fragmentShader << " qt_global_clearcoat += qt_iblClearcoat;\n";
1613 } else if (hasIblProbe) {
1614 vertexShader.generateWorldNormal(inKey);
1615 fragmentShader.addInclude("sampleProbe.glsllib");
1616 if (hasCustomIblProbe) {
1617 // DIFFUSE, SPECULAR, BASE_COLOR, AO_FACTOR, SPECULAR_AMOUNT, NORMAL, VIEW_VECTOR, IBL_ORIENTATION(, SHARED)
1618 fragmentShader << " vec3 qt_iblDiffuse = vec3(0.0);\n";
1619 fragmentShader << " vec3 qt_iblSpecular = vec3(0.0);\n";
1620 fragmentShader << " qt_iblProbeProcessor(qt_iblDiffuse, qt_iblSpecular, qt_customBaseColor, qt_aoFactor, qt_specularFactor, qt_roughnessAmount, qt_world_normal, qt_view_vector";
1621 if (hasIblOrientation)
1622 fragmentShader << ", qt_lightProbeOrientation";
1623 else
1624 fragmentShader << ", mat3(1.0)";
1625 if (usesSharedVar)
1626 fragmentShader << ", qt_customShared);\n";
1627 else
1628 fragmentShader << ");\n";
1629 } else {
1630 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
1631 fragmentShader << " vec3 qt_iblDiffuse = qt_diffuseColor.rgb * (1.0 - qt_specularAmount) * qt_sampleDiffuse(qt_world_normal).rgb;\n";
1632 } else {
1633 fragmentShader << " vec3 qt_iblDiffuse = qt_diffuseColor.rgb * qt_sampleDiffuse(qt_world_normal).rgb;\n";
1634 }
1635 if (specularLightingEnabled) {
1636 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularEnabled()) {
1637 fragmentShader << " vec3 qt_iblSpecular = "
1638 << "qt_specularTint * qt_sampleGlossyPrincipled(qt_world_normal, qt_view_vector, qt_specularAmount, qt_roughnessAmount).rgb;\n";
1639 } else {
1640 fragmentShader << " vec3 qt_iblSpecular = qt_specularAmount * "
1641 << "qt_specularTint * qt_sampleGlossy(qt_world_normal, qt_view_vector, qt_roughnessAmount).rgb;\n";
1642 }
1643 }
1644 if (enableClearcoat) {
1645 fragmentShader << " vec3 qt_iblClearcoat = qt_sampleGlossyPrincipled(qt_clearcoatNormal, qt_view_vector, qt_clearcoatF0, qt_clearcoatRoughness).rgb;\n";
1646 }
1647 }
1648
1649 fragmentShader << " global_diffuse_light.rgb += qt_iblDiffuse * qt_aoFactor;\n";
1650 if (specularLightingEnabled)
1651 fragmentShader << " global_specular_light += qt_iblSpecular * qt_aoFactor;\n";
1652 if (enableClearcoat)
1653 fragmentShader << " qt_global_clearcoat += qt_iblClearcoat * qt_aoFactor;\n";
1654 } else if (hasCustomIblProbe) {
1655 // Prevent breaking the fragment code while seeking uniforms
1656 fragmentShader.addUniform("qt_lightProbe", "samplerCube");
1657 fragmentShader.addUniform("qt_lightProbeProperties", "vec4");
1658 }
1659
1660 // This can run even without a IBL probe
1661 if (enableTransmission) {
1662 fragmentShader << " qt_global_transmission += qt_transmissionFactor * qt_getIBLVolumeRefraction(qt_world_normal, qt_view_vector, qt_roughnessAmount, "
1663 "qt_diffuseColor.rgb, qt_specularAmount, qt_varWorldPos, qt_material_specular.w, qt_thicknessFactor, qt_attenuationColor, qt_attenuationDistance);\n";
1664 }
1665
1666 if (hasImage) {
1667 bool texColorDeclared = false;
1668 for (QSSGRenderableImage *image = firstImage; image; image = image->m_nextImage) {
1669 // map types other than these 2 are handled elsewhere
1670 if (image->m_mapType != QSSGRenderableImage::Type::Specular
1671 && image->m_mapType != QSSGRenderableImage::Type::Emissive)
1672 {
1673 continue;
1674 }
1675
1676 if (!texColorDeclared) {
1677 fragmentShader.append(" vec4 qt_texture_color;");
1678 texColorDeclared = true;
1679 }
1680
1681 const bool hasIdentityMap = identityImages.contains(image);
1682 if (hasIdentityMap)
1683 generateImageUVSampler(vertexShader, fragmentShader, inKey, *image, imageFragCoords, image->m_imageNode.m_indexUV);
1684 else
1685 generateImageUVCoordinates(vertexShader, fragmentShader, inKey, *image, enableParallaxMapping, image->m_imageNode.m_indexUV);
1686
1687 const auto &names = imageStringTable[int(image->m_mapType)];
1688 fragmentShader << " qt_texture_color = texture2D(" << names.imageSampler
1689 << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ");\n";
1690
1691 switch (image->m_mapType) {
1692 case QSSGRenderableImage::Type::Specular:
1693 fragmentShader.addInclude("tonemapping.glsllib");
1694 fragmentShader.append(" global_specular_light += qt_sRGBToLinear(qt_texture_color.rgb) * qt_specularTint;");
1695 fragmentShader.append(" global_diffuse_light.a *= qt_texture_color.a;");
1696 break;
1697 case QSSGRenderableImage::Type::Emissive:
1698 fragmentShader.addInclude("tonemapping.glsllib");
1699 fragmentShader.append(" qt_global_emission *= qt_sRGBToLinear(qt_texture_color.rgb);");
1700 break;
1701 default:
1702 Q_ASSERT(false);
1703 break;
1704 }
1705 }
1706 }
1707
1708 if (enableTransmission)
1709 fragmentShader << " global_diffuse_light.rgb = mix(global_diffuse_light.rgb, qt_global_transmission, qt_transmissionFactor);\n";
1710
1711 if (materialAdapter->isPrincipled()) {
1712 fragmentShader << " global_diffuse_light.rgb *= 1.0 - qt_metalnessAmount;\n";
1713 }
1714
1715 if (enableFog) {
1716 fragmentShader.addInclude("fog.glsllib");
1717 fragmentShader << " calculateFog(qt_global_emission, global_specular_light, global_diffuse_light.rgb);\n";
1718 }
1719
1720 fragmentShader << " vec4 qt_color_sum = vec4(global_diffuse_light.rgb + global_specular_light + qt_global_emission, global_diffuse_light.a);\n";
1721
1722 if (enableClearcoat) {
1723 fragmentShader.addInclude("bsdf.glsllib");
1724 fragmentShader << " vec3 qt_clearcoatFresnel = qt_schlick3(qt_clearcoatF0, qt_clearcoatF90, clamp(dot(qt_clearcoatNormal, qt_view_vector), 0.0, 1.0));\n";
1725 fragmentShader << " qt_global_clearcoat = qt_global_clearcoat * qt_clearcoatAmount;\n";
1726 fragmentShader << " qt_color_sum.rgb = qt_color_sum.rgb * (1.0 - qt_clearcoatAmount * qt_clearcoatFresnel) + qt_global_clearcoat;\n";
1727 }
1728
1729 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_customPostProcessor"))) {
1730 // COLOR_SUM, DIFFUSE, SPECULAR, EMISSIVE, UV0, UV1(, SHARED)
1731 fragmentShader << " qt_customPostProcessor(qt_color_sum, global_diffuse_light, global_specular_light, qt_global_emission, qt_texCoord0, qt_texCoord1";
1732 if (usesSharedVar)
1733 fragmentShader << ", qt_customShared);\n";
1734 else
1735 fragmentShader << ");\n";
1736 }
1737
1738 Q_ASSERT(!isDepthPass && !isOrthoShadowPass && !isCubeShadowPass);
1739 fragmentShader.addInclude("tonemapping.glsllib");
1740 fragmentShader.append(" fragOutput = vec4(qt_tonemap(qt_color_sum));");
1741
1742 // Debug Overrides for viewing various parts of the shading process
1744 fragmentShader.append(" vec3 debugOutput = vec3(0.0);\n");
1745 switch (debugMode) {
1747 fragmentShader.append(" debugOutput += qt_tonemap(qt_diffuseColor.rgb);\n");
1748 break;
1750 fragmentShader.append(" debugOutput += vec3(qt_roughnessAmount);\n");
1751 break;
1753 fragmentShader.append(" debugOutput += vec3(qt_metalnessAmount);\n");
1754 break;
1756 fragmentShader.append(" debugOutput += qt_tonemap(global_diffuse_light.rgb);\n");
1757 break;
1759 fragmentShader.append(" debugOutput += qt_tonemap(global_specular_light);\n");
1760 break;
1762 fragmentShader.append(" debugOutput += vec3(qt_shadow_map_occl);\n");
1763 break;
1765 fragmentShader.append(" debugOutput += qt_tonemap(qt_global_emission);\n");
1766 break;
1768 fragmentShader.append(" debugOutput += vec3(qt_aoFactor);\n");
1769 break;
1771 fragmentShader.append(" debugOutput += qt_world_normal * 0.5 + 0.5;\n");
1772 break;
1774 fragmentShader.append(" debugOutput += qt_tangent * 0.5 + 0.5;\n");
1775 break;
1777 fragmentShader.append(" debugOutput += qt_binormal * 0.5 + 0.5;\n");
1778 break;
1780 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy())
1781 fragmentShader.append(" debugOutput += qt_f0;");
1782 break;
1784 Q_UNREACHABLE();
1785 break;
1786 }
1787 fragmentShader.append(" fragOutput = vec4(debugOutput, 1.0);\n");
1788 }
1789 } else {
1790 if ((isOrthoShadowPass || isCubeShadowPass || isDepthPass) && isOpaqueDepthPrePass) {
1791 fragmentShader << " if ((qt_diffuseColor.a * qt_objectOpacity) < 1.0)\n";
1792 fragmentShader << " discard;\n";
1793 }
1794
1795 if (isOrthoShadowPass) {
1796 fragmentShader.addUniform("qt_shadowDepthAdjust", "vec2");
1797 fragmentShader << " // directional shadow pass\n"
1798 << " float qt_shadowDepth = (qt_varDepth + qt_shadowDepthAdjust.x) * qt_shadowDepthAdjust.y;\n"
1799 << " fragOutput = vec4(qt_shadowDepth);\n";
1800 } else if (isCubeShadowPass) {
1801 fragmentShader.addUniform("qt_cameraPosition", "vec3");
1802 fragmentShader.addUniform("qt_cameraProperties", "vec2");
1803 fragmentShader << " // omnidirectional shadow pass\n"
1804 << " vec3 qt_shadowCamPos = vec3(qt_cameraPosition.x, qt_cameraPosition.y, qt_cameraPosition.z);\n"
1805 << " float qt_shadowDist = length(qt_varShadowWorldPos - qt_shadowCamPos);\n"
1806 << " qt_shadowDist = (qt_shadowDist - qt_cameraProperties.x) / (qt_cameraProperties.y - qt_cameraProperties.x);\n"
1807 << " fragOutput = vec4(qt_shadowDist, qt_shadowDist, qt_shadowDist, 1.0);\n";
1808 } else {
1809 fragmentShader.addInclude("tonemapping.glsllib");
1810 fragmentShader.append(" fragOutput = vec4(qt_tonemap(qt_diffuseColor.rgb), qt_diffuseColor.a * qt_objectOpacity);");
1811 }
1812 }
1813}
1814
1816 QSSGMaterialVertexPipeline &vertexPipeline,
1819 const QSSGShaderFeatures &inFeatureSet,
1820 const QSSGRenderGraphObject &inMaterial,
1821 const QSSGShaderLightListView &inLights,
1822 QSSGRenderableImage *inFirstImage,
1823 QSSGShaderLibraryManager &shaderLibraryManager,
1824 QSSGShaderCache &theCache)
1825{
1826 QByteArray materialInfoString; // also serves as the key for the cache in compileGeneratedRhiShader
1827 // inShaderKeyPrefix can be a static string for default materials, but must
1828 // be unique for different sets of shaders in custom materials.
1829 materialInfoString = inShaderKeyPrefix;
1830 key.toString(materialInfoString, inProperties);
1831
1832 // the call order is: beginVertex, beginFragment, endVertex, endFragment
1833 vertexPipeline.beginVertexGeneration(key, inFeatureSet, shaderLibraryManager);
1834 generateFragmentShader(vertexPipeline.fragment(), vertexPipeline, key, inProperties, inFeatureSet, inMaterial, inLights, inFirstImage, shaderLibraryManager);
1835 vertexPipeline.endVertexGeneration();
1836 vertexPipeline.endFragmentGeneration();
1837
1838 return vertexPipeline.programGenerator()->compileGeneratedRhiShader(materialInfoString, inFeatureSet, shaderLibraryManager, theCache, {});
1839}
1840
1841static float ZERO_MATRIX[16] = {};
1842
1845 char *ubufData,
1846 QSSGRhiGraphicsPipelineState *inPipelineState,
1847 const QSSGRenderGraphObject &inMaterial,
1848 const QSSGShaderDefaultMaterialKey &inKey,
1850 const QSSGRenderCamera &inCamera,
1851 const QMatrix4x4 &inModelViewProjection,
1852 const QMatrix3x3 &inNormalMatrix,
1853 const QMatrix4x4 &inGlobalTransform,
1854 const QMatrix4x4 &clipSpaceCorrMatrix,
1855 const QMatrix4x4 &localInstanceTransform,
1856 const QMatrix4x4 &globalInstanceTransform,
1857 const QSSGDataView<float> &inMorphWeights,
1858 QSSGRenderableImage *inFirstImage,
1859 float inOpacity,
1860 const QSSGLayerGlobalRenderProperties &inRenderProperties,
1861 const QSSGShaderLightListView &inLights,
1862 const QSSGShaderReflectionProbe &reflectionProbe,
1863 bool receivesShadows,
1864 bool receivesReflections,
1865 const QVector2D *shadowDepthAdjust,
1866 QRhiTexture *lightmapTexture)
1867{
1868 QSSGShaderMaterialAdapter *materialAdapter = getMaterialAdapter(inMaterial);
1869 QSSGRhiShaderPipeline::CommonUniformIndices &cui = shaders.commonUniformIndices;
1870
1871 materialAdapter->setCustomPropertyUniforms(ubufData, shaders, renderContext);
1872
1873 const QVector3D camGlobalPos = inCamera.getGlobalPos();
1874 const QVector2D camProperties(inCamera.clipNear, inCamera.clipFar);
1875
1876 shaders.setUniform(ubufData, "qt_cameraPosition", &camGlobalPos, 3 * sizeof(float), &cui.cameraPositionIdx);
1877 shaders.setUniform(ubufData, "qt_cameraDirection", &inRenderProperties.cameraData, 3 * sizeof(float), &cui.cameraDirectionIdx);
1878 shaders.setUniform(ubufData, "qt_cameraProperties", &camProperties, 2 * sizeof(float), &cui.cameraPropertiesIdx);
1879
1880 // Only calculate and update Matrix uniforms if they are needed
1881 bool usesProjectionMatrix = false;
1882 bool usesInvProjectionMatrix = false;
1883 bool usesViewMatrix = false;
1884 bool usesViewProjectionMatrix = false;
1885 bool usesModelViewProjectionMatrix = false;
1886 bool usesNormalMatrix = false;
1887 bool usesParentMatrix = false;
1888
1889 if (inMaterial.type == QSSGRenderGraphObject::Type::CustomMaterial) {
1890 const auto *customMaterial = static_cast<const QSSGRenderCustomMaterial *>(&inMaterial);
1891 usesProjectionMatrix = customMaterial->m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix);
1892 usesInvProjectionMatrix = customMaterial->m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix);
1893 // ### these should use flags like the above two
1894 usesViewMatrix = true;
1895 usesViewProjectionMatrix = true;
1896 }
1897 const bool usesInstancing = inProperties.m_usesInstancing.getValue(inKey);
1898 if (usesInstancing) {
1899 // Instanced calls have to calculate MVP and normalMatrix in the vertex shader
1900 usesViewProjectionMatrix = true;
1901 usesParentMatrix = true;
1902 } else {
1903 usesModelViewProjectionMatrix = true;
1904 usesNormalMatrix = true;
1905 }
1906
1907 if (materialAdapter->isTransmissionEnabled())
1908 usesViewProjectionMatrix = true;
1909
1910 // Update matrix uniforms
1911 if (usesProjectionMatrix || usesInvProjectionMatrix) {
1912 const QMatrix4x4 projection = clipSpaceCorrMatrix * inCamera.projection;
1913 if (usesProjectionMatrix)
1914 shaders.setUniform(ubufData, "qt_projectionMatrix", projection.constData(), 16 * sizeof(float), &cui.projectionMatrixIdx);
1915 if (usesInvProjectionMatrix)
1916 shaders.setUniform(ubufData, "qt_inverseProjectionMatrix", projection.inverted().constData(), 16 * sizeof (float), &cui.inverseProjectionMatrixIdx);
1917 }
1918 if (usesViewMatrix) {
1919 const QMatrix4x4 viewMatrix = inCamera.globalTransform.inverted();
1920 shaders.setUniform(ubufData, "qt_viewMatrix", viewMatrix.constData(), 16 * sizeof(float), &cui.viewMatrixIdx);
1921 }
1922 if (usesViewProjectionMatrix) {
1923 QMatrix4x4 viewProj(Qt::Uninitialized);
1924 inCamera.calculateViewProjectionMatrix(viewProj);
1925 viewProj = clipSpaceCorrMatrix * viewProj;
1926 shaders.setUniform(ubufData, "qt_viewProjectionMatrix", viewProj.constData(), 16 * sizeof(float), &cui.viewProjectionMatrixIdx);
1927 }
1928
1929 // qt_modelMatrix is always available, but differnt when using instancing
1930 if (usesInstancing)
1931 shaders.setUniform(ubufData, "qt_modelMatrix", localInstanceTransform.constData(), 16 * sizeof(float), &cui.modelMatrixIdx);
1932 else
1933 shaders.setUniform(ubufData, "qt_modelMatrix", inGlobalTransform.constData(), 16 * sizeof(float), &cui.modelMatrixIdx);
1934
1935 if (usesModelViewProjectionMatrix) {
1936 QMatrix4x4 mvp{ clipSpaceCorrMatrix };
1937 mvp *= inModelViewProjection;
1938 shaders.setUniform(ubufData, "qt_modelViewProjection", mvp.constData(), 16 * sizeof(float), &cui.modelViewProjectionIdx);
1939 }
1940 if (usesNormalMatrix)
1941 shaders.setUniform(ubufData, "qt_normalMatrix", inNormalMatrix.constData(), 12 * sizeof(float), &cui.normalMatrixIdx,
1942 QSSGRhiShaderPipeline::UniformFlag::Mat3); // real size will be 12 floats, setUniform repacks as needed
1943 if (usesParentMatrix)
1944 shaders.setUniform(ubufData, "qt_parentMatrix", globalInstanceTransform.constData(), 16 * sizeof(float));
1945
1946 // Morphing
1947 const qsizetype morphSize = inProperties.m_targetCount.getValue(inKey);
1948 if (morphSize > 0) {
1949 if (inMorphWeights.mSize >= morphSize) {
1950 shaders.setUniformArray(ubufData, "qt_morphWeights", inMorphWeights.mData, morphSize,
1952 } else {
1953 const QList<float> zeroWeights(morphSize - inMorphWeights.mSize, 0.0f);
1954 QList<float> newWeights(inMorphWeights.mData, inMorphWeights.mData + inMorphWeights.mSize);
1955 newWeights.append(zeroWeights);
1956 shaders.setUniformArray(ubufData, "qt_morphWeights", newWeights.constData(), morphSize,
1958 }
1959 }
1960
1961 QVector3D theLightAmbientTotal;
1962 shaders.resetShadowMaps();
1963 float lightColor[QSSG_MAX_NUM_LIGHTS][3];
1964 QSSGShaderLightsUniformData &lightsUniformData(shaders.lightsUniformData());
1965 lightsUniformData.count = 0;
1966
1967 for (quint32 lightIdx = 0, shadowMapCount = 0, lightEnd = inLights.size();
1968 lightIdx < lightEnd && lightIdx < QSSG_MAX_NUM_LIGHTS; ++lightIdx)
1969 {
1970 QSSGRenderLight *theLight(inLights[lightIdx].light);
1971 const bool lightShadows = inLights[lightIdx].shadows;
1972 const float brightness = theLight->m_brightness;
1973 lightColor[lightIdx][0] = theLight->m_diffuseColor.x() * brightness;
1974 lightColor[lightIdx][1] = theLight->m_diffuseColor.y() * brightness;
1975 lightColor[lightIdx][2] = theLight->m_diffuseColor.z() * brightness;
1976 lightsUniformData.count += 1;
1977 QSSGShaderLightData &lightData(lightsUniformData.lightData[lightIdx]);
1978 const QVector3D &lightSpecular(theLight->m_specularColor);
1979 lightData.specular[0] = lightSpecular.x() * brightness;
1980 lightData.specular[1] = lightSpecular.y() * brightness;
1981 lightData.specular[2] = lightSpecular.z() * brightness;
1982 lightData.specular[3] = 1.0f;
1983 const QVector3D &lightDirection(inLights[lightIdx].direction);
1984 lightData.direction[0] = lightDirection.x();
1985 lightData.direction[1] = lightDirection.y();
1986 lightData.direction[2] = lightDirection.z();
1987 lightData.direction[3] = 1.0f;
1988
1989 // When it comes to receivesShadows, it is a bit tricky: to stay
1990 // compatible with the old, direct OpenGL rendering path (and the
1991 // generated shader code), we will need to ensure the texture
1992 // (shadowmap0, shadowmap1, ...) and sampler bindings are present.
1993 // So receivesShadows must not be included in the following
1994 // condition. Instead, it is the other shadow-related uniforms that
1995 // get an all-zero value, which then ensures no shadow contribution
1996 // for the object in question.
1997
1998 if (lightShadows && shadowMapCount < QSSG_MAX_NUM_SHADOW_MAPS) {
1999 QSSGRhiShadowMapProperties &theShadowMapProperties(shaders.addShadowMap());
2000 ++shadowMapCount;
2001
2002 QSSGShadowMapEntry *pEntry = inRenderProperties.shadowMapManager->shadowMapEntry(lightIdx);
2003 Q_ASSERT(pEntry);
2004
2005 const auto names = setupShadowMapVariableNames(lightIdx);
2006
2007 if (theLight->type != QSSGRenderLight::Type::DirectionalLight) {
2008 theShadowMapProperties.shadowMapTexture = pEntry->m_rhiDepthCube;
2009 theShadowMapProperties.shadowMapTextureUniformName = names.shadowCubeStem;
2010 if (receivesShadows)
2011 shaders.setUniform(ubufData, names.shadowMatrixStem, pEntry->m_lightView.constData(), 16 * sizeof(float));
2012 else
2013 shaders.setUniform(ubufData, names.shadowMatrixStem, ZERO_MATRIX, 16 * sizeof(float));
2014 } else {
2015 theShadowMapProperties.shadowMapTexture = pEntry->m_rhiDepthMap;
2016 theShadowMapProperties.shadowMapTextureUniformName = names.shadowMapStem;
2017 if (receivesShadows) {
2018 // add fixed scale bias matrix
2019 const QMatrix4x4 bias = {
2020 0.5, 0.0, 0.0, 0.5,
2021 0.0, 0.5, 0.0, 0.5,
2022 0.0, 0.0, 0.5, 0.5,
2023 0.0, 0.0, 0.0, 1.0 };
2024 const QMatrix4x4 m = bias * pEntry->m_lightVP;
2025 shaders.setUniform(ubufData, names.shadowMatrixStem, m.constData(), 16 * sizeof(float));
2026 } else {
2027 shaders.setUniform(ubufData, names.shadowMatrixStem, ZERO_MATRIX, 16 * sizeof(float));
2028 }
2029 }
2030
2031 if (receivesShadows) {
2032 const QVector4D shadowControl(theLight->m_shadowBias,
2033 theLight->m_shadowFactor,
2034 theLight->m_shadowMapFar,
2035 inRenderProperties.isYUpInFramebuffer ? 0.0f : 1.0f);
2036 shaders.setUniform(ubufData, names.shadowControlStem, &shadowControl, 4 * sizeof(float));
2037 } else {
2038 shaders.setUniform(ubufData, names.shadowControlStem, ZERO_MATRIX, 4 * sizeof(float));
2039 }
2040 }
2041
2042 if (theLight->type == QSSGRenderLight::Type::PointLight
2043 || theLight->type == QSSGRenderLight::Type::SpotLight) {
2044 const QVector3D globalPos = theLight->getGlobalPos();
2045 lightData.position[0] = globalPos.x();
2046 lightData.position[1] = globalPos.y();
2047 lightData.position[2] = globalPos.z();
2048 lightData.position[3] = 1.0f;
2052 lightData.coneAngle = 180.0f;
2053 if (theLight->type == QSSGRenderLight::Type::SpotLight) {
2054 const float coneAngle = theLight->m_coneAngle;
2055 const float innerConeAngle = (theLight->m_innerConeAngle > coneAngle) ?
2056 coneAngle : theLight->m_innerConeAngle;
2057 lightData.coneAngle = qCos(qDegreesToRadians(coneAngle));
2058 lightData.innerConeAngle = qCos(qDegreesToRadians(innerConeAngle));
2059 }
2060 }
2061
2062 theLightAmbientTotal += theLight->m_ambientColor;
2063 }
2064
2065 shaders.setDepthTexture(inRenderProperties.rhiDepthTexture);
2066 shaders.setSsaoTexture(inRenderProperties.rhiSsaoTexture);
2067 shaders.setScreenTexture(inRenderProperties.rhiScreenTexture);
2068 shaders.setLightmapTexture(lightmapTexture);
2069
2070 QSSGRenderImage *theLightProbe = inRenderProperties.lightProbe;
2071
2072 // If the material has its own IBL Override, we should use that image instead.
2073 QSSGRenderImage *materialIblProbe = materialAdapter->iblProbe();
2074 if (materialIblProbe)
2075 theLightProbe = materialIblProbe;
2076 QSSGRenderImageTexture lightProbeTexture;
2077 if (theLightProbe)
2078 lightProbeTexture = renderContext.bufferManager()->loadRenderImage(theLightProbe, QSSGBufferManager::MipModeBsdf);
2079 if (theLightProbe && lightProbeTexture.m_texture) {
2080 QSSGRenderTextureCoordOp theHorzLightProbeTilingMode = theLightProbe->m_horizontalTilingMode;
2081 QSSGRenderTextureCoordOp theVertLightProbeTilingMode = theLightProbe->m_verticalTilingMode;
2082 const int maxMipLevel = lightProbeTexture.m_mipmapCount - 1;
2083
2084 if (!materialIblProbe && !inRenderProperties.probeOrientation.isIdentity()) {
2085 shaders.setUniform(ubufData, "qt_lightProbeOrientation",
2086 inRenderProperties.probeOrientation.constData(),
2087 12 * sizeof(float), &cui.lightProbeOrientationIdx,
2089 }
2090
2091 const float props[4] = { 0.0f, float(maxMipLevel), inRenderProperties.probeHorizon, inRenderProperties.probeExposure };
2092 shaders.setUniform(ubufData, "qt_lightProbeProperties", props, 4 * sizeof(float), &cui.lightProbePropertiesIdx);
2093
2094 shaders.setLightProbeTexture(lightProbeTexture.m_texture, theHorzLightProbeTilingMode, theVertLightProbeTilingMode);
2095 } else {
2096 // no lightprobe
2097 const float emptyProps[4] = { 0.0f, 0.0f, -1.0f, 0.0f };
2098 shaders.setUniform(ubufData, "qt_lightProbeProperties", emptyProps, 4 * sizeof(float), &cui.lightProbePropertiesIdx);
2099
2100 shaders.setLightProbeTexture(nullptr);
2101 }
2102
2103 if (receivesReflections && reflectionProbe.enabled) {
2104 shaders.setUniform(ubufData, "qt_reflectionProbeCubeMapCenter", &reflectionProbe.probeCubeMapCenter, 3 * sizeof(float), &cui.reflectionProbeCubeMapCenter);
2105 shaders.setUniform(ubufData, "qt_reflectionProbeBoxMin", &reflectionProbe.probeBoxMin, 3 * sizeof(float), &cui.reflectionProbeBoxMin);
2106 shaders.setUniform(ubufData, "qt_reflectionProbeBoxMax", &reflectionProbe.probeBoxMax, 3 * sizeof(float), &cui.reflectionProbeBoxMax);
2107 shaders.setUniform(ubufData, "qt_reflectionProbeCorrection", &reflectionProbe.parallaxCorrection, sizeof(int), &cui.reflectionProbeCorrection);
2108 }
2109
2110 const QVector3D emissiveColor = materialAdapter->emissiveColor();
2111 shaders.setUniform(ubufData, "qt_material_emissive_color", &emissiveColor, 3 * sizeof(float), &cui.material_emissiveColorIdx);
2112
2113 const auto qMix = [](float x, float y, float a) {
2114 return (x * (1.0f - a) + (y * a));
2115 };
2116
2117 const auto qMix3 = [&qMix](const QVector3D &x, const QVector3D &y, float a) {
2118 return QVector3D{qMix(x.x(), y.x(), a), qMix(x.y(), y.y(), a), qMix(x.z(), y.z(), a)};
2119 };
2120
2121 const QVector4D color = materialAdapter->color();
2122 const QVector3D materialSpecularTint = materialAdapter->specularTint();
2123 const QVector3D specularTint = materialAdapter->isPrincipled() ? qMix3(QVector3D(1.0f, 1.0f, 1.0f), color.toVector3D(), materialSpecularTint.x())
2124 : materialSpecularTint;
2125 shaders.setUniform(ubufData, "qt_material_base_color", &color, 4 * sizeof(float), &cui.material_baseColorIdx);
2126
2127 const float ior = materialAdapter->ior();
2128 QVector4D specularColor(specularTint, ior);
2129 shaders.setUniform(ubufData, "qt_material_specular", &specularColor, 4 * sizeof(float), &cui.material_specularIdx);
2130
2131 // metalnessAmount cannot be multiplied in here yet due to custom materials
2132 const bool hasLighting = materialAdapter->hasLighting();
2133 shaders.setLightsEnabled(hasLighting);
2134 if (hasLighting) {
2135 for (int lightIdx = 0; lightIdx < lightsUniformData.count; ++lightIdx) {
2136 QSSGShaderLightData &lightData(lightsUniformData.lightData[lightIdx]);
2137 lightData.diffuse[0] = lightColor[lightIdx][0];
2138 lightData.diffuse[1] = lightColor[lightIdx][1];
2139 lightData.diffuse[2] = lightColor[lightIdx][2];
2140 lightData.diffuse[3] = 1.0f;
2141 }
2142 memcpy(ubufData + shaders.ub0LightDataOffset(), &lightsUniformData, shaders.ub0LightDataSize());
2143 }
2144
2145 shaders.setUniform(ubufData, "qt_light_ambient_total", &theLightAmbientTotal, 3 * sizeof(float), &cui.light_ambient_totalIdx);
2146
2147 const float materialProperties[4] = {
2148 materialAdapter->specularAmount(),
2149 materialAdapter->specularRoughness(),
2150 materialAdapter->metalnessAmount(),
2151 inOpacity
2152 };
2153 shaders.setUniform(ubufData, "qt_material_properties", materialProperties, 4 * sizeof(float), &cui.material_propertiesIdx);
2154
2155 const float materialProperties2[4] = {
2156 materialAdapter->fresnelPower(),
2157 materialAdapter->bumpAmount(),
2158 materialAdapter->translucentFallOff(),
2159 materialAdapter->diffuseLightWrap()
2160 };
2161 shaders.setUniform(ubufData, "qt_material_properties2", materialProperties2, 4 * sizeof(float), &cui.material_properties2Idx);
2162
2163 const float materialProperties3[4] = {
2164 materialAdapter->occlusionAmount(),
2165 materialAdapter->alphaCutOff(),
2166 materialAdapter->clearcoatAmount(),
2167 materialAdapter->clearcoatRoughnessAmount()
2168 };
2169 shaders.setUniform(ubufData, "qt_material_properties3", materialProperties3, 4 * sizeof(float), &cui.material_properties3Idx);
2170
2171 const float materialProperties4[4] = {
2172 materialAdapter->heightAmount(),
2173 materialAdapter->minHeightSamples(),
2174 materialAdapter->maxHeightSamples(),
2175 materialAdapter->transmissionFactor()
2176 };
2177 shaders.setUniform(ubufData, "qt_material_properties4", materialProperties4, 4 * sizeof(float), &cui.material_properties4Idx);
2178
2179 // We only ever use attenuation and thickness uniforms when using transmission
2180 if (materialAdapter->isTransmissionEnabled()) {
2181 const QVector4D attenuationProperties(materialAdapter->attenuationColor(), materialAdapter->attenuationDistance());
2182 shaders.setUniform(ubufData, "qt_material_attenuation", &attenuationProperties, 4 * sizeof(float), &cui.material_attenuationIdx);
2183
2184 const float thickness = materialAdapter->thicknessFactor();
2185 shaders.setUniform(ubufData, "qt_material_thickness", &thickness, sizeof(float), &cui.thicknessFactorIdx);
2186 }
2187
2188 const float rhiProperties[4] = {
2189 inRenderProperties.isYUpInFramebuffer ? 1.0f : -1.0f,
2190 inRenderProperties.isYUpInNDC ? 1.0f : -1.0f,
2191 inRenderProperties.isClipDepthZeroToOne ? 0.0f : -1.0f,
2192 0.0f // unused
2193 };
2194 shaders.setUniform(ubufData, "qt_rhi_properties", rhiProperties, 4 * sizeof(float), &cui.rhiPropertiesIdx);
2195
2196 qsizetype imageIdx = 0;
2197 for (QSSGRenderableImage *theImage = inFirstImage; theImage; theImage = theImage->m_nextImage, ++imageIdx) {
2198 // we need to map image to uniform name: "image0_rotations", "image0_offsets", etc...
2199 const auto &names = imageStringTable[int(theImage->m_mapType)];
2200 if (imageIdx == cui.imageIndices.size())
2202 auto &indices = cui.imageIndices[imageIdx];
2203
2204 const QMatrix4x4 &textureTransform = theImage->m_imageNode.m_textureTransform;
2205 // We separate rotational information from offset information so that just maybe the shader
2206 // will attempt to push less information to the card.
2207 const float *dataPtr(textureTransform.constData());
2208 // The third member of the offsets contains a flag indicating if the texture was
2209 // premultiplied or not.
2210 // We use this to mix the texture alpha.
2211 const float offsets[3] = { dataPtr[12], dataPtr[13], 0.0f /* non-premultiplied */ };
2212 shaders.setUniform(ubufData, names.imageOffsets, offsets, sizeof(offsets), &indices.imageOffsetsUniformIndex);
2213 // Grab just the upper 2x2 rotation matrix from the larger matrix.
2214 const float rotations[4] = { dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5] };
2215 shaders.setUniform(ubufData, names.imageRotations, rotations, sizeof(rotations), &indices.imageRotationsUniformIndex);
2216 }
2217
2218 if (shadowDepthAdjust)
2219 shaders.setUniform(ubufData, "qt_shadowDepthAdjust", shadowDepthAdjust, 2 * sizeof(float), &cui.shadowDepthAdjustIdx);
2220
2221 const bool usesPointsTopology = inProperties.m_usesPointsTopology.getValue(inKey);
2222 if (usesPointsTopology) {
2223 const float pointSize = materialAdapter->pointSize();
2224 shaders.setUniform(ubufData, "qt_materialPointSize", &pointSize, sizeof(float), &cui.pointSizeIdx);
2225 }
2226
2227 // qt_fogColor = (fogColor.x, fogColor.y, fogColor.z, fogDensity)
2228 // qt_fogDepthProperties = (fogDepthBegin, fogDepthEnd, fogDepthCurve, fogDepthEnabled ? 1.0 : 0.0)
2229 // qt_fogHeightProperties = (fogHeightMin, fogHeightMax, fogHeightCurve, fogHeightEnabled ? 1.0 : 0.0)
2230 // qt_fogTransmitProperties = (fogTransmitCurve, 0.0, 0.0, fogTransmitEnabled ? 1.0 : 0.0)
2231 if (inRenderProperties.layer.fog.enabled) {
2232 const float fogColor[4] = {
2233 inRenderProperties.layer.fog.color.x(),
2234 inRenderProperties.layer.fog.color.y(),
2235 inRenderProperties.layer.fog.color.z(),
2236 inRenderProperties.layer.fog.density
2237 };
2238 shaders.setUniform(ubufData, "qt_fogColor", fogColor, 4 * sizeof(float), &cui.fogColorIdx);
2239 const float fogDepthProperties[4] = {
2240 inRenderProperties.layer.fog.depthBegin,
2241 inRenderProperties.layer.fog.depthEnd,
2242 inRenderProperties.layer.fog.depthCurve,
2243 inRenderProperties.layer.fog.depthEnabled ? 1.0f : 0.0f
2244 };
2245 shaders.setUniform(ubufData, "qt_fogDepthProperties", fogDepthProperties, 4 * sizeof(float), &cui.fogDepthPropertiesIdx);
2246 const float fogHeightProperties[4] = {
2247 inRenderProperties.layer.fog.heightMin,
2248 inRenderProperties.layer.fog.heightMax,
2249 inRenderProperties.layer.fog.heightCurve,
2250 inRenderProperties.layer.fog.heightEnabled ? 1.0f : 0.0f
2251 };
2252 shaders.setUniform(ubufData, "qt_fogHeightProperties", fogHeightProperties, 4 * sizeof(float), &cui.fogHeightPropertiesIdx);
2253 const float fogTransmitProperties[4] = {
2254 inRenderProperties.layer.fog.transmitCurve,
2255 0.0f,
2256 0.0f,
2257 inRenderProperties.layer.fog.transmitEnabled ? 1.0f : 0.0f
2258 };
2259 shaders.setUniform(ubufData, "qt_fogTransmitProperties", fogTransmitProperties, 4 * sizeof(float), &cui.fogTransmitPropertiesIdx);
2260 }
2261
2262 inPipelineState->lineWidth = materialAdapter->lineWidth();
2263}
2264
2266
2268{
2269 return {std::begin(qssg_shader_arg_names), std::end(qssg_shader_arg_names) };;
2270}
\inmodule QtCore
Definition qbytearray.h:57
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool isIdentity() const
Returns true if this matrix is the identity; false otherwise.
const T * constData() const
Returns a constant pointer to the raw data of this matrix.
Definition qlist.h:74
const_pointer constData() const noexcept
Definition qlist.h:416
void push_back(parameter_type t)
Definition qlist.h:672
void append(parameter_type t)
Definition qlist.h:441
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
QMatrix4x4 inverted(bool *invertible=nullptr) const
Returns the inverse of this matrix.
const float * constData() const
Returns a constant pointer to the raw data of this matrix.
Definition qmatrix4x4.h:147
\inmodule QtGui
Definition qrhi.h:883
QSSGRhiShaderPipelinePtr compileGeneratedRhiShader(const QByteArray &inMaterialInfoString, const QSSGShaderFeatures &inFeatureSet, QSSGShaderLibraryManager &shaderLibraryManager, QSSGShaderCache &theCache, QSSGRhiShaderPipeline::StageFlags stageFlags)
const std::unique_ptr< QSSGBufferManager > & bufferManager() const
QSSGShadowMapEntry * shadowMapEntry(int lightIdx)
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:672
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
direction
Combined button and popup list for selecting options.
void textureCoordVariableName(char(&outString)[TEXCOORD_VAR_LEN], quint8 uvSet)
void textureCoordVaryingName(char(&outString)[TEXCOORD_VAR_LEN], quint8 uvSet)
Q_QUICK3DRUNTIMERENDER_EXPORT QList< QByteArrayView > reservedArgumentNames()
constexpr Initialization Uninitialized
Q_DECL_CONSTEXPR float translateConstantAttenuation(float attenuation)
Definition qssgutils_p.h:40
Q_DECL_CONSTEXPR float translateQuadraticAttenuation(float attenuation)
Definition qssgutils_p.h:44
Q_DECL_CONSTEXPR float translateLinearAttenuation(float attenuation)
Definition qssgutils_p.h:42
Definition image.cpp:4
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
Q_CORE_EXPORT char * qstrncpy(char *dst, const char *src, size_t len)
Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt,...)
#define Q_UNLIKELY(x)
#define qWarning
Definition qlogging.h:162
return ret
auto qCos(T v)
Definition qmath.h:60
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint id
[7]
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLenum GLuint GLsizei const GLenum * props
GLenum GLuint GLintptr offset
GLsizei GLenum const void * indices
GLint y
GLuint GLenum GLenum transform
GLuint GLuint * names
GLint void * img
Definition qopenglext.h:233
GLfloat bias
GLuint GLsizei const GLuint const GLintptr * offsets
GLsizei GLsizei GLuint * shaders
Definition qopenglext.h:677
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QSSGRenderTextureCoordOp
static void calculatePointLightAttenuation(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames)
static void handleDirectionalLight(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames, bool usesSharedVar, bool hasCustomFrag, QSSGShaderMaterialAdapter *materialAdapter, QSSGShaderLibraryManager &shaderLibraryManager, bool specularLightingEnabled, bool enableClearcoat, bool enableTransmission)
static void sanityCheckImageForSampler(const QSSGRenderableImage &image, const char *samplerName)
static void handleSpecularLight(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames, QSSGShaderMaterialAdapter *materialAdapter, QSSGShaderLibraryManager &shaderLibraryManager, bool usesSharedVar, bool hasCustomFrag, bool specularLightingEnabled, bool enableClearcoat, bool enableTransmission, bool useNormalizedDirection)
static void generateTempLightColor(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames, QSSGShaderMaterialAdapter *materialAdapter)
static QByteArray uvTransform(const QByteArray &imageRotations, const QByteArray &imageOffsets)
static void generateImageUVSampler(QSSGMaterialVertexPipeline &vertexGenerator, QSSGStageGeneratorBase &fragmentShader, const QSSGShaderDefaultMaterialKey &key, const QSSGRenderableImage &image, char(&outString)[TEXCOORD_VAR_LEN], quint8 uvSet=0)
static void generateMainLightCalculation(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialVertexPipeline &vertexShader, const QSSGShaderDefaultMaterialKey &inKey, const QSSGRenderGraphObject &inMaterial, const QSSGShaderLightListView &lights, QSSGShaderLibraryManager &shaderLibraryManager, QSSGRenderableImage *translucencyImage, bool hasCustomFrag, bool usesSharedVar, bool enableLightmap, bool enableShadowMaps, bool specularLightingEnabled, bool enableClearcoat, bool enableTransmission)
static void generateImageUVCoordinates(QSSGMaterialVertexPipeline &vertexShader, QSSGStageGeneratorBase &fragmentShader, const QSSGShaderDefaultMaterialKey &key, QSSGRenderableImage &image, bool forceFragmentShader=false, quint32 uvSet=0, bool reuseImageCoords=false)
static void addLocalVariable(QSSGStageGeneratorBase &inGenerator, const QByteArray &inName, const QByteArray &inType)
static bool hasCustomFunction(const QByteArray &funcName, QSSGShaderMaterialAdapter *materialAdapter, QSSGShaderLibraryManager &shaderLibraryManager)
static void addTranslucencyIrradiance(QSSGStageGeneratorBase &infragmentShader, QSSGRenderableImage *image, const QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames)
static QVarLengthArray< QSSGMaterialShaderGenerator::ShadowVariableNames, 16 > q3ds_shadowMapVariableNames
static QSSGMaterialShaderGenerator::ShadowVariableNames setupShadowMapVariableNames(qsizetype lightIdx)
static void generateShadowMapOcclusion(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialVertexPipeline &vertexShader, quint32 lightIdx, bool inShadowEnabled, QSSGRenderLight::Type inType, const QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames, const QSSGShaderDefaultMaterialKey &inKey)
static void handleSpotLight(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames, const QByteArray &lightVarPrefix, QSSGShaderMaterialAdapter *materialAdapter, QSSGShaderLibraryManager &shaderLibraryManager, bool usesSharedVar, bool hasCustomFrag, bool specularLightingEnabled, bool enableClearcoat, bool enableTransmission)
static void handlePointLight(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames, QSSGShaderMaterialAdapter *materialAdapter, QSSGShaderLibraryManager &shaderLibraryManager, bool usesSharedVar, bool hasCustomFrag, bool specularLightingEnabled, bool enableClearcoat, bool enableTransmission)
static constexpr QByteArrayView qssg_shader_arg_names[]
static void outputSpecularEquation(QSSGRenderDefaultMaterial::MaterialSpecularModel inSpecularModel, QSSGStageGeneratorBase &fragmentShader, const QByteArray &inLightDir, const QByteArray &inLightSpecColor)
static void generateDirections(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames, const QByteArray &lightVarPrefix, QSSGMaterialVertexPipeline &vertexShader, const QSSGShaderDefaultMaterialKey &inKey)
static void generateFragmentShader(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialVertexPipeline &vertexShader, const QSSGShaderDefaultMaterialKey &inKey, const QSSGShaderDefaultMaterialKeyProperties &keyProps, const QSSGShaderFeatures &featureSet, const QSSGRenderGraphObject &inMaterial, const QSSGShaderLightListView &lights, QSSGRenderableImage *firstImage, QSSGShaderLibraryManager &shaderLibraryManager)
static void maybeAddMaterialFresnel(QSSGStageGeneratorBase &fragmentShader, const QSSGShaderDefaultMaterialKeyProperties &keyProps, QSSGDataView< quint32 > inKey, bool hasMetalness)
static QSSGShaderMaterialAdapter * getMaterialAdapter(const QSSGRenderGraphObject &inMaterial)
static QSSGMaterialShaderGenerator::LightVariableNames setupLightVariableNames(qint32 lightIdx, QSSGRenderLight &inLight)
std::shared_ptr< QSSGRhiShaderPipeline > QSSGRhiShaderPipelinePtr
#define QSSG_MAX_NUM_LIGHTS
#define QSSG_MAX_NUM_SHADOW_MAPS
unsigned int quint32
Definition qtypes.h:45
int qint32
Definition qtypes.h:44
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned char quint8
Definition qtypes.h:41
bool contains(const AT &t) const noexcept
Definition qlist.h:44
qsizetype size() const
const T * mData
qsizetype mSize
bool isEmpty() const
static QSSGRhiShaderPipelinePtr generateMaterialRhiShader(const QByteArray &inShaderKeyPrefix, QSSGMaterialVertexPipeline &vertexGenerator, const QSSGShaderDefaultMaterialKey &key, QSSGShaderDefaultMaterialKeyProperties &inProperties, const QSSGShaderFeatures &inFeatureSet, const QSSGRenderGraphObject &inMaterial, const QSSGShaderLightListView &inLights, QSSGRenderableImage *inFirstImage, QSSGShaderLibraryManager &shaderLibraryManager, QSSGShaderCache &theCache)
static const char * getSamplerName(QSSGRenderableImage::Type type)
static void setRhiMaterialProperties(const QSSGRenderContextInterface &, QSSGRhiShaderPipeline &shaders, char *ubufData, QSSGRhiGraphicsPipelineState *inPipelineState, const QSSGRenderGraphObject &inMaterial, const QSSGShaderDefaultMaterialKey &inKey, QSSGShaderDefaultMaterialKeyProperties &inProperties, const QSSGRenderCamera &inCamera, const QMatrix4x4 &inModelViewProjection, const QMatrix3x3 &inNormalMatrix, const QMatrix4x4 &inGlobalTransform, const QMatrix4x4 &clipSpaceCorrMatrix, const QMatrix4x4 &localInstanceTransform, const QMatrix4x4 &globalInstanceTransform, const QSSGDataView< float > &inMorphWeights, QSSGRenderableImage *inFirstImage, float inOpacity, const QSSGLayerGlobalRenderProperties &inRenderProperties, const QSSGShaderLightListView &inLights, const QSSGShaderReflectionProbe &reflectionProbe, bool receivesShadows, bool receivesReflections, const QVector2D *shadowDepthAdjust, QRhiTexture *lightmapTexture)
void generateShadowWorldPosition(const QSSGShaderDefaultMaterialKey &inKey)
void beginVertexGeneration(const QSSGShaderDefaultMaterialKey &inKey, const QSSGShaderFeatures &inFeatureSet, QSSGShaderLibraryManager &shaderLibraryManager)
void addDefinition(const QByteArray &name, const QByteArray &value=QByteArray())
void addUniform(const QByteArray &name, const QByteArray &type)
void generateEnvMapReflection(const QSSGShaderDefaultMaterialKey &inKey)
QSSGStageGeneratorBase & fragment()
void beginFragmentGeneration(QSSGShaderLibraryManager &shaderLibraryManager)
void generateVertexColor(const QSSGShaderDefaultMaterialKey &inKey)
void generateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &inKey, bool &genTangent, bool &genBinormal)
void assignOutput(const QByteArray &inVarName, const QByteArray &inVarValueExpr)
QSSGProgramGenerator * programGenerator() const
void generateViewVector(const QSSGShaderDefaultMaterialKey &inKey)
void addOutgoing(const QByteArray &name, const QByteArray &type)
void addFunction(const QByteArray &functionName)
void generateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey)
void generateWorldPosition(const QSSGShaderDefaultMaterialKey &inKey)
void generateUVCoords(quint32 inUVSet, const QSSGShaderDefaultMaterialKey &inKey)
Generates UV coordinates in shader code.
void generateLightmapUVCoords(const QSSGShaderDefaultMaterialKey &inKey)
void calculateViewProjectionMatrix(QMatrix4x4 &outMatrix) const
QSSGRenderTextureCoordOp m_horizontalTilingMode
QSSGRenderTextureCoordOp m_verticalTilingMode
struct QSSGRenderLayer::FogOptions fog
QVector3D m_specularColor
QMatrix4x4 globalTransform
QVector3D getGlobalPos() const
QSSGRenderableImage * m_nextImage
const QSSGRenderImage & m_imageNode
QVarLengthArray< ImageIndices, 16 > imageIndices
QSSGShaderKeyUnsigned< 8 > m_targetTexCoord1Offset
QSSGShaderKeyTextureChannel m_textureChannels[SingleChannelImageCount]
QSSGShaderKeyUnsigned< 8 > m_targetPositionOffset
QSSGShaderKeyUnsigned< 8 > m_targetTexCoord0Offset
QSSGShaderKeyUnsigned< 8 > m_targetBinormalOffset
constexpr bool isSet(Feature feature) const
bool getValue(QSSGDataView< quint32 > inDataStore) const
TexturChannelBits getTextureChannel(QSSGDataView< quint32 > inKeySet) const
quint32 getValue(QSSGDataView< quint32 > inDataStore) const
QSSGShaderLightData lightData[QSSG_MAX_NUM_LIGHTS]
virtual float bumpAmount()=0
virtual float ior()=0
virtual float clearcoatRoughnessAmount()=0
virtual float heightAmount()=0
virtual QVector3D specularTint()=0
virtual float maxHeightSamples()=0
virtual bool isVertexColorsEnabled()=0
virtual float occlusionAmount()=0
virtual QSSGRenderImage * iblProbe()=0
virtual float specularAmount()=0
virtual float attenuationDistance()=0
virtual QVector3D attenuationColor()=0
virtual float clearcoatAmount()=0
virtual float transmissionFactor()=0
virtual bool isPrincipled()=0
virtual bool isSpecularEnabled()=0
virtual bool hasCustomShaderFunction(QSSGShaderCache::ShaderType shaderType, const QByteArray &funcName, QSSGShaderLibraryManager &shaderLibraryManager)
virtual QVector3D emissiveColor()=0
virtual bool hasCustomShaderSnippet(QSSGShaderCache::ShaderType type)
virtual float specularRoughness()=0
virtual bool isTransmissionEnabled()=0
virtual float pointSize()=0
virtual float fresnelPower()=0
virtual float metalnessAmount()=0
virtual float lineWidth()=0
virtual QVector4D color()=0
virtual float alphaCutOff()=0
virtual float translucentFallOff()=0
virtual float diffuseLightWrap()=0
virtual float minHeightSamples()=0
virtual float thicknessFactor()=0
virtual bool hasLighting()=0
virtual bool isSpecularGlossy()=0
virtual QSSGRenderDefaultMaterial::MaterialSpecularModel specularModel()=0
virtual bool isClearcoatEnabled()=0
virtual QSSGRenderDefaultMaterial::MaterialAlphaMode alphaMode()=0
virtual void setCustomPropertyUniforms(char *ubufData, QSSGRhiShaderPipeline &shaderPipeline, const QSSGRenderContextInterface &context)
virtual bool isMetalnessEnabled()=0
QMatrix4x4 m_lightVP
light view projection matrix
QMatrix4x4 m_lightView
light view transform
virtual void addFunction(const QByteArray &functionName) final
virtual void addUniform(const QByteArray &name, const QByteArray &type)
virtual void append(const QByteArray &data)
virtual void addInclude(const QByteArray &name) final
Definition moc.h:24