Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
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// Qt-Security score:significant reason:default
5
6
7/* clang-format off */
8
9#include <QtQuick3DUtils/private/qssgutils_p.h>
10#include <QtQuick3DUtils/private/qssgassert_p.h>
11
12#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrendershadercodegenerator_p.h>
15#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
16#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrendershadowmap_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgrendercustommaterial_p.h>
20#include <QtQuick3DRuntimeRender/private/qssgrendershaderlibrarymanager_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgrendershaderkeys_p.h>
22#include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h>
23#include <QtQuick3DRuntimeRender/private/qssgvertexpipelineimpl_p.h>
24#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
25#include <QtQuick3DRuntimeRender/private/qssgrenderpass_p.h>
26#include <QtQuick3DRuntimeRender/private/qssgrenderhelpers_p.h>
27#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
28#include <QtQuick3DRuntimeRender/private/qssgshaderresourcemergecontext_p.h>
29
30#include <QtCore/QByteArray>
31
32#include <cstdio>
33
34QT_BEGIN_NAMESPACE
35
36namespace {
37using Type = QSSGRenderableImage::Type;
38template<Type> struct ImageStrings {};
39#define DefineImageStrings(V) template<> struct ImageStrings<Type::V> \
40{
41 static constexpr const char* sampler() { return "qt_"#V"Map_sampler"; }
42 static constexpr const char* offsets() { return "qt_"#V"Map_offsets"; }
43 static constexpr const char* rotations() { return "qt_"#V"Map_rotations"; }
44 static constexpr const char* fragCoords1() { return "qt_"#V"Map_uv_coords1"; }
45 static constexpr const char* fragCoords2() { return "qt_"#V"Map_uv_coords2"; }
46 static constexpr const char* samplerSize() { return "qt_"#V"Map_size"; }\
47}
48
55DefineImageStrings(SpecularAmountMap);
57DefineImageStrings(Translucency);
64DefineImageStrings(ClearcoatRoughness);
65DefineImageStrings(ClearcoatNormal);
66DefineImageStrings(Transmission);
69DefineImageStrings(SheenRoughness);
71DefineImageStrings(Iridescence);
72DefineImageStrings(IridescenceThickness);
73
75{
76 const char *imageSampler;
77 const char *imageFragCoords;
79 const char *imageOffsets;
80 const char *imageRotations;
81};
82
83#define DefineImageStringTableEntry(V)
84 { ImageStrings<Type::V>::sampler(), ImageStrings<Type::V>::fragCoords1(), ImageStrings<Type::V>::fragCoords2(),
85 ImageStrings<Type::V>::offsets(), ImageStrings<Type::V>::rotations() }
86
113
114const int TEXCOORD_VAR_LEN = 16;
115
116void textureCoordVaryingName(char (&outString)[TEXCOORD_VAR_LEN], quint8 uvSet)
117{
118 // For now, uvSet will be less than 2.
119 // But this value will be verified in the setProperty function.
120 Q_ASSERT(uvSet < 9);
121 qstrncpy(outString, "qt_varTexCoordX", TEXCOORD_VAR_LEN);
122 outString[14] = '0' + uvSet;
123}
124
125void textureCoordVariableName(char (&outString)[TEXCOORD_VAR_LEN], quint8 uvSet)
126{
127 // For now, uvSet will be less than 2.
128 // But this value will be verified in the setProperty function.
129 Q_ASSERT(uvSet < 9);
130 qstrncpy(outString, "qt_texCoordX", TEXCOORD_VAR_LEN);
131 outString[11] = '0' + uvSet;
132}
133
134}
135
136const char *QSSGMaterialShaderGenerator::getSamplerName(QSSGRenderableImage::Type type)
137{
138 return imageStringTable[int(type)].imageSampler;
139}
140
141static void addLocalVariable(QSSGStageGeneratorBase &inGenerator, const QByteArray &inName, const QByteArray &inType)
142{
143 inGenerator << " " << inType << " " << inName << ";\n";
144}
145
146static QByteArray uvTransform(const QByteArray& imageRotations, const QByteArray& imageOffsets)
147{
148 QByteArray transform;
149 transform = " qt_uTransform = vec3(" + imageRotations + ".x, " + imageRotations + ".y, " + imageOffsets + ".x);\n";
150 transform += " qt_vTransform = vec3(" + imageRotations + ".z, " + imageRotations + ".w, " + imageOffsets + ".y);\n";
151 return transform;
152}
153
154static void generateImageUVCoordinates(QSSGMaterialVertexPipeline &vertexShader,
155 QSSGStageGeneratorBase &fragmentShader,
156 const QSSGShaderDefaultMaterialKey &key,
157 const ImageStringSet &names,
158 bool forceFragmentShader = false,
159 quint32 uvSet = 0,
160 bool reuseImageCoords = false,
161 bool useEnvironmentMapping = false)
162{
163 char textureCoordName[TEXCOORD_VAR_LEN];
164 fragmentShader.addUniform(names.imageSampler, "sampler2D");
165 if (!forceFragmentShader) {
166 vertexShader.addUniform(names.imageOffsets, "vec3");
167 vertexShader.addUniform(names.imageRotations, "vec4");
168 } else {
169 fragmentShader.addUniform(names.imageOffsets, "vec3");
170 fragmentShader.addUniform(names.imageRotations, "vec4");
171 }
172 QByteArray uvTrans = uvTransform(names.imageRotations, names.imageOffsets);
173 if (!useEnvironmentMapping) { // default to UV mapping
174 if (!forceFragmentShader) {
175 vertexShader << uvTrans;
176 vertexShader.addOutgoing(names.imageFragCoords, "vec2");
177 vertexShader.addFunction("getTransformedUVCoords");
178 } else {
179 fragmentShader << uvTrans;
180 fragmentShader.addFunction("getTransformedUVCoords");
181 }
182 vertexShader.generateUVCoords(uvSet, key);
183 if (!forceFragmentShader) {
184 textureCoordVaryingName(textureCoordName, uvSet);
185 vertexShader << " vec2 " << names.imageFragCoordsTemp << " = qt_getTransformedUVCoords(vec3(" << textureCoordName << ", 1.0), qt_uTransform, qt_vTransform);\n";
186 vertexShader.assignOutput(names.imageFragCoords, names.imageFragCoordsTemp);
187 } else {
188 textureCoordVariableName(textureCoordName, uvSet);
189 if (reuseImageCoords)
190 fragmentShader << " ";
191 else
192 fragmentShader << " vec2 ";
193 fragmentShader << names.imageFragCoords << " = qt_getTransformedUVCoords(vec3(" << textureCoordName << ", 1.0), qt_uTransform, qt_vTransform);\n";
194 }
195 } else {
196 fragmentShader.addUniform(names.imageOffsets, "vec3");
197 fragmentShader.addUniform(names.imageRotations, "vec4");
198 fragmentShader << uvTrans;
199 vertexShader.generateEnvMapReflection(key);
200 fragmentShader.addFunction("getTransformedUVCoords");
201 if (reuseImageCoords)
202 fragmentShader << " ";
203 else
204 fragmentShader << " vec2 ";
205 fragmentShader << names.imageFragCoords << " = qt_getTransformedUVCoords(environment_map_reflection, qt_uTransform, qt_vTransform);\n";
206 }
207}
208
209static void generateImageUVSampler(QSSGMaterialVertexPipeline &vertexGenerator,
210 QSSGStageGeneratorBase &fragmentShader,
211 const QSSGShaderDefaultMaterialKey &key,
212 const ImageStringSet &names,
213 char (&outString)[TEXCOORD_VAR_LEN],
214 quint8 uvSet = 0)
215{
216 fragmentShader.addUniform(names.imageSampler, "sampler2D");
217 // NOTE: Actually update the uniform name here
218 textureCoordVariableName(outString, uvSet);
219 vertexGenerator.generateUVCoords(uvSet, key);
220}
221
222static inline QSSGShaderMaterialAdapter *getMaterialAdapter(const QSSGRenderGraphObject &inMaterial)
223{
224 switch (inMaterial.type) {
225 case QSSGRenderGraphObject::Type::DefaultMaterial:
226 case QSSGRenderGraphObject::Type::PrincipledMaterial:
227 case QSSGRenderGraphObject::Type::SpecularGlossyMaterial:
228 return static_cast<const QSSGRenderDefaultMaterial &>(inMaterial).adapter;
229 case QSSGRenderGraphObject::Type::CustomMaterial:
230 return static_cast<const QSSGRenderCustomMaterial &>(inMaterial).adapter;
231 default:
232 break;
233 }
234 return nullptr;
235}
236
237// NOTE!!!: PLEASE ADD NEW VARS HERE!
239 { "DIFFUSE" },
240 { "BASE_COLOR" },
241 { "METALNESS" },
242 { "ROUGHNESS" },
243 { "EMISSIVE" },
244 { "SPECULAR_AMOUNT" },
245 { "EMISSIVE_COLOR" },
246 { "LIGHT_COLOR" },
247 { "LIGHT_ATTENUATION" },
248 { "SPOT_FACTOR" },
249 { "SHADOW_CONTRIB" },
250 { "FRESNEL_CONTRIB" },
251 { "TO_LIGHT_DIR" },
252 { "NORMAL" },
253 { "VIEW_VECTOR" },
254 { "TOTAL_AMBIENT_COLOR" },
255 { "COLOR_SUM" },
256 { "BINORMAL" },
257 { "TANGENT" },
258 { "FRESNEL_POWER" },
259 { "INSTANCE_MODEL_MATRIX" },
260 { "INSTANCE_MODELVIEWPROJECTION_MATRIX" },
261 { "UV0" },
262 { "UV1" },
263 { "VERTEX" },
264 { "FRESNEL_SCALE" },
265 { "FRESNEL_BIAS" },
266 { "CLEARCOAT_FRESNEL_POWER" },
267 { "CLEARCOAT_FRESNEL_SCALE" },
268 { "CLEARCOAT_FRESNEL_BIAS" },
269 { "CLEARCOAT_AMOUNT" },
270 { "CLEARCOAT_NORMAL" },
271 { "CLEARCOAT_ROUGHNESS" },
272 { "SHEEN_COLOR" },
273 { "SHEEN_ROUGHNESS" },
274 { "ANISOTROPY_STRENGTH" },
275 { "ANISOTROPY_ROTATION" },
276 { "IRIDESCENCE_FACTOR" },
277 { "IRIDESCENCE_IOR" },
278 { "IRIDESCENCE_THICKNESS" },
279 { "DISPERSION" },
280 { "IOR" },
281 { "TRANSMISSION_FACTOR" },
282 { "THICKNESS_FACTOR" },
283 { "ATTENUATION_COLOR" },
284 { "ATTENUATION_DISTANCE" },
285 { "OCCLUSION_AMOUNT" },
286};
287
288const char *QSSGMaterialShaderGenerator::directionalLightProcessorArgumentList()
289{
290 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";
291}
292
293const char *QSSGMaterialShaderGenerator::pointLightProcessorArgumentList()
294{
295 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";
296}
297
298const char *QSSGMaterialShaderGenerator::spotLightProcessorArgumentList()
299{
300 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";
301}
302
303const char *QSSGMaterialShaderGenerator::ambientLightProcessorArgumentList()
304{
305 return "inout vec3 DIFFUSE, in vec3 TOTAL_AMBIENT_COLOR, in vec3 NORMAL, in vec3 VIEW_VECTOR";
306}
307
308const char *QSSGMaterialShaderGenerator::specularLightProcessorArgumentList()
309{
310 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";
311}
312
313const char *QSSGMaterialShaderGenerator::shadedFragmentMainArgumentList()
314{
315 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, inout float IOR, inout float OCCLUSION_AMOUNT";
316}
317
318const char *QSSGMaterialShaderGenerator::postProcessorArgumentList()
319{
320 return "inout vec4 COLOR_SUM, in vec4 DIFFUSE, in vec3 SPECULAR, in vec3 EMISSIVE, in vec2 UV0, in vec2 UV1";
321}
322
323const char *QSSGMaterialShaderGenerator::iblProbeProcessorArgumentList()
324{
325 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";
326}
327
328const char *QSSGMaterialShaderGenerator::vertexMainArgumentList()
329{
330 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";
331}
332
333const char *QSSGMaterialShaderGenerator::vertexInstancedMainArgumentList()
334{
335 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";
336}
337
338#define MAX_MORPH_TARGET 8
339
340static void generateFragmentDefines(QSSGStageGeneratorBase &fragmentShader,
341 const QSSGShaderDefaultMaterialKey &inKey,
342 const QSSGShaderDefaultMaterialKeyProperties &keyProps,
343 QSSGShaderMaterialAdapter *materialAdapter,
344 QSSGShaderLibraryManager &shaderLibraryManager,
345 const QSSGUserShaderAugmentation &shaderAugmentation)
346{
347 if (materialAdapter->hasCustomShaderSnippet(QSSGShaderCache::ShaderType::Fragment)) {
348 auto hasCustomFunction = [&shaderLibraryManager, materialAdapter](const QByteArray &funcName) {
349 return materialAdapter->hasCustomShaderFunction(QSSGShaderCache::ShaderType::Fragment, funcName, shaderLibraryManager);
350 };
351
352 if (hasCustomFunction(QByteArrayLiteral("qt_directionalLightProcessor")))
353 fragmentShader.addDefinition("QSSG_CUSTOM_MATERIAL_DIRECTIONAL_LIGHT_PROCESSOR", "1");
354 if (hasCustomFunction(QByteArrayLiteral("qt_pointLightProcessor")))
355 fragmentShader.addDefinition("QSSG_CUSTOM_MATERIAL_POINT_LIGHT_PROCESSOR", "1");
356 if (hasCustomFunction(QByteArrayLiteral("qt_spotLightProcessor")))
357 fragmentShader.addDefinition("QSSG_CUSTOM_MATERIAL_SPOT_LIGHT_PROCESSOR", "1");
358 if (hasCustomFunction(QByteArrayLiteral("qt_specularLightProcessor")))
359 fragmentShader.addDefinition("QSSG_CUSTOM_MATERIAL_SPECULAR_PROCESSOR", "1");
360 if (hasCustomFunction(QByteArrayLiteral("qt_iblProbeProcessor")))
361 fragmentShader.addDefinition("QSSG_CUSTOM_MATERIAL_IBL_PROBE_PROCESSOR", "1");
362 if (hasCustomFunction(QByteArrayLiteral("qt_ambientLightProcessor")))
363 fragmentShader.addDefinition("QSSG_CUSTOM_MATERIAL_AMBIENT_LIGHT_PROCESSOR", "1");
364 if (hasCustomFunction(QByteArrayLiteral("qt_postProcessor")))
365 fragmentShader.addDefinition("QSSG_CUSTOM_MATERIAL_POST_PROCESSOR", "1");
366 }
367
368 if (materialAdapter->usesSharedVariables())
369 fragmentShader.addDefinition("QSSG_CUSTOM_MATERIAL_SHARED_VARIABLES", "1");
370
371 if (keyProps.m_hasShadows.getValue(inKey))
372 fragmentShader.addDefinition("QSSG_ENABLE_SHADOWMAPPING", "1");
373 if (keyProps.m_specularEnabled.getValue(inKey))
374 fragmentShader.addDefinition("QSSG_ENABLE_SPECULAR", "1");
375 if (keyProps.m_clearcoatEnabled.getValue(inKey))
376 fragmentShader.addDefinition("QSSG_ENABLE_CLEARCOAT", "1");
377 if (keyProps.m_sheenEnabled.getValue(inKey))
378 fragmentShader.addDefinition("QSSG_ENABLE_SHEEN", "1");
379 if (keyProps.m_transmissionEnabled.getValue(inKey))
380 fragmentShader.addDefinition("QSSG_ENABLE_TRANSMISSION", "1");
381 if (keyProps.m_dispersionEnabled.getValue(inKey) && keyProps.m_transmissionEnabled.getValue(inKey))
382 fragmentShader.addDefinition("QSSG_ENABLE_DISPERSION", "1");
383 if (keyProps.m_metallicRoughnessEnabled.getValue(inKey))
384 fragmentShader.addDefinition("QSSG_ENABLE_METALLIC_ROUGHNESS_WORKFLOW", "1");
385 if (keyProps.m_specularGlossyEnabled.getValue(inKey))
386 fragmentShader.addDefinition("QSSG_ENABLE_SPECULAR_GLOSSY_WORKFLOW", "1");
387 switch (keyProps.m_diffuseModel.getDiffuseModel(inKey)) {
388 case QSSGRenderDefaultMaterial::MaterialDiffuseModel::Burley:
389 fragmentShader.addDefinition("QSSG_ENABLE_DIFFUSE_MODEL_BURLEY", "1");
390 break;
391 case QSSGRenderDefaultMaterial::MaterialDiffuseModel::Lambert:
392 fragmentShader.addDefinition("QSSG_ENABLE_DIFFUSE_MODEL_LAMBERT", "1");
393 break;
394 case QSSGRenderDefaultMaterial::MaterialDiffuseModel::LambertWrap:
395 fragmentShader.addDefinition("QSSG_ENABLE_DIFFUSE_MODEL_LAMBERT_WRAP", "1");
396 break;
397 }
398 switch (keyProps.m_specularModel.getSpecularModel(inKey)) {
399 case QSSGRenderDefaultMaterial::MaterialSpecularModel::BlinnPhong:
400 fragmentShader.addDefinition("QSSG_ENABLE_SPECULAR_MODEL_BLINN_PHONG", "1");
401 break;
402 case QSSGRenderDefaultMaterial::MaterialSpecularModel::SchlickGGX:
403 fragmentShader.addDefinition("QSSG_ENABLE_SPECULAR_MODEL_SCHLICK_GGX", "1");
404 break;
405 }
406 // Shadow softness
407 switch (keyProps.m_shadowSoftness.getShadowSoftness(inKey)) {
408 case QSSGRenderLight::SoftShadowQuality::Hard:
409 fragmentShader.addDefinition("QSSG_SHADOW_SOFTNESS", "0");
410 break;
411 case QSSGRenderLight::SoftShadowQuality::PCF4:
412 fragmentShader.addDefinition("QSSG_SHADOW_SOFTNESS", "4");
413 break;
414 case QSSGRenderLight::SoftShadowQuality::PCF8:
415 fragmentShader.addDefinition("QSSG_SHADOW_SOFTNESS", "8");
416 break;
417 case QSSGRenderLight::SoftShadowQuality::PCF16:
418 case QSSGRenderLight::SoftShadowQuality::PCF32:
419 case QSSGRenderLight::SoftShadowQuality::PCF64:
420 fragmentShader.addDefinition("QSSG_SHADOW_SOFTNESS", "16");
421 break;
422 ;}
423
424
425 for (const auto &def : std::as_const(shaderAugmentation.defines))
426 fragmentShader.addDefinition(def.name, def.value);
427}
428
431 bool m_isActive = false;
433
434 // Use shared texcoord when transforms are identity
438
440 {
441 switch (type) {
442 case QSSGRenderableImage::Type::Diffuse:
443 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::DiffuseMap;
444 case QSSGRenderableImage::Type::Opacity:
445 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::OpacityMap;
446 case QSSGRenderableImage::Type::Specular:
447 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::SpecularMap;
448 case QSSGRenderableImage::Type::Emissive:
449 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::EmissiveMap;
450 case QSSGRenderableImage::Type::Bump:
451 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::BumpMap;
452 case QSSGRenderableImage::Type::SpecularAmountMap:
453 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::SpecularAmountMap;
454 case QSSGRenderableImage::Type::Normal:
455 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::NormalMap;
456 case QSSGRenderableImage::Type::Translucency:
457 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::TranslucencyMap;
458 case QSSGRenderableImage::Type::Roughness:
459 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::RoughnessMap;
460 case QSSGRenderableImage::Type::BaseColor:
461 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::BaseColorMap;
462 case QSSGRenderableImage::Type::Metalness:
463 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::MetalnessMap;
464 case QSSGRenderableImage::Type::Occlusion:
465 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::OcclusionMap;
466 case QSSGRenderableImage::Type::Height:
467 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::HeightMap;
468 case QSSGRenderableImage::Type::Clearcoat:
469 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::ClearcoatMap;
470 case QSSGRenderableImage::Type::ClearcoatRoughness:
471 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::ClearcoatRoughnessMap;
472 case QSSGRenderableImage::Type::ClearcoatNormal:
473 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::ClearcoatNormalMap;
474 case QSSGRenderableImage::Type::Transmission:
475 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::TransmissionMap;
476 case QSSGRenderableImage::Type::Thickness:
477 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::ThicknessMap;
478 case QSSGRenderableImage::Type::SheenColor:
479 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::SheenColorMap;
480 case QSSGRenderableImage::Type::SheenRoughness:
481 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::SheenRoughnessMap;
482 case QSSGRenderableImage::Type::Anisotropy:
483 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::AnisotropyMap;
484 case QSSGRenderableImage::Type::Iridescence:
485 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::IridescenceMap;
486 case QSSGRenderableImage::Type::IridescenceThickness:
487 return QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::IridescenceThicknessMap;
488 case QSSGRenderableImage::Type::Unknown:
489 break;
490 }
491 return {};
492 }
493
494 SamplerState(const QSSGShaderDefaultMaterialKey &inKey, const QSSGShaderDefaultMaterialKeyProperties &keyProps)
495 : m_inKey(inKey)
497 {
498 for (int i = 0; i < QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::ImageMapCount; ++i) {
499 const QSSGShaderDefaultMaterialKeyProperties::ImageMapNames mapName = QSSGShaderDefaultMaterialKeyProperties::ImageMapNames(i);
500 const QSSGShaderKeyImageMap &mapValue = m_keyProps.m_imageMaps[mapName];
501 // just check if any are enabled
502 if (mapValue.isEnabled(m_inKey))
503 m_isActive = true;
504 }
505 }
506
507 bool isActive() const { return m_isActive; }
508
509 bool hasImage(QSSGRenderableImage::Type type) const
510 {
511 if (auto imageType = fromType(type))
512 return m_keyProps.m_imageMaps[*imageType].isEnabled(m_inKey);
513
514 return false;
515 }
516
517 bool uvGenerated(QSSGShaderDefaultMaterialKeyProperties::ImageMapNames imageType) const
518 {
519 if (imageType < 0 || imageType >= QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::ImageMapCount)
520 return false;
521 return uvCoordinatesGenerated[size_t(imageType)];
522 }
523
524 void generateImageUVAndSampler(QSSGRenderableImage::Type imageType,
525 QSSGMaterialVertexPipeline &vertexShader,
526 QSSGStageGeneratorBase &fragmentShader,
527 const QSSGShaderDefaultMaterialKey &key,
528 bool forceFragmentShader = false)
529 {
530 if (auto mapType = fromType(imageType)) {
531 if (uvGenerated(*mapType))
532 return; // don't do it a second time
533
534 const QSSGShaderKeyImageMap &mapValue = m_keyProps.m_imageMaps[size_t(*mapType)];
535 const quint8 indexUV = mapValue.isUsingUV1(m_inKey) ? 1 : 0;
536 const auto &samplerNames = imageStringTable[int(imageType)]; // using the QSSGRenderableImage::Type
537 if (mapValue.isIdentityTransform(m_inKey)) {
538 generateImageUVSampler(vertexShader, fragmentShader, key, samplerNames, imageFragCoords, indexUV);
539 } else {
541 // The frist time we need to declare the shared variable
542 fragmentShader.append(" vec3 qt_uTransform;");
543 fragmentShader.append(" vec3 qt_vTransform;");
545 }
546 generateImageUVCoordinates(vertexShader, fragmentShader, key, samplerNames, forceFragmentShader, indexUV, false, mapValue.isEnvMap(m_inKey) || mapValue.isLightProbe(m_inKey));
547 }
548 uvCoordinatesGenerated[int(*mapType)] = true;
549 }
550 }
551
552 const char *samplerName(QSSGRenderableImage::Type imageType) const
553 {
554 if (imageType <= QSSGRenderableImage::Type::Unknown)
555 return "";
556 return imageStringTable[int(imageType)].imageSampler;
557 }
558
559 const char *fragCoordsName(QSSGRenderableImage::Type imageType) const
560 {
561 if (auto mapType = fromType(imageType)) {
562 if (!uvGenerated(*mapType))
563 qWarning("Requesting image frag coords for image type %d that has not been generated", int(imageType));
564
565 const QSSGShaderKeyImageMap &mapValue = m_keyProps.m_imageMaps[size_t(*mapType)];
566 if (mapValue.isIdentityTransform(m_inKey))
567 return imageFragCoords;
568
569 return imageStringTable[int(imageType)].imageFragCoords;
570 }
571
572 return "";
573 }
574
575};
576
589
590 // Requirments for Pass
591 bool needsBaseColor = false; // qt_diffuseColor
592 bool needsRoughness = false; // qt_roughnessAmount
593 bool needsMetalness = false; // qt_metalnessAmount
594 bool needsDiffuseLight = false; // global_diffuse_light
595 bool needsSpecularLight = false; // global_specular_light
596 bool needsEmission = false; // global_emission
597 bool needsWorldNormal = false; // qt_world_normal
598 bool needsWorldTangent = false; // qt_tangent
599 bool needsWorldBinormal = false; // qt_binormal
600
601 bool needsF0 = false; // qt_f0
602 bool needsF90 = false; // qt_f90
603 bool needsAmbientOcclusion = false; // qt_ao_factor
604
605
606 // Available Information
607 bool hasVertexColors = false;
608 bool hasLighting = false;
609 bool hasPunctualLights = false;
610 bool hasSpecularLight = false;
611 bool hasIblProbe = false;
612 bool hasReflectionProbe = false;
613 bool hasIblOrientation = false;
614 bool hasShadowMap = false;
615 bool hasSSAOMap = false;
616 bool hasLightMap = false;
617 bool hasBumpNormalMap = false;
618 bool hasParallaxMapping = false;
619 bool hasClearcoat = false;
620 bool hasSheen = false;
621 bool hasAnisotropy = false;
622 bool hasIridescence = false;
623 bool hasDispersion = false;
625 bool hasTransmission = false;
628 bool hasFog = false;
629
630
631 // Material Properties
632 bool isDoubleSided = false;
636 bool isPbrMaterial = false;
638 bool isUserPass = false;
640 int viewCount = 1;
642 bool oitMSAA = false;
644
645 PassRequirmentsState(const QSSGShaderDefaultMaterialKey &inKey,
646 const QSSGShaderDefaultMaterialKeyProperties &keyProps,
647 const QSSGShaderFeatures &featureSet,
648 const SamplerState &samplerState,
649 const QSSGUserShaderAugmentation &shaderAugmentation)
650 {
651 const bool isDepthPass = featureSet.isSet(QSSGShaderFeatures::Feature::DepthPass);
652 const bool isOrthoShadowPass = featureSet.isSet(QSSGShaderFeatures::Feature::OrthoShadowPass);
653 const bool isPerspectiveShadowPass = featureSet.isSet(QSSGShaderFeatures::Feature::PerspectiveShadowPass);
654 isOpaqueDepthPrePass = featureSet.isSet(QSSGShaderFeatures::Feature::OpaqueDepthPrePass);
655 const bool isNormalPass = featureSet.isSet(QSSGShaderFeatures::Feature::NormalPass);
656
657 hasVertexColors = keyProps.m_vertexColorsEnabled.getValue(inKey)
658 || keyProps.m_usesVarColor.getValue(inKey)
659 || keyProps.m_vertexColorsMaskEnabled.getValue(inKey)
660 || keyProps.m_usesInstancing.getValue(inKey)
661 || keyProps.m_blendParticles.getValue(inKey);
662 hasLighting = keyProps.m_hasLighting.getValue(inKey);
663 hasPunctualLights = keyProps.m_hasPunctualLights.getValue(inKey);
664 isDoubleSided = keyProps.m_isDoubleSided.getValue(inKey);
665 hasIblProbe = keyProps.m_hasIbl.getValue(inKey);
666 hasReflectionProbe = featureSet.isSet(QSSGShaderFeatures::Feature::ReflectionProbe);
667 oitMethod = static_cast<QSSGRenderLayer::OITMethod>(keyProps.m_orderIndependentTransparency.getValue(inKey));
668 oitMSAA = keyProps.m_oitMSAA.getValue(inKey);
669 isSpecularAAEnabled = keyProps.m_specularAAEnabled.getValue(inKey);
670
671 // TODO: Not sure I agree with the following, but this is the current behavior
672 hasSpecularLight |= keyProps.m_specularEnabled.getValue(inKey);
675 hasSpecularLight |= samplerState.hasImage(QSSGRenderableImage::Type::SpecularAmountMap);
676
677 hasIblOrientation = featureSet.isSet(QSSGShaderFeatures::Feature::IblOrientation);
678 hasShadowMap = featureSet.isSet(QSSGShaderFeatures::Feature::Ssm);
679 hasSSAOMap = featureSet.isSet(QSSGShaderFeatures::Feature::Ssao);
680 hasLightMap = featureSet.isSet(QSSGShaderFeatures::Feature::Lightmap);
681 hasBumpNormalMap = samplerState.hasImage(QSSGRenderableImage::Type::Normal) || samplerState.hasImage(QSSGRenderableImage::Type::Bump);
682 hasParallaxMapping = samplerState.hasImage(QSSGRenderableImage::Type::Height);
683 hasClearcoat = keyProps.m_clearcoatEnabled.getValue(inKey);
684 hasSheen = keyProps.m_sheenEnabled.getValue(inKey);
685 // Anisotropy only modifies the specular lobe
686 hasAnisotropy = keyProps.m_anisotropyEnabled.getValue(inKey) && hasSpecularLight;
687 // Iridescence only modifies the specular lobe
688 hasIridescence = keyProps.m_iridescenceEnabled.getValue(inKey) && hasSpecularLight;
689 hasTransmission = keyProps.m_transmissionEnabled.getValue(inKey);
690 // Declared whenever requested, but only applied with transmission
691 hasDispersionKeyword = keyProps.m_dispersionEnabled.getValue(inKey);
693 hasFresnelScaleBias = keyProps.m_fresnelScaleBiasEnabled.getValue(inKey);
694 hasClearcoatFresnelScaleBias = keyProps.m_clearcoatFresnelScaleBiasEnabled.getValue(inKey);
695 isMetallicRoughnessWorkflow = keyProps.m_metallicRoughnessEnabled.getValue(inKey);
696 isSpecularGlossinessWorkflow = keyProps.m_specularGlossyEnabled.getValue(inKey);
698 isUserPass = featureSet.isSet(QSSGShaderFeatures::Feature::UserRenderPass);
699 hasFog = keyProps.m_fogEnabled.getValue(inKey);
700 numMorphTargets = keyProps.m_targetCount.getValue(inKey);
701 viewCount = featureSet.isSet(QSSGShaderFeatures::Feature::DisableMultiView) ? 1 : keyProps.m_viewCount.getValue(inKey);
702
703 if (isDepthPass) {
704 passType = Depth;
706 needsBaseColor = true;
707 } else if (isOrthoShadowPass) {
710 needsBaseColor = true;
711 } else if (isPerspectiveShadowPass) {
714 needsBaseColor = true;
715 } else if (isNormalPass) {
717 needsWorldNormal = true;
718 needsWorldTangent = true;
719 needsWorldBinormal = true;
720 needsRoughness = true;
721 } else if (isUserPass) {
722 passType = User;
723 // Use shaderAugmentation to figure out what features are needed
724 needsBaseColor = shaderAugmentation.needsBaseColor;
725 needsRoughness = shaderAugmentation.needsRoughness;
726 needsMetalness = shaderAugmentation.needsMetalness;
727 needsEmission = shaderAugmentation.needsEmissiveLight;
728 needsWorldNormal = shaderAugmentation.needsWorldNormal;
729 // WORLD_NORMAL for a normal-mapped material requires tangent space to be set up.
730 // Ensure tangent/binormal are generated whenever the world normal is requested.
731 needsWorldTangent = shaderAugmentation.needsWorldTangent || shaderAugmentation.needsWorldNormal;
732 needsWorldBinormal = shaderAugmentation.needsWorldBinormal || shaderAugmentation.needsWorldNormal;
733
734 if (shaderAugmentation.needsDiffuseLight ||
735 shaderAugmentation.needsSpecularLight ||
736 shaderAugmentation.needsF0 ||
737 shaderAugmentation.needsF90) {
738 // Turn everything on for now
739 needsBaseColor = true;
740 needsRoughness = true;
741 needsMetalness = true;
742 needsDiffuseLight = true;
743 needsSpecularLight = true;
744 needsEmission = true;
745 needsWorldNormal = true;
746 needsWorldTangent = true;
747 needsWorldBinormal = true;
748 needsF0 = true;
749 needsF90 = true;
751 }
752
753 } else {
754 // Either a Color or Debug Pass
755 passType = Color;
756 debugMode = QSSGRenderLayer::MaterialDebugMode(keyProps.m_debugMode.getValue(inKey));
757 if (debugMode == QSSGRenderLayer::MaterialDebugMode::None) {
758 needsBaseColor = true;
759 needsRoughness = true;
760 needsMetalness = true;
761 needsDiffuseLight = true;
762 needsSpecularLight = true;
763 needsEmission = true;
764 needsWorldNormal = true;
765 needsWorldTangent = true;
766 needsWorldBinormal = true;
767 needsF0 = true;
768 needsF90 = true;
770 } else {
771 passType = Debug;
772 switch (debugMode) {
773 case QSSGRenderLayer::MaterialDebugMode::None:
774 break;
775 case QSSGRenderLayer::MaterialDebugMode::BaseColor:
776 needsBaseColor = true;
777 break;
778 case QSSGRenderLayer::MaterialDebugMode::Roughness:
779 needsRoughness = true;
780 break;
781 case QSSGRenderLayer::MaterialDebugMode::Metalness:
782 needsMetalness = true;
783 break;
784 case QSSGRenderLayer::MaterialDebugMode::Diffuse:
785 needsBaseColor = true;
786 needsRoughness = true;
787 needsMetalness = true;
788 needsDiffuseLight = true;
789 needsEmission = true;
790 needsWorldNormal = true;
791 needsWorldTangent = true;
792 needsWorldBinormal = true;
793 needsF0 = true;
794 needsF90 = true;
796 break;
797 case QSSGRenderLayer::MaterialDebugMode::Specular:
798 needsBaseColor = true;
799 needsRoughness = true;
800 needsMetalness = true;
801 needsSpecularLight = true;
802 needsEmission = true;
803 needsWorldNormal = true;
804 needsWorldTangent = true;
805 needsWorldBinormal = true;
806 needsF0 = true;
807 needsF90 = true;
809 break;
810 case QSSGRenderLayer::MaterialDebugMode::ShadowOcclusion:
811 break;
812 case QSSGRenderLayer::MaterialDebugMode::Emission:
813 needsEmission = true;
814 break;
815 case QSSGRenderLayer::MaterialDebugMode::AmbientOcclusion:
817 break;
818 case QSSGRenderLayer::MaterialDebugMode::Normal:
819 needsWorldNormal = true;
820 break;
821 case QSSGRenderLayer::MaterialDebugMode::Tangent:
822 needsWorldTangent = true;
823 break;
824 case QSSGRenderLayer::MaterialDebugMode::Binormal:
825 needsWorldBinormal = true;
826 break;
827 case QSSGRenderLayer::MaterialDebugMode::F0:
828 needsBaseColor = true;
829 needsRoughness = true;
830 needsMetalness = true;
831 needsSpecularLight = true;
832 needsEmission = true;
833 needsWorldNormal = true;
834 needsWorldTangent = true;
835 needsWorldBinormal = true;
836 needsF0 = true;
837 needsF90 = true;
839 break;
840 }
841 }
842 }
843
844 // Parallax mapping requires world tangent/binormal in all pass types,
845 // since qt_tangent and qt_binormal are passed to qt_parallaxMapping().
846 if (hasParallaxMapping) {
847 needsWorldNormal = true;
848 needsWorldTangent = true;
849 needsWorldBinormal = true;
850 }
851
852 // Normal/bump map sampling uses the TBN matrix (qt_tangent, qt_binormal)
853 // to transform the sampled normal to world space. Ensure tangent and binormal
854 // are generated whenever a normal map is present and the world normal is needed.
856 needsWorldTangent = true;
857 needsWorldBinormal = true;
858 }
859
860 // Anisotropy is oriented by the tangent frame
861 if (hasAnisotropy) {
862 needsWorldNormal = true;
863 needsWorldTangent = true;
864 needsWorldBinormal = true;
865 }
866 }
867
869 // In debug passes, the custom fragment main must be called so that
870 // material-derived values (e.g. NORMAL modified by normal-map sampling code in
871 // MAIN()) are applied before the debug output is generated.
872 if (passType == Debug)
873 return true;
875 }
876
880
881};
882
883
884static void generateFragmentShader(QSSGStageGeneratorBase &fragmentShader,
885 QSSGMaterialVertexPipeline &vertexShader,
886 const QSSGShaderDefaultMaterialKey &inKey,
887 const QSSGShaderDefaultMaterialKeyProperties &keyProps,
888 const QSSGShaderFeatures &featureSet,
889 const QSSGRenderGraphObject &inMaterial,
890 const QSSGUserShaderAugmentation &shaderAugmentation,
891 QSSGShaderLibraryManager &shaderLibraryManager)
892{
893 QSSGShaderMaterialAdapter *materialAdapter = getMaterialAdapter(inMaterial);
894 auto hasCustomFunction = [&shaderLibraryManager, materialAdapter](const QByteArray &funcName) {
895 return materialAdapter->hasCustomShaderFunction(QSSGShaderCache::ShaderType::Fragment,
896 funcName,
897 shaderLibraryManager);
898 };
899
900 auto channelStr = [](const QSSGShaderKeyTextureChannel &chProp, const QSSGShaderDefaultMaterialKey &inKey) -> QByteArray {
901 QByteArray ret;
902 switch (chProp.getTextureChannel(inKey)) {
903 case QSSGShaderKeyTextureChannel::R:
904 ret.append(".r");
905 break;
906 case QSSGShaderKeyTextureChannel::G:
907 ret.append(".g");
908 break;
909 case QSSGShaderKeyTextureChannel::B:
910 ret.append(".b");
911 break;
912 case QSSGShaderKeyTextureChannel::A:
913 ret.append(".a");
914 break;
915 }
916 return ret;
917 };
918
919 auto maskVariableByVertexColorChannel = [&fragmentShader, keyProps, inKey]( const QByteArray &maskVariable, const QSSGRenderDefaultMaterial::VertexColorMask &maskEnum ){
920 if (keyProps.m_vertexColorsMaskEnabled.getValue(inKey)) {
921 if ( keyProps.m_vertexColorRedMask.getValue(inKey) & maskEnum )
922 fragmentShader << " " << maskVariable << " *= qt_vertColorMask.r;\n";
923 else if ( keyProps.m_vertexColorGreenMask.getValue(inKey) & maskEnum )
924 fragmentShader << " " << maskVariable << " *= qt_vertColorMask.g;\n";
925 else if ( keyProps.m_vertexColorBlueMask.getValue(inKey) & maskEnum )
926 fragmentShader << " " << maskVariable << " *= qt_vertColorMask.b;\n";
927 else if ( keyProps.m_vertexColorAlphaMask.getValue(inKey) & maskEnum )
928 fragmentShader << " " << maskVariable << " *= qt_vertColorMask.a;\n";
929 }
930 };
931
932 generateFragmentDefines(fragmentShader, inKey, keyProps, materialAdapter, shaderLibraryManager, shaderAugmentation);
933
934 // Determine the available texture channels
935 SamplerState samplerState(inKey, keyProps);
936 // Determine the requirements of this rendering pass
937 const PassRequirmentsState passRequirmentState(inKey, keyProps, featureSet, samplerState, shaderAugmentation);
938
939 const bool hasCustomVert = materialAdapter->hasCustomShaderSnippet(QSSGShaderCache::ShaderType::Vertex);
940
941 const int viewCount = featureSet.isSet(QSSGShaderFeatures::Feature::DisableMultiView)
942 ? 1 : keyProps.m_viewCount.getValue(inKey);
943
944 // Morphing
945 if (passRequirmentState.numMorphTargets > 0 || hasCustomVert) {
946 vertexShader.addDefinition(QByteArrayLiteral("QT_MORPH_MAX_COUNT"),
947 QByteArray::number(passRequirmentState.numMorphTargets));
948 quint8 offset;
949 if ((offset = keyProps.m_targetPositionOffset.getValue(inKey)) < UINT8_MAX) {
950 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_POSITION_OFFSET"),
951 QByteArray::number(offset));
952 }
953 if ((offset = keyProps.m_targetNormalOffset.getValue(inKey)) < UINT8_MAX) {
954 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_NORMAL_OFFSET"),
955 QByteArray::number(offset));
956 }
957 if ((offset = keyProps.m_targetTangentOffset.getValue(inKey)) < UINT8_MAX) {
958 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_TANGENT_OFFSET"),
959 QByteArray::number(offset));
960 }
961 if ((offset = keyProps.m_targetBinormalOffset.getValue(inKey)) < UINT8_MAX) {
962 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_BINORMAL_OFFSET"),
963 QByteArray::number(offset));
964 }
965 if ((offset = keyProps.m_targetTexCoord0Offset.getValue(inKey)) < UINT8_MAX) {
966 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_TEX0_OFFSET"),
967 QByteArray::number(offset));
968 }
969 if ((offset = keyProps.m_targetTexCoord1Offset.getValue(inKey)) < UINT8_MAX) {
970 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_TEX1_OFFSET"),
971 QByteArray::number(offset));
972 }
973 if ((offset = keyProps.m_targetColorOffset.getValue(inKey)) < UINT8_MAX) {
974 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_COLOR_OFFSET"),
975 QByteArray::number(offset));
976 }
977 }
978
979 if (passRequirmentState.passType == PassRequirmentsState::User) {
980 if (shaderAugmentation.hasUserAugmentation())
981 fragmentShader << shaderAugmentation.preamble;
982 }
983
984 for (const auto &u : shaderAugmentation.propertyUniforms)
985 fragmentShader.addUniform(u.name, u.typeName);
986
987 // Unshaded custom materials need no code in main (apart from calling qt_customMain)
988 const bool hasCustomFrag = materialAdapter->hasCustomShaderSnippet(QSSGShaderCache::ShaderType::Fragment);
989 const bool usesSharedVar = materialAdapter->usesSharedVariables();
990
991 vertexShader.beginFragmentGeneration(shaderLibraryManager, passRequirmentState.oitMethod);
992
993 if (passRequirmentState.passType == PassRequirmentsState::OrthoShadow) {
994 vertexShader.generateDepth();
995 fragmentShader.addUniform("qt_shadowDepthAdjust", "vec2");
996 }
997
998
999 if (passRequirmentState.passType == PassRequirmentsState::PerspectiveShadow)
1000 vertexShader.generateShadowWorldPosition(inKey);
1001
1002
1003 if (hasCustomFrag && materialAdapter->isUnshaded()) {
1004 // Unlike the depth texture pass, the normal texture pass needs to
1005 // output valid fragment values. The custom main is skipped in
1006 // beginVertexGeneration if isNormalPass is true, but something must be
1007 // written to gl_FragColor (the normals), so pretend we are shaded...
1008 // if (passRequirmentState.passType != PassRequirmentsState::Normal)
1009 return;
1010 }
1011
1012 // hasCustomFrag == Shaded custom material from this point on, for Unshaded we returned above
1013
1014 // The fragment or vertex shaders may not use the material_properties or diffuse
1015 // uniforms in all cases but it is simpler to just add them and let the linker strip them.
1016 // if (passRequirmentState.needsEmission)
1017 fragmentShader.addUniform("qt_material_emissive_color", "vec3");
1018 // if (passRequirmentState.needsBaseColor)
1019 fragmentShader.addUniform("qt_material_base_color", "vec4");
1020 fragmentShader.addUniform("qt_material_properties", "vec4");
1021 fragmentShader.addUniform("qt_material_properties2", "vec4");
1022 fragmentShader.addUniform("qt_material_properties3", "vec4");
1023 if (passRequirmentState.hasParallaxMapping || passRequirmentState.hasTransmission)
1024 fragmentShader.addUniform("qt_material_properties4", "vec4");
1025 if (!hasCustomFrag) {
1026 if (passRequirmentState.hasTransmission) {
1027 fragmentShader.addUniform("qt_material_attenuation", "vec4");
1028 fragmentShader.addUniform("qt_material_thickness", "float");
1029 if (passRequirmentState.hasDispersion)
1030 fragmentShader.addUniform("qt_material_dispersion", "float");
1031 }
1032 if (passRequirmentState.hasFresnelScaleBias || passRequirmentState.hasClearcoatFresnelScaleBias)
1033 fragmentShader.addUniform("qt_material_properties5", "vec4");
1034 fragmentShader.addUniform("qt_material_clearcoat_normal_strength", "float");
1035 fragmentShader.addUniform("qt_material_clearcoat_fresnel_power", "float");
1036 if (passRequirmentState.hasSheen) {
1037 fragmentShader.addUniform("qt_material_sheen_color", "vec3");
1038 fragmentShader.addUniform("qt_material_sheen_roughness", "float");
1039 }
1040 if (passRequirmentState.hasAnisotropy) {
1041 // (strength, cos(rotation), sin(rotation))
1042 fragmentShader.addUniform("qt_material_anisotropy", "vec3");
1043 }
1044 if (passRequirmentState.hasIridescence) {
1045 // (factor, film ior, thickness minimum, thickness maximum)
1046 fragmentShader.addUniform("qt_material_iridescence", "vec4");
1047 }
1048 }
1049
1050 if (passRequirmentState.hasVertexColors) {
1051 vertexShader.generateVertexColor(inKey);
1052 }else {
1053 fragmentShader.append(" vec4 qt_vertColorMask = vec4(1.0);");
1054 fragmentShader.append(" vec4 qt_vertColor = vec4(1.0);");
1055 }
1056
1057 if (passRequirmentState.needsWorldNormal || passRequirmentState.needsWorldTangent || passRequirmentState.needsWorldBinormal || hasCustomFrag) {
1058 vertexShader.generateViewVector(inKey);
1059 if (keyProps.m_usesProjectionMatrix.getValue(inKey)) {
1060 if (viewCount >= 2)
1061 fragmentShader.addUniformArray("qt_projectionMatrix", "mat4", viewCount);
1062 else
1063 fragmentShader.addUniform("qt_projectionMatrix", "mat4");
1064 }
1065 if (keyProps.m_usesInverseProjectionMatrix.getValue(inKey)) {
1066 if (viewCount >= 2)
1067 fragmentShader.addUniformArray("qt_inverseProjectionMatrix", "mat4", viewCount);
1068 else
1069 fragmentShader.addUniform("qt_inverseProjectionMatrix", "mat4");
1070 }
1071 vertexShader.generateWorldNormal(inKey);
1072 vertexShader.generateWorldPosition(inKey);
1073
1074 if (passRequirmentState.needsWorldTangent || passRequirmentState.needsWorldBinormal || hasCustomFrag) {
1075 bool genTangent = false;
1076 bool genBinormal = false;
1077 vertexShader.generateVarTangentAndBinormal(inKey, genTangent, genBinormal);
1078
1079 if (!genTangent) {
1080 QSSGRenderableImage::Type id = QSSGRenderableImage::Type::Unknown;
1081 if (passRequirmentState.hasBumpNormalMap) {
1082 // Generate imageCoords for bump/normal map first.
1083 // Some operations needs to use the TBN transform and if the
1084 // tangent vector is not provided, it is necessary.
1085 id = samplerState.hasImage(QSSGRenderableImage::Type::Bump) ? QSSGRenderableImage::Type::Bump : QSSGRenderableImage::Type::Normal;
1086 } else if (samplerState.hasImage(QSSGRenderableImage::Type::ClearcoatNormal)) {
1087 // For the corner case that there is only a clearcoat normal map
1088 id = QSSGRenderableImage::Type::ClearcoatNormal;
1089 } else if (passRequirmentState.hasParallaxMapping) {
1090 // For the corner case that there is only a height map (parallax mapping),
1091 // use its UV coordinates to derive tangents analytically.
1092 id = QSSGRenderableImage::Type::Height;
1093 } else if (samplerState.hasImage(QSSGRenderableImage::Type::Anisotropy)) {
1094 id = QSSGRenderableImage::Type::Anisotropy;
1095 }
1096
1097 if (id > QSSGRenderableImage::Type::Unknown) {
1098 samplerState.generateImageUVAndSampler(id, vertexShader, fragmentShader, inKey, true);
1099 fragmentShader << " vec2 dUVdx = dFdx(" << samplerState.fragCoordsName(id) << ");\n"
1100 << " vec2 dUVdy = dFdy(" << samplerState.fragCoordsName(id) << ");\n";
1101 fragmentShader << " qt_tangent = (dUVdy.y * dFdx(qt_varWorldPos) - dUVdx.y * dFdy(qt_varWorldPos)) / (dUVdx.x * dUVdy.y - dUVdx.y * dUVdy.x);\n"
1102 << " qt_tangent = qt_tangent - dot(qt_world_normal, qt_tangent) * qt_world_normal;\n"
1103 << " qt_tangent = normalize(qt_tangent);\n";
1104 } else if (passRequirmentState.hasAnisotropy) {
1105 // Avoids NaN on meshes without tangents or a normal map
1106 fragmentShader.addInclude("anisotropy.glsllib");
1107 fragmentShader << " qt_tangent = qt_orthonormalTangent(qt_world_normal);\n";
1108 }
1109 }
1110 if (!genBinormal)
1111 fragmentShader << " qt_binormal = cross(qt_world_normal, qt_tangent);\n";
1112 }
1113
1114 if (passRequirmentState.isDoubleSided) {
1115 fragmentShader.append("#if QSHADER_HLSL && QSHADER_VIEW_COUNT >= 2");
1116 fragmentShader.append(" const float qt_facing = 1.0;");
1117 fragmentShader.append("#else");
1118 fragmentShader.append(" const float qt_facing = gl_FrontFacing ? 1.0 : -1.0;");
1119 fragmentShader.append("#endif");
1120 fragmentShader.append(" qt_world_normal *= qt_facing;\n");
1121 if (passRequirmentState.needsWorldTangent || passRequirmentState.needsWorldBinormal || hasCustomFrag) {
1122 fragmentShader.append(" qt_tangent *= qt_facing;");
1123 fragmentShader.append(" qt_binormal *= qt_facing;");
1124 }
1125 }
1126 }
1127
1128 if (hasCustomFrag) {
1129 // A custom shaded material is effectively a principled material for
1130 // our purposes here. The defaults are different from a
1131 // PrincipledMaterial however, since this is more sensible here.
1132 // (because the shader has to state it to get things)
1133 // These should match the defaults of PrincipledMaterial.
1134 fragmentShader << " float qt_customOcclusionAmount = 1.0;\n";
1135 fragmentShader << " float qt_customIOR = 1.5;\n";
1136 fragmentShader << " float qt_customSpecularAmount = 0.5;\n"; // overrides qt_material_properties.x
1137 fragmentShader << " float qt_customSpecularRoughness = 0.0;\n"; // overrides qt_material_properties.y
1138 fragmentShader << " float qt_customMetalnessAmount = 0.0;\n"; // overrides qt_material_properties.z
1139 fragmentShader << " float qt_customFresnelPower = 5.0;\n"; // overrides qt_material_properties2.x
1140 fragmentShader << " vec4 qt_customBaseColor = vec4(1.0);\n"; // overrides qt_material_base_color
1141 fragmentShader << " vec3 qt_customEmissiveColor = vec3(0.0);\n"; // overrides qt_material_emissive_color
1142 if (passRequirmentState.hasClearcoat) {
1143 fragmentShader << " float qt_customClearcoatAmount = 0.0;\n";
1144 fragmentShader << " float qt_customClearcoatFresnelPower = 5.0;\n";
1145 fragmentShader << " float qt_customClearcoatRoughness = 0.0;\n";
1146 fragmentShader << " vec3 qt_customClearcoatNormal = qt_world_normal;\n";
1147 if (passRequirmentState.hasClearcoatFresnelScaleBias) {
1148 fragmentShader << " float qt_customClearcoatFresnelScale = 1.0;\n";
1149 fragmentShader << " float qt_customClearcoatFresnelBias = 0.0;\n";
1150 }
1151 }
1152 if (passRequirmentState.hasSheen) {
1153 fragmentShader << " vec3 qt_customSheenColor = vec3(0.0);\n";
1154 fragmentShader << " float qt_customSheenRoughness = 0.0;\n";
1155 }
1156 if (passRequirmentState.hasAnisotropy) {
1157 fragmentShader << " float qt_customAnisotropyStrength = 0.0;\n";
1158 fragmentShader << " float qt_customAnisotropyRotation = 0.0;\n";
1159 }
1160 if (passRequirmentState.hasIridescence) {
1161 fragmentShader << " float qt_customIridescenceFactor = 0.0;\n";
1162 fragmentShader << " float qt_customIridescenceIor = 1.3;\n";
1163 fragmentShader << " float qt_customIridescenceThickness = 400.0;\n";
1164 }
1165 if (passRequirmentState.hasFresnelScaleBias) {
1166 fragmentShader << " float qt_customFresnelScale = 1.0;\n";
1167 fragmentShader << " float qt_customFresnelBias = 0.0;\n";
1168 }
1169
1170 if (passRequirmentState.hasDispersionKeyword)
1171 fragmentShader << " float qt_customDispersion = 0.0;\n";
1172
1173 if (passRequirmentState.hasTransmission) {
1174 fragmentShader << " float qt_customTransmissionFactor = 0.0;\n";
1175 fragmentShader << " float qt_customThicknessFactor = 0.0;\n";
1176 fragmentShader << " vec3 qt_customAttenuationColor = vec3(1.0);\n";
1177 fragmentShader << " float qt_customAttenuationDistance = 0.0;\n";
1178 }
1179 if (usesSharedVar)
1180 fragmentShader << " QT_SHARED_VARS qt_customShared;\n";
1181 // Generate the varyings for UV0 and UV1 since customer materials don't use image
1182 // properties directly.
1183 vertexShader.generateUVCoords(0, inKey);
1184 vertexShader.generateUVCoords(1, inKey);
1185 if (passRequirmentState.shouldIncludeCustomFragmentMain() && hasCustomFunction(QByteArrayLiteral("qt_customMain"))) {
1186 fragmentShader << " qt_customMain(qt_customBaseColor,\n"
1187 << " qt_customEmissiveColor,\n"
1188 << " qt_customMetalnessAmount,\n"
1189 << " qt_customSpecularRoughness,\n"
1190 << " qt_customSpecularAmount,\n"
1191 << " qt_customFresnelPower,\n"
1192 << " qt_world_normal,\n"
1193 << " qt_tangent,\n"
1194 << " qt_binormal,\n"
1195 << " qt_texCoord0,\n"
1196 << " qt_texCoord1,\n"
1197 << " qt_view_vector,\n"
1198 << " qt_customIOR,\n"
1199 << " qt_customOcclusionAmount";
1200 if (passRequirmentState.hasClearcoat) {
1201 fragmentShader << ",\n qt_customClearcoatAmount,\n"
1202 << " qt_customClearcoatFresnelPower,\n"
1203 << " qt_customClearcoatRoughness,\n"
1204 << " qt_customClearcoatNormal";
1205 if (passRequirmentState.hasClearcoatFresnelScaleBias) {
1206 fragmentShader << ",\n qt_customClearcoatFresnelScale,\n"
1207 << " qt_customClearcoatFresnelBias";
1208 }
1209 }
1210 if (passRequirmentState.hasSheen) {
1211 fragmentShader << ",\n qt_customSheenColor,\n"
1212 << " qt_customSheenRoughness";
1213 }
1214 if (passRequirmentState.hasAnisotropy) {
1215 fragmentShader << ",\n qt_customAnisotropyStrength,\n"
1216 << " qt_customAnisotropyRotation";
1217 }
1218 if (passRequirmentState.hasIridescence) {
1219 fragmentShader << ",\n qt_customIridescenceFactor,\n"
1220 << " qt_customIridescenceIor,\n"
1221 << " qt_customIridescenceThickness";
1222 }
1223 if (passRequirmentState.hasFresnelScaleBias) {
1224 fragmentShader << ",\n qt_customFresnelScale,\n"
1225 << " qt_customFresnelBias";
1226 }
1227 if (passRequirmentState.hasDispersionKeyword)
1228 fragmentShader << ",\n qt_customDispersion";
1229 if (passRequirmentState.hasTransmission) {
1230 fragmentShader << ",\n qt_customTransmissionFactor,\n"
1231 << " qt_customThicknessFactor,\n"
1232 << " qt_customAttenuationColor,\n"
1233 << " qt_customAttenuationDistance";
1234 }
1235 if (usesSharedVar)
1236 fragmentShader << "\n, qt_customShared);\n";
1237 else
1238 fragmentShader << ");\n";
1239 }
1240 fragmentShader << " vec4 qt_diffuseColor = qt_customBaseColor * qt_vertColor;\n";
1241 fragmentShader << " vec3 qt_global_emission = qt_customEmissiveColor;\n";
1242 fragmentShader << " float qt_iOR = qt_customIOR;\n";
1243 } else {
1244 fragmentShader << " vec4 qt_diffuseColor = qt_material_base_color * qt_vertColor;\n";
1245 fragmentShader << " vec3 qt_global_emission = qt_material_emissive_color;\n";
1246 if (passRequirmentState.hasSpecularLight || samplerState.isActive())
1247 fragmentShader << " float qt_iOR = qt_material_specular.w;\n";
1248 }
1249
1250 const bool hasCustomIblProbe = hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_iblProbeProcessor"));
1251
1252
1253 // Lightmaps
1254 // hasLighting && needsDiffuseLight || needsSpecularLight
1255 if (passRequirmentState.hasLighting && (passRequirmentState.needsDiffuseLight || passRequirmentState.needsSpecularLight)) {
1256 if (passRequirmentState.hasLightMap) {
1257 vertexShader.generateLightmapUVCoords(inKey);
1258 fragmentShader.addFunction("lightmap");
1259 }
1260 }
1261
1262 // Heightmap / Parallax Mapping
1263 // Possible Optimization: Also conditionally, only when we plan on using another map/texture since it only gets used when sampling
1264 if (passRequirmentState.hasParallaxMapping) {
1265 // Adjust UV coordinates to account for parallaxMapping before
1266 // reading any other texture.
1267 fragmentShader.addInclude("parallaxMapping.glsllib");
1268 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Height, vertexShader, fragmentShader, inKey, true);
1269 fragmentShader << " float qt_heightAmount = qt_material_properties4.x;\n";
1270 maskVariableByVertexColorChannel( "qt_heightAmount", QSSGRenderDefaultMaterial::HeightAmountMask );
1271 fragmentShader << " qt_texCoord0 = qt_parallaxMapping(" << samplerState.fragCoordsName(QSSGRenderableImage::Type::Height) << ",\n"
1272 << " " << samplerState.samplerName(QSSGRenderableImage::Type::Height) << ",\n"
1273 << " qt_tangent,\n"
1274 << " qt_binormal,\n"
1275 << " qt_world_normal,\n"
1276 << " qt_varWorldPos, \n"
1277 << "#if QSHADER_VIEW_COUNT >= 2\n"
1278 << " qt_cameraPosition[qt_viewIndex],\n"
1279 << "#else\n"
1280 << " qt_cameraPosition,\n"
1281 << "#endif\n"
1282 << " qt_heightAmount,\n"
1283 << " qt_material_properties4.y,\n"
1284 << " qt_material_properties4.z);\n";
1285 }
1286
1287 // Clearcoat (before normal/bump has a chance to overwrite qt_world_normal)
1288 if (passRequirmentState.hasClearcoat && (passRequirmentState.needsDiffuseLight || passRequirmentState.needsSpecularLight)) {
1289 addLocalVariable(fragmentShader, "qt_clearcoatNormal", "vec3");
1290 // Clearcoat normal should be calculated not considering the normalImage for the base material
1291 // If both are to be the same then just set the same normalImage for the base and clearcoat
1292 // This does mean that this value should be calculated before qt_world_normal is overwritten by
1293 // the normalMap.
1294 if (hasCustomFrag) {
1295 fragmentShader << " qt_clearcoatNormal = qt_customClearcoatNormal;\n";
1296 } else {
1297 if (samplerState.hasImage(QSSGRenderableImage::Type::ClearcoatNormal)) {
1298 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::ClearcoatNormal, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1299 fragmentShader.addFunction("sampleNormalTexture");
1300 fragmentShader << " float qt_clearcoat_normal_strength = qt_material_clearcoat_normal_strength;\n";
1301 maskVariableByVertexColorChannel( "qt_clearcoat_normal_strength", QSSGRenderDefaultMaterial::ClearcoatNormalStrengthMask );
1302 fragmentShader << " qt_clearcoatNormal = qt_sampleNormalTexture(" << samplerState.samplerName(QSSGRenderableImage::Type::ClearcoatNormal)
1303 << ", qt_clearcoat_normal_strength, "
1304 << samplerState.fragCoordsName(QSSGRenderableImage::Type::ClearcoatNormal)
1305 << ", qt_tangent, qt_binormal, qt_world_normal);\n";
1306
1307 } else {
1308 // same as qt_world_normal then
1309 fragmentShader << " qt_clearcoatNormal = qt_world_normal;\n";
1310 }
1311 }
1312 }
1313
1314 // Normal / Bump Map
1315 if (passRequirmentState.needsWorldNormal) {
1316 if (samplerState.hasImage(QSSGRenderableImage::Type::Bump)) {
1317 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Bump, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1318 fragmentShader.append(" float qt_bumpAmount = qt_material_properties2.y;\n");
1319 maskVariableByVertexColorChannel( "qt_bumpAmount", QSSGRenderDefaultMaterial::NormalStrengthMask );
1320 fragmentShader.addInclude("defaultMaterialBumpNoLod.glsllib");
1321 fragmentShader << " qt_world_normal = qt_defaultMaterialBumpNoLod("
1322 << samplerState.samplerName(QSSGRenderableImage::Type::Bump)
1323 << ", qt_bumpAmount, " << samplerState.fragCoordsName(QSSGRenderableImage::Type::Bump)
1324 << ", qt_tangent, qt_binormal, qt_world_normal);\n";
1325 } else if (samplerState.hasImage(QSSGRenderableImage::Type::Normal)) {
1326 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Normal, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1327 fragmentShader.append(" float qt_normalStrength = qt_material_properties2.y;\n");
1328 maskVariableByVertexColorChannel( "qt_normalStrength", QSSGRenderDefaultMaterial::NormalStrengthMask );
1329 fragmentShader.addFunction("sampleNormalTexture");
1330 fragmentShader << " qt_world_normal = qt_sampleNormalTexture(" << samplerState.samplerName(QSSGRenderableImage::Type::Normal)
1331 << ", qt_normalStrength, " << samplerState.fragCoordsName(QSSGRenderableImage::Type::Normal)
1332 << ", qt_tangent, qt_binormal, qt_world_normal);\n";
1333 }
1334 }
1335
1336 // !hasLighting does not mean 'no light source'
1337 // it should be KHR_materials_unlit
1338 // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
1339 if ((passRequirmentState.hasLighting || passRequirmentState.passType == PassRequirmentsState::Normal) && passRequirmentState.needsWorldNormal) {
1340 fragmentShader.append(" vec3 tmp_light_color;");
1341 }
1342
1343 if (passRequirmentState.hasSpecularLight || samplerState.isActive()) {
1344 fragmentShader.append(" vec3 qt_specularBase;");
1345 fragmentShader.addUniform("qt_material_specular", "vec4");
1346 if (hasCustomFrag)
1347 fragmentShader.append(" vec3 qt_specularTint = vec3(1.0);");
1348 else
1349 fragmentShader.append(" vec3 qt_specularTint = qt_material_specular.rgb;");
1350 }
1351
1352 // Base Color / Diffuse / Albedo
1353 if ((samplerState.hasImage(QSSGRenderableImage::Type::BaseColor) || samplerState.hasImage(QSSGRenderableImage::Type::Diffuse)) && passRequirmentState.needsBaseColor) {
1354
1355 // first off, which one
1356 QSSGRenderableImage::Type baseImageType = QSSGRenderableImage::Type::Unknown;
1357 if (samplerState.hasImage(QSSGRenderableImage::Type::BaseColor))
1358 baseImageType = QSSGRenderableImage::Type::BaseColor;
1359 else if (samplerState.hasImage(QSSGRenderableImage::Type::Diffuse))
1360 baseImageType = QSSGRenderableImage::Type::Diffuse;
1361
1362 // Generate the UVs and sampler snippets
1363 samplerState.generateImageUVAndSampler(baseImageType, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1364
1365 if (keyProps.m_baseColorSingleChannelEnabled.getValue(inKey)) {
1366 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::BaseColorChannel];
1367 fragmentShader << " vec4 qt_base_texture_color = vec4(vec3(texture2D(" << samplerState.samplerName(baseImageType)
1368 << ", " << samplerState.fragCoordsName(baseImageType) << ")" << channelStr(channelProps, inKey) << "), 1.0f);\n";
1369 } else {
1370 fragmentShader << " vec4 qt_base_texture_color = texture2D(" << samplerState.samplerName(baseImageType)
1371 << ", " << samplerState.fragCoordsName(baseImageType) << ");\n";
1372 }
1373
1374 if (keyProps.m_imageMaps[QSSGShaderDefaultMaterialKeyProperties::BaseColorMap].isPreMultipliedAlpha(inKey))
1375 fragmentShader << " qt_base_texture_color.rgb /= qt_base_texture_color.a;\n";
1376
1377 if (!keyProps.m_imageMaps[QSSGShaderDefaultMaterialKeyProperties::BaseColorMap].isLinear(inKey)) {
1378 // Diffuse/BaseColor maps need to converted to linear color space
1379 fragmentShader.addInclude("tonemapping.glsllib");
1380 fragmentShader << " qt_base_texture_color = qt_sRGBToLinear(qt_base_texture_color);\n";
1381 }
1382
1383 fragmentShader << " qt_diffuseColor *= qt_base_texture_color;\n";
1384 }
1385
1386 // Alpha cutoff
1387 if (keyProps.m_alphaMode.getAlphaMode(inKey) == QSSGRenderDefaultMaterial::MaterialAlphaMode::Mask) {
1388 // The Implementation Notes from
1389 // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#alpha-coverage
1390 // must be met. Hence the discard.
1391 fragmentShader << " if (qt_diffuseColor.a < qt_material_properties3.y) {\n"
1392 << " qt_diffuseColor = vec4(0.0);\n"
1393 << " discard;\n"
1394 << " } else {\n"
1395 << " qt_diffuseColor.a = 1.0;\n"
1396 << " }\n";
1397 } else if (keyProps.m_alphaMode.getAlphaMode(inKey) == QSSGRenderDefaultMaterial::MaterialAlphaMode::Opaque) {
1398 fragmentShader << " qt_diffuseColor.a = 1.0;\n";
1399 }
1400
1401 // Opacity
1402 if (samplerState.hasImage(QSSGRenderableImage::Type::Opacity)) {
1403 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Opacity, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1404 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::OpacityChannel];
1405 fragmentShader << " float qt_opacity_map_value = texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Opacity)
1406 << ", " << samplerState.fragCoordsName(QSSGRenderableImage::Type::Opacity) << ")" << channelStr(channelProps, inKey) << ";\n";
1407 if (keyProps.m_invertOpacityMapValue.getValue(inKey))
1408 fragmentShader << " qt_opacity_map_value = 1.0 - qt_opacity_map_value;\n";
1409 fragmentShader << " qt_objectOpacity *= qt_opacity_map_value;\n";
1410 }
1411
1412 // Ambient Occlusion
1413 if (passRequirmentState.needsAmbientOcclusion) {
1414 addLocalVariable(fragmentShader, "qt_aoFactor", "float");
1415
1416 if (passRequirmentState.hasSSAOMap) {
1417 fragmentShader.addInclude("ssao.glsllib");
1418 fragmentShader.append(" qt_aoFactor = qt_screenSpaceAmbientOcclusionFactor();");
1419 } else {
1420 fragmentShader.append(" qt_aoFactor = 1.0;");
1421 }
1422
1423 if (hasCustomFrag)
1424 fragmentShader << " qt_aoFactor *= qt_customOcclusionAmount;\n";
1425 }
1426
1427 // Roughness
1428 if (passRequirmentState.needsRoughness) {
1429 if (hasCustomFrag)
1430 fragmentShader << " float qt_roughnessAmount = qt_customSpecularRoughness;\n";
1431 else
1432 fragmentShader << " float qt_roughnessAmount = qt_material_properties.y;\n";
1433
1434 maskVariableByVertexColorChannel( "qt_roughnessAmount", QSSGRenderDefaultMaterial::RoughnessMask );
1435
1436 if (samplerState.hasImage(QSSGRenderableImage::Type::Roughness)) {
1437 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Roughness, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1438 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::RoughnessChannel];
1439 fragmentShader << " qt_roughnessAmount *= texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Roughness) << ", "
1440 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Roughness) << ")" << channelStr(channelProps, inKey) << ";\n";
1441 }
1442
1443 // Convert Glossy to Roughness
1444 if (passRequirmentState.isSpecularGlossinessWorkflow)
1445 fragmentShader << " qt_roughnessAmount = clamp(1.0 - qt_roughnessAmount, 0.0, 1.0);\n";
1446 }
1447
1448 // Metalness
1449 if (passRequirmentState.needsMetalness) {
1450 if (hasCustomFrag)
1451 fragmentShader << " float qt_metalnessAmount = qt_customMetalnessAmount;\n";
1452 else if (!passRequirmentState.isSpecularGlossinessWorkflow)
1453 fragmentShader << " float qt_metalnessAmount = qt_material_properties.z;\n";
1454 else
1455 fragmentShader << " float qt_metalnessAmount = 0.0;\n";
1456
1457 maskVariableByVertexColorChannel( "qt_metalnessAmount", QSSGRenderDefaultMaterial::MetalnessMask );
1458
1459 if (passRequirmentState.hasSpecularLight && samplerState.hasImage(QSSGRenderableImage::Type::Metalness)) {
1460 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::MetalnessChannel];
1461 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Metalness, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1462 fragmentShader << " float qt_sampledMetalness = texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Metalness) << ", "
1463 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Metalness) << ")" << channelStr(channelProps, inKey) << ";\n";
1464 fragmentShader << " qt_metalnessAmount = clamp(qt_metalnessAmount * qt_sampledMetalness, 0.0, 1.0);\n";
1465 }
1466 }
1467
1468 // Special case for depth pre-pass
1469 if (passRequirmentState.shouldDiscardNonOpaque()) {
1470 // Epsilon guards against rasterizer precision causing opaque fragments to be discarded (QTBUG-140392).
1471 fragmentShader << " if ((qt_diffuseColor.a * qt_objectOpacity) < (1.0 - 1e-6))\n";
1472 fragmentShader << " discard;\n";
1473 }
1474
1475 // This is a Lighting Pass
1476 if (passRequirmentState.hasLighting && (passRequirmentState.needsDiffuseLight || passRequirmentState.needsSpecularLight)) {
1477 if (passRequirmentState.hasSpecularLight) {
1478 vertexShader.generateViewVector(inKey);
1479 fragmentShader.addUniform("qt_material_properties", "vec4");
1480
1481 if (passRequirmentState.isPbrMaterial)
1482 fragmentShader << " qt_specularBase = vec3(1.0);\n";
1483 else
1484 fragmentShader << " qt_specularBase = qt_diffuseColor.rgb;\n";
1485 if (hasCustomFrag)
1486 fragmentShader << " float qt_specularFactor = qt_customSpecularAmount;\n";
1487 else
1488 fragmentShader << " float qt_specularFactor = qt_material_properties.x;\n";
1489
1490 maskVariableByVertexColorChannel( "qt_specularFactor", QSSGRenderDefaultMaterial::SpecularAmountMask );
1491 }
1492
1493 fragmentShader.addUniform("qt_light_ambient_total", "vec3");
1494
1495 fragmentShader.append(" vec4 global_diffuse_light = vec4(0.0);");
1496
1497 if (passRequirmentState.hasLightMap) {
1498 fragmentShader << " global_diffuse_light.rgb = qt_lightmap_color(qt_texCoordLightmap) * (1.0 - qt_metalnessAmount) * qt_diffuseColor.rgb;\n";
1499 } else {
1500 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_ambientLightProcessor"))) {
1501 // DIFFUSE, TOTAL_AMBIENT_COLOR, NORMAL, VIEW_VECTOR(, SHARED)
1502 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");
1503 if (usesSharedVar)
1504 fragmentShader << ", qt_customShared);\n";
1505 else
1506 fragmentShader << ");\n";
1507 } else {
1508 fragmentShader.append(" global_diffuse_light = vec4(qt_light_ambient_total.rgb * (1.0 - qt_metalnessAmount) * qt_diffuseColor.rgb, 0.0);");
1509 }
1510 }
1511
1512 fragmentShader.append(" vec3 global_specular_light = vec3(0.0);");
1513
1514 // Fragment lighting means we can perhaps attenuate the specular amount by a texture
1515 // lookup.
1516 if (samplerState.hasImage(QSSGRenderableImage::Type::SpecularAmountMap)) {
1517 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::SpecularAmountMap, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1518
1519 if (keyProps.m_specularSingleChannelEnabled.getValue(inKey)) {
1520 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::SpecularAmountChannel];
1521 fragmentShader << " vec4 qt_specular_amount_map = vec4(vec3(texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::SpecularAmountMap)
1522 << ", " << samplerState.fragCoordsName(QSSGRenderableImage::Type::SpecularAmountMap) << ")" << channelStr(channelProps, inKey) << "), 1.0f);\n";
1523 } else {
1524 fragmentShader << " vec4 qt_specular_amount_map = texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::SpecularAmountMap)
1525 << ", " << samplerState.fragCoordsName(QSSGRenderableImage::Type::SpecularAmountMap) << ");\n";
1526 }
1527 fragmentShader << " qt_specularBase *= qt_sRGBToLinear(qt_specular_amount_map).rgb;\n";
1528 }
1529
1530 if (passRequirmentState.hasSpecularLight) {
1531 if (passRequirmentState.isSpecularGlossinessWorkflow) {
1532 fragmentShader << " qt_specularTint *= qt_specularBase;\n";
1533 fragmentShader << " vec3 qt_specularAmount = vec3(1.0);\n";
1534 } else {
1535 fragmentShader << " vec3 qt_specularAmount = qt_specularBase * vec3(qt_metalnessAmount + qt_specularFactor * (1.0 - qt_metalnessAmount));\n";
1536 }
1537 }
1538
1539 if (samplerState.hasImage(QSSGRenderableImage::Type::Translucency)) {
1540 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Translucency, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1541 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::TranslucencyChannel];
1542 fragmentShader << " float qt_translucent_depth_range = texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Translucency)
1543 << ", " << samplerState.fragCoordsName(QSSGRenderableImage::Type::Translucency) << ")" << channelStr(channelProps, inKey) << ";\n";
1544 fragmentShader << " float qt_translucent_thickness = qt_translucent_depth_range * qt_translucent_depth_range;\n";
1545 fragmentShader << " float qt_translucent_thickness_exp = exp(qt_translucent_thickness * qt_material_properties2.z);\n";
1546 }
1547
1548 // Occlusion Map
1549 if (samplerState.hasImage(QSSGRenderableImage::Type::Occlusion)) {
1550 addLocalVariable(fragmentShader, "qt_ao", "float");
1551 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Occlusion, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1552 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::OcclusionChannel];
1553 fragmentShader << " qt_ao = texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Occlusion) << ", "
1554 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Occlusion) << ")" << channelStr(channelProps, inKey) << ";\n";
1555 fragmentShader << " qt_aoFactor *= qt_ao * qt_material_properties3.x;\n"; // qt_material_properties3.x is the OcclusionAmount
1556 maskVariableByVertexColorChannel( "qt_aoFactor", QSSGRenderDefaultMaterial::OcclusionAmountMask );
1557 }
1558
1559 if (passRequirmentState.hasSheen) {
1560 fragmentShader.addInclude("sheen.glsllib");
1561 addLocalVariable(fragmentShader, "qt_sheenColor", "vec3");
1562 addLocalVariable(fragmentShader, "qt_sheenRoughness", "float");
1563 addLocalVariable(fragmentShader, "qt_global_sheen", "vec3");
1564
1565 if (hasCustomFrag)
1566 fragmentShader << " qt_sheenColor = qt_customSheenColor;\n";
1567 else
1568 fragmentShader << " qt_sheenColor = qt_material_sheen_color;\n";
1569 if (hasCustomFrag)
1570 fragmentShader << " qt_sheenRoughness = qt_customSheenRoughness;\n";
1571 else
1572 fragmentShader << " qt_sheenRoughness = qt_material_sheen_roughness;\n";
1573 fragmentShader << " qt_global_sheen = vec3(0.0);\n";
1574
1575 if (samplerState.hasImage(QSSGRenderableImage::Type::SheenColor)) {
1576 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::SheenColor, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1577 fragmentShader << " vec3 qt_sheenColorSample = texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::SheenColor) << ", "
1578 << samplerState.fragCoordsName(QSSGRenderableImage::Type::SheenColor) << ").rgb;\n";
1579 if (!keyProps.m_imageMaps[QSSGShaderDefaultMaterialKeyProperties::SheenColorMap].isLinear(inKey)) {
1580 fragmentShader.addInclude("tonemapping.glsllib");
1581 fragmentShader << " qt_sheenColorSample = qt_sRGBToLinear(qt_sheenColorSample);\n";
1582 }
1583 fragmentShader << " qt_sheenColor *= qt_sheenColorSample;\n";
1584 }
1585
1586 if (samplerState.hasImage(QSSGRenderableImage::Type::SheenRoughness)) {
1587 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::SheenRoughness, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1588 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::SheenRoughnessChannel];
1589 fragmentShader << " qt_sheenRoughness *= texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::SheenRoughness) << ", "
1590 << samplerState.fragCoordsName(QSSGRenderableImage::Type::SheenRoughness) << ")" << channelStr(channelProps, inKey) << ";\n";
1591 fragmentShader << " qt_sheenRoughness = clamp(qt_sheenRoughness, 0.0, 1.0);\n";
1592 }
1593 }
1594
1595 if (passRequirmentState.hasClearcoat) {
1596 addLocalVariable(fragmentShader, "qt_clearcoatAmount", "float");
1597 addLocalVariable(fragmentShader, "qt_clearcoatRoughness", "float");
1598 addLocalVariable(fragmentShader, "qt_clearcoatF0", "vec3");
1599 addLocalVariable(fragmentShader, "qt_clearcoatF90", "vec3");
1600 addLocalVariable(fragmentShader, "qt_global_clearcoat", "vec3");
1601
1602 if (hasCustomFrag)
1603 fragmentShader << " qt_clearcoatAmount = qt_customClearcoatAmount;\n";
1604 else
1605 fragmentShader << " qt_clearcoatAmount = qt_material_properties3.z;\n";
1606 maskVariableByVertexColorChannel( "qt_clearcoatAmount", QSSGRenderDefaultMaterial::ClearcoatAmountMask );
1607 if (hasCustomFrag)
1608 fragmentShader << " qt_clearcoatRoughness = qt_customClearcoatRoughness;\n";
1609 else
1610 fragmentShader << " qt_clearcoatRoughness = qt_material_properties3.w;\n";
1611 maskVariableByVertexColorChannel( "qt_clearcoatRoughness", QSSGRenderDefaultMaterial::ClearcoatRoughnessAmountMask );
1612 fragmentShader << " qt_clearcoatF0 = vec3(((1.0-qt_iOR) * (1.0-qt_iOR)) / ((1.0+qt_iOR) * (1.0+qt_iOR)));\n";
1613 fragmentShader << " qt_clearcoatF90 = vec3(1.0);\n";
1614 fragmentShader << " qt_global_clearcoat = vec3(0.0);\n";
1615
1616 if (samplerState.hasImage(QSSGRenderableImage::Type::Clearcoat)) {
1617 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Clearcoat, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1618 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::ClearcoatChannel];
1619 fragmentShader << " qt_clearcoatAmount *= texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Clearcoat) << ", "
1620 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Clearcoat) << ")" << channelStr(channelProps, inKey) << ";\n";
1621 }
1622
1623 if (samplerState.hasImage(QSSGRenderableImage::Type::ClearcoatRoughness)) {
1624 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::ClearcoatRoughness, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1625 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::ClearcoatRoughnessChannel];
1626 fragmentShader << " qt_clearcoatRoughness *= texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::ClearcoatRoughness) << ", "
1627 << samplerState.fragCoordsName(QSSGRenderableImage::Type::ClearcoatRoughness) << ")" << channelStr(channelProps, inKey) << ";\n";
1628 fragmentShader << " qt_clearcoatRoughness = clamp(qt_clearcoatRoughness, 0.0, 1.0);\n";
1629 }
1630 }
1631
1632 if (passRequirmentState.hasTransmission) {
1633 fragmentShader.addInclude("transmission.glsllib");
1634 addLocalVariable(fragmentShader, "qt_transmissionFactor", "float");
1635 addLocalVariable(fragmentShader, "qt_global_transmission", "vec3");
1636 // Volume
1637 addLocalVariable(fragmentShader, "qt_thicknessFactor", "float");
1638 addLocalVariable(fragmentShader, "qt_attenuationColor", "vec3");
1639 addLocalVariable(fragmentShader, "qt_attenuationDistance", "float");
1640 fragmentShader << " qt_global_transmission = vec3(0.0);\n";
1641
1642 if (hasCustomFrag) {
1643 fragmentShader << " qt_transmissionFactor = qt_customTransmissionFactor;\n";
1644 fragmentShader << " qt_thicknessFactor = qt_customThicknessFactor;\n";
1645 fragmentShader << " qt_attenuationColor = qt_customAttenuationColor;\n";
1646 fragmentShader << " qt_attenuationDistance = qt_customAttenuationDistance;\n";
1647 } else {
1648 fragmentShader << " qt_transmissionFactor = qt_material_properties4.w;\n";
1649 maskVariableByVertexColorChannel( "qt_transmissionFactor", QSSGRenderDefaultMaterial::TransmissionFactorMask );
1650
1651 if (samplerState.hasImage(QSSGRenderableImage::Type::Transmission)) {
1652 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Transmission, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1653 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::TransmissionChannel];
1654 fragmentShader << " qt_transmissionFactor *= texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Transmission) << ", "
1655 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Transmission) << ")" << channelStr(channelProps, inKey) << ";\n";
1656 }
1657
1658 fragmentShader << " qt_thicknessFactor = qt_material_thickness;\n";
1659 maskVariableByVertexColorChannel( "qt_thicknessFactor", QSSGRenderDefaultMaterial::ThicknessFactorMask );
1660 fragmentShader << " qt_attenuationColor = qt_material_attenuation.xyz;\n";
1661 fragmentShader << " qt_attenuationDistance = qt_material_attenuation.w;\n";
1662
1663 if (samplerState.hasImage(QSSGRenderableImage::Type::Thickness)) {
1664 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Thickness, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1665 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::ThicknessChannel];
1666 fragmentShader << " qt_thicknessFactor *= texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Thickness) << ", "
1667 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Thickness) << ")" << channelStr(channelProps, inKey) << ";\n";
1668 }
1669 }
1670
1671 if (passRequirmentState.hasDispersion) {
1672 addLocalVariable(fragmentShader, "qt_dispersion", "float");
1673 if (hasCustomFrag)
1674 fragmentShader << " qt_dispersion = qt_customDispersion;\n";
1675 else
1676 fragmentShader << " qt_dispersion = qt_material_dispersion;\n";
1677 }
1678 }
1679 if (passRequirmentState.hasPunctualLights || passRequirmentState.hasSpecularLight) {
1680 fragmentShader << " vec3 qt_f0 = vec3(1.0);\n";
1681 fragmentShader << " vec3 qt_f90 = vec3(1.0);\n";
1682 }
1683
1684 if (passRequirmentState.hasSpecularLight) {
1685 fragmentShader.addInclude("principledMaterialFresnel.glsllib");
1686 if (!passRequirmentState.isSpecularGlossinessWorkflow) {
1687 fragmentShader << " qt_f0 = qt_F0_ior(qt_iOR, qt_metalnessAmount, qt_diffuseColor.rgb);\n";
1688 } else {
1689 fragmentShader << " const float qt_reflectance = max(max(qt_specularTint.r, qt_specularTint.g), qt_specularTint.b);\n";
1690 fragmentShader << " qt_f0 = qt_specularTint;\n";
1691 fragmentShader << " qt_specularTint = vec3(1.0);\n";
1692 fragmentShader << " qt_f90 = vec3(clamp(qt_reflectance * 50.0, 0.0, 1.0));\n";
1693 fragmentShader << " qt_diffuseColor.rgb *= (1 - qt_reflectance);\n";
1694 }
1695
1696 // Replacing qt_f0 reaches every specular path, at the cost of a second Schlick ramp on top
1697 if (passRequirmentState.hasIridescence) {
1698 fragmentShader.addDefinition("QSSG_ENABLE_IRIDESCENCE", "1");
1699 fragmentShader.addInclude("iridescence.glsllib");
1700 fragmentShader << " float qt_iridescenceFactor = ";
1701 if (hasCustomFrag)
1702 fragmentShader << "qt_customIridescenceFactor;\n";
1703 else
1704 fragmentShader << "qt_material_iridescence.x;\n";
1705 fragmentShader << " float qt_iridescenceIor = ";
1706 if (hasCustomFrag)
1707 fragmentShader << "qt_customIridescenceIor;\n";
1708 else
1709 fragmentShader << "qt_material_iridescence.y;\n";
1710
1711 // Custom materials give a final thickness, with no uniform for the range
1712 const char *thicknessMinimum = hasCustomFrag ? "qt_customIridescenceThickness"
1713 : "qt_material_iridescence.z";
1714 const char *thicknessMaximum = hasCustomFrag ? "qt_customIridescenceThickness"
1715 : "qt_material_iridescence.w";
1716 // With no thickness map the film is uniformly at its maximum
1717 fragmentShader << " float qt_iridescenceThickness = " << thicknessMaximum << ";\n";
1718
1719 if (samplerState.hasImage(QSSGRenderableImage::Type::Iridescence)) {
1720 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Iridescence, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1721 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::IridescenceChannel];
1722 fragmentShader << " qt_iridescenceFactor *= texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Iridescence) << ", "
1723 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Iridescence) << ")" << channelStr(channelProps, inKey) << ";\n";
1724 }
1725
1726 if (samplerState.hasImage(QSSGRenderableImage::Type::IridescenceThickness)) {
1727 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::IridescenceThickness, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1728 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::IridescenceThicknessChannel];
1729 fragmentShader << " qt_iridescenceThickness = mix(" << thicknessMinimum << ", " << thicknessMaximum
1730 << ", texture2D("
1731 << samplerState.samplerName(QSSGRenderableImage::Type::IridescenceThickness) << ", "
1732 << samplerState.fragCoordsName(QSSGRenderableImage::Type::IridescenceThickness) << ")" << channelStr(channelProps, inKey) << ");\n";
1733 }
1734
1735 fragmentShader << " float qt_iridescenceNdotV = clamp(dot(qt_world_normal, qt_view_vector), 0.0, 1.0);\n";
1736 fragmentShader << " qt_f0 = mix(qt_f0, qt_evalIridescence(1.0, qt_iridescenceIor, qt_iridescenceNdotV, "
1737 << "qt_iridescenceThickness, qt_f0), qt_iridescenceFactor);\n";
1738 }
1739
1740 if (passRequirmentState.isSpecularAAEnabled) {
1741 fragmentShader.append(" vec3 vNormalWsDdx = dFdx(qt_world_normal.xyz);\n");
1742 fragmentShader.append(" vec3 vNormalWsDdy = dFdy(qt_world_normal.xyz);\n");
1743 fragmentShader.append(" float flGeometricRoughnessFactor = pow(clamp(max(dot(vNormalWsDdx, vNormalWsDdx), dot(vNormalWsDdy, vNormalWsDdy)), 0.0, 1.0), 0.333);\n");
1744 fragmentShader.append(" qt_roughnessAmount = max(flGeometricRoughnessFactor, qt_roughnessAmount);\n");
1745 }
1746
1747 // Must follow any adjustment of qt_roughnessAmount
1748 if (passRequirmentState.hasAnisotropy) {
1749 fragmentShader.addDefinition("QSSG_ENABLE_ANISOTROPY", "1");
1750 fragmentShader.addInclude("anisotropy.glsllib");
1751 addLocalVariable(fragmentShader, "qt_anisotropyStrength", "float");
1752 addLocalVariable(fragmentShader, "qt_anisotropicT", "vec3");
1753 addLocalVariable(fragmentShader, "qt_anisotropicB", "vec3");
1754 addLocalVariable(fragmentShader, "qt_anisotropyAlphaT", "float");
1755 addLocalVariable(fragmentShader, "qt_anisotropyAlphaB", "float");
1756
1757 fragmentShader << " vec2 qt_anisotropyDirection = vec2(1.0, 0.0);\n";
1758 if (hasCustomFrag) {
1759 fragmentShader << " qt_anisotropyStrength = qt_customAnisotropyStrength;\n";
1760 fragmentShader << " float qt_anisotropyCos = cos(qt_customAnisotropyRotation);\n";
1761 fragmentShader << " float qt_anisotropySin = sin(qt_customAnisotropyRotation);\n";
1762 } else {
1763 fragmentShader << " qt_anisotropyStrength = qt_material_anisotropy.x;\n";
1764 fragmentShader << " float qt_anisotropyCos = qt_material_anisotropy.y;\n";
1765 fragmentShader << " float qt_anisotropySin = qt_material_anisotropy.z;\n";
1766 }
1767
1768 if (samplerState.hasImage(QSSGRenderableImage::Type::Anisotropy)) {
1769 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Anisotropy, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1770 fragmentShader << " vec3 qt_anisotropySample = texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Anisotropy) << ", "
1771 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Anisotropy) << ").rgb;\n";
1772 fragmentShader << " qt_anisotropyDirection = qt_anisotropySample.rg * 2.0 - vec2(1.0);\n";
1773 fragmentShader << " qt_anisotropyStrength *= qt_anisotropySample.b;\n";
1774 }
1775
1776 fragmentShader << " qt_anisotropyDirection = vec2(qt_anisotropyCos * qt_anisotropyDirection.x - qt_anisotropySin * qt_anisotropyDirection.y,\n"
1777 << " qt_anisotropySin * qt_anisotropyDirection.x + qt_anisotropyCos * qt_anisotropyDirection.y);\n";
1778 fragmentShader << " qt_anisotropicT = qt_anisotropyDirection.x * qt_tangent + qt_anisotropyDirection.y * qt_binormal;\n";
1779 // Normal mapping may have moved the shading normal off the tangent frame
1780 fragmentShader << " qt_anisotropicT -= dot(qt_world_normal, qt_anisotropicT) * qt_world_normal;\n";
1781 // A zero direction, e.g. from a map texel of exactly 0.5, would give NaN
1782 fragmentShader << " qt_anisotropicT = dot(qt_anisotropicT, qt_anisotropicT) > 1e-8 ? normalize(qt_anisotropicT)\n"
1783 << " : qt_orthonormalTangent(qt_world_normal);\n";
1784 fragmentShader << " qt_anisotropicB = normalize(cross(qt_world_normal, qt_anisotropicT));\n";
1785
1786 fragmentShader << " qt_anisotropyAlphaB = clamp(qt_roughnessAmount * qt_roughnessAmount, 0.001, 1.0);\n";
1787 fragmentShader << " qt_anisotropyAlphaT = mix(qt_anisotropyAlphaB, 1.0, qt_anisotropyStrength * qt_anisotropyStrength);\n";
1788 }
1789
1790 if (hasCustomFrag)
1791 fragmentShader << " float qt_fresnelPower = qt_customFresnelPower;\n";
1792 else
1793 fragmentShader << " float qt_fresnelPower = qt_material_properties2.x;\n";
1794
1795 if (passRequirmentState.isPbrMaterial) {
1796 fragmentShader << " vec3 qt_principledMaterialFresnelValue = qt_principledMaterialFresnel(qt_world_normal, qt_view_vector, qt_f0, qt_roughnessAmount, qt_fresnelPower);\n";
1797 if (passRequirmentState.hasFresnelScaleBias) {
1798 if (hasCustomFrag) {
1799 fragmentShader << " float qt_fresnelScale = qt_customFresnelScale;\n";
1800 fragmentShader << " float qt_fresnelBias = qt_customFresnelBias;\n";
1801 } else {
1802 fragmentShader << " float qt_fresnelScale = qt_material_properties5.x;\n";
1803 fragmentShader << " float qt_fresnelBias = qt_material_properties5.y;\n";
1804 }
1805 fragmentShader << " qt_principledMaterialFresnelValue = clamp(vec3(qt_fresnelBias) + "
1806 << "qt_fresnelScale * qt_principledMaterialFresnelValue, 0.0, 1.0);\n";
1807 }
1808 fragmentShader << " qt_specularAmount *= qt_principledMaterialFresnelValue;\n";
1809 if (passRequirmentState.isMetallicRoughnessWorkflow) {
1810 // Make sure that we scale the specularTint with repsect to metalness (no tint if qt_metalnessAmount == 1)
1811 // We actually need to do this here because we won't know the final metalness value until this point.
1812 fragmentShader << " qt_specularTint = mix(vec3(1.0), qt_specularTint, 1.0 - qt_metalnessAmount);\n";
1813 }
1814 } else {
1815 fragmentShader << " qt_specularAmount *= qt_principledMaterialFresnel(qt_world_normal, qt_view_vector, qt_f0, qt_roughnessAmount, qt_fresnelPower);\n";
1816 }
1817 }
1818
1819 if (passRequirmentState.hasLighting && passRequirmentState.hasPunctualLights) {
1820 fragmentShader.addUniform("qt_lightAndShadowCounts", "vec4");
1821 fragmentShader.addFunction("processPunctualLighting");
1822 fragmentShader << " qt_processPunctualLighting(global_diffuse_light.rgb,\n"
1823 << " global_specular_light.rgb,\n"
1824 << " qt_diffuseColor.rgb,\n"
1825 << " qt_varWorldPos,\n"
1826 << " qt_world_normal.xyz,\n"
1827 << " qt_view_vector,\n"
1828 << "#if QSSG_ENABLE_SPECULAR\n"
1829 << " qt_specularAmount,\n"
1830 << " qt_specularTint,\n"
1831 << "#endif // QSSG_ENABLE_SPECULAR\n"
1832 << " qt_roughnessAmount,\n"
1833 << " qt_metalnessAmount,\n"
1834 << "#if QSSG_CUSTOM_MATERIAL_DIRECTIONAL_LIGHT_PROCESSOR || QSSG_CUSTOM_MATERIAL_POINT_LIGHT_PROCESSOR || QSSG_CUSTOM_MATERIAL_SPOT_LIGHT_PROCESSOR || QSSG_CUSTOM_MATERIAL_SPECULAR_PROCESSOR\n"
1835 << " qt_customBaseColor,\n"
1836 << "#endif // QSSG_CUSTOM_MATERIAL_*\n"
1837 << "#if QSSG_CUSTOM_MATERIAL_SPECULAR_PROCESSOR\n"
1838 << " qt_customSpecularAmount,\n"
1839 << "#endif // QSSG_CUSTOM_MATERIAL_SPECULAR_PROCESSOR\n"
1840 << "#if QSSG_CUSTOM_MATERIAL_SHARED_VARIABLES\n"
1841 << " qt_customShared,\n"
1842 << "#endif // QSSG_CUSTOM_MATERIAL_SHARED_VARIABLES\n"
1843 << "#if QSSG_ENABLE_CLEARCOAT\n"
1844 << " qt_global_clearcoat,\n"
1845 << " qt_clearcoatNormal,\n"
1846 << " qt_clearcoatRoughness,\n"
1847 << " qt_clearcoatF0,\n"
1848 << " qt_clearcoatF90,\n"
1849 << "#endif // QSSG_ENABLE_CLEARCOAT\n"
1850 << "#if QSSG_ENABLE_SHEEN\n"
1851 << " qt_global_sheen,\n"
1852 << " qt_sheenColor,\n"
1853 << " qt_sheenRoughness,\n"
1854 << "#endif // QSSG_ENABLE_SHEEN\n"
1855 << "#if QSSG_ENABLE_ANISOTROPY\n"
1856 << " qt_anisotropicT,\n"
1857 << " qt_anisotropicB,\n"
1858 << " qt_anisotropyAlphaT,\n"
1859 << " qt_anisotropyAlphaB,\n"
1860 << "#endif // QSSG_ENABLE_ANISOTROPY\n"
1861 << "#if QSSG_ENABLE_TRANSMISSION\n"
1862 << " qt_global_transmission,\n"
1863 << " qt_thicknessFactor,\n"
1864 << " qt_iOR,\n"
1865 << " qt_transmissionFactor,\n"
1866 << " qt_attenuationColor,\n"
1867 << " qt_attenuationDistance,\n"
1868 << "#endif // QSSG_ENABLE_TRANSMISSION\n"
1869 << " qt_f0,\n"
1870 << " qt_f90);\n";
1871 } //QSSG_CUSTOM_MATERIAL_SPECULAR_PROCESSOR
1872
1873 // The color in rgb is ready, including shadowing, just need to apply
1874 // the ambient occlusion factor. The alpha is the model opacity
1875 // multiplied by the alpha from the material color and/or the vertex colors.
1876 fragmentShader << " global_diffuse_light = vec4(global_diffuse_light.rgb * qt_aoFactor, qt_objectOpacity * qt_diffuseColor.a);\n";
1877
1878 if (passRequirmentState.hasReflectionProbe) {
1879 vertexShader.generateWorldNormal(inKey);
1880 fragmentShader.addInclude("sampleReflectionProbe.glsllib");
1881
1882 // Diffuse
1883 if (passRequirmentState.isPbrMaterial)
1884 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * (1.0 - qt_specularAmount) * qt_sampleDiffuseReflection(qt_reflectionMap, qt_world_normal).rgb;\n";
1885 else
1886 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * qt_sampleDiffuseReflection(qt_reflectionMap, qt_world_normal).rgb;\n";
1887
1888 // Specular
1889 if (passRequirmentState.hasSpecularLight) {
1890 if (passRequirmentState.hasAnisotropy) {
1891 fragmentShader << " vec3 qt_anisotropyIblNormal = qt_anisotropicBentNormal(qt_world_normal, "
1892 << "qt_view_vector, qt_anisotropicB, qt_anisotropyStrength, qt_roughnessAmount);\n";
1893 }
1894 if (passRequirmentState.isPbrMaterial && passRequirmentState.hasAnisotropy) {
1895 fragmentShader << " global_specular_light += qt_specularTint * qt_sampleGlossyAnisotropicReflectionProbe("
1896 << "qt_reflectionMap, qt_world_normal, qt_anisotropyIblNormal, qt_view_vector, "
1897 << "qt_specularAmount, qt_roughnessAmount).rgb;\n";
1898 } else if (passRequirmentState.isPbrMaterial)
1899 fragmentShader << " global_specular_light += qt_specularTint * qt_sampleGlossyReflectionPrincipled(qt_reflectionMap, "
1900 << "qt_world_normal, qt_view_vector, qt_specularAmount, qt_roughnessAmount).rgb;\n";
1901 else
1902 fragmentShader << " global_specular_light += qt_specularAmount * qt_specularTint * qt_sampleGlossyReflection(qt_reflectionMap, qt_world_normal, qt_view_vector, qt_roughnessAmount).rgb;\n";
1903 }
1904
1905 // Clearcoat (pbr Only)
1906 if (passRequirmentState.hasClearcoat)
1907 fragmentShader << " qt_global_clearcoat += qt_sampleGlossyReflectionPrincipled(qt_reflectionMap, qt_clearcoatNormal, qt_view_vector, qt_clearcoatF0, qt_clearcoatRoughness).rgb;\n";
1908 if (passRequirmentState.hasSheen)
1909 fragmentShader << " qt_global_sheen += qt_sampleGlossySheenReflectionProbe(qt_reflectionMap, qt_world_normal, qt_view_vector, qt_sheenColor, qt_sheenRoughness).rgb;\n";
1910
1911 } else if (passRequirmentState.hasIblProbe) {
1912 vertexShader.generateWorldNormal(inKey);
1913 fragmentShader.addInclude("sampleProbe.glsllib");
1914 if (hasCustomIblProbe) {
1915 // DIFFUSE, SPECULAR, BASE_COLOR, AO_FACTOR, SPECULAR_AMOUNT, NORMAL, VIEW_VECTOR, IBL_ORIENTATION(, SHARED)
1916 fragmentShader << " vec3 qt_iblDiffuse = vec3(0.0);\n";
1917 fragmentShader << " vec3 qt_iblSpecular = vec3(0.0);\n";
1918 if (passRequirmentState.hasClearcoat)
1919 fragmentShader << " vec3 qt_iblClearcoat = vec3(0.0);\n";
1920 if (passRequirmentState.hasSheen)
1921 fragmentShader << " vec3 qt_iblSheen = vec3(0.0);\n";
1922 fragmentShader << " qt_iblProbeProcessor(qt_iblDiffuse, qt_iblSpecular, qt_customBaseColor, qt_aoFactor, qt_specularFactor, qt_roughnessAmount, qt_world_normal, qt_view_vector";
1923 if (passRequirmentState.hasIblOrientation)
1924 fragmentShader << ", qt_lightProbeOrientation";
1925 else
1926 fragmentShader << ", mat3(1.0)";
1927 if (usesSharedVar)
1928 fragmentShader << ", qt_customShared);\n";
1929 else
1930 fragmentShader << ");\n";
1931 } else {
1932 // Diffuse
1933 if (passRequirmentState.isPbrMaterial)
1934 fragmentShader << " vec3 qt_iblDiffuse = qt_diffuseColor.rgb * (1.0 - qt_specularAmount) * qt_sampleDiffuse(qt_world_normal).rgb;\n";
1935 else
1936 fragmentShader << " vec3 qt_iblDiffuse = qt_diffuseColor.rgb * qt_sampleDiffuse(qt_world_normal).rgb;\n";
1937
1938 // Specular
1939 if (passRequirmentState.hasSpecularLight) {
1940 if (passRequirmentState.hasAnisotropy) {
1941 fragmentShader << " vec3 qt_anisotropyIblNormal = qt_anisotropicBentNormal(qt_world_normal, "
1942 << "qt_view_vector, qt_anisotropicB, qt_anisotropyStrength, qt_roughnessAmount);\n";
1943 }
1944 if (passRequirmentState.isPbrMaterial && passRequirmentState.hasAnisotropy) {
1945 fragmentShader << " vec3 qt_iblSpecular = qt_specularTint * qt_sampleGlossyAnisotropic("
1946 << "qt_world_normal, qt_anisotropyIblNormal, qt_view_vector, "
1947 << "qt_specularAmount, qt_roughnessAmount).rgb;\n";
1948 } else if (passRequirmentState.isPbrMaterial)
1949 fragmentShader << " vec3 qt_iblSpecular = qt_specularTint * qt_sampleGlossyPrincipled("
1950 << "qt_world_normal, qt_view_vector, qt_specularAmount, qt_roughnessAmount).rgb;\n";
1951 else
1952 fragmentShader << " vec3 qt_iblSpecular = qt_specularAmount * qt_specularTint * qt_sampleGlossy(qt_world_normal, qt_view_vector, qt_roughnessAmount).rgb;\n";
1953 }
1954
1955 // Clearcoat (pbr Only)
1956 if (passRequirmentState.hasClearcoat)
1957 fragmentShader << " vec3 qt_iblClearcoat = qt_sampleGlossyPrincipled(qt_clearcoatNormal, qt_view_vector, qt_clearcoatF0, qt_clearcoatRoughness).rgb;\n";
1958
1959 if (passRequirmentState.hasSheen)
1960 fragmentShader << " vec3 qt_iblSheen = qt_sampleGlossySheen(qt_world_normal, qt_view_vector, qt_sheenColor, qt_sheenRoughness).rgb;\n";
1961 }
1962
1963 fragmentShader << " global_diffuse_light.rgb += qt_iblDiffuse * qt_aoFactor;\n";
1964 if (passRequirmentState.hasSpecularLight)
1965 fragmentShader << " global_specular_light += qt_iblSpecular * qt_aoFactor;\n";
1966 if (passRequirmentState.hasClearcoat)
1967 fragmentShader << " qt_global_clearcoat += qt_iblClearcoat * qt_aoFactor;\n";
1968 if (passRequirmentState.hasSheen)
1969 fragmentShader << " qt_global_sheen += qt_iblSheen * qt_aoFactor;\n";
1970 } else if (hasCustomIblProbe) {
1971 // Prevent breaking the fragment code while seeking uniforms
1972 fragmentShader.addUniform("qt_lightProbe", "samplerCube");
1973 fragmentShader.addUniform("qt_lightProbeProperties", "vec4");
1974 }
1975
1976 // This can run even without a IBL probe
1977 if (passRequirmentState.hasTransmission) {
1978 fragmentShader << " qt_global_transmission += qt_transmissionFactor * qt_getIBLVolumeRefraction(qt_world_normal, qt_view_vector, qt_roughnessAmount, "
1979 "qt_diffuseColor.rgb, qt_specularAmount, qt_varWorldPos, qt_iOR, qt_thicknessFactor, qt_attenuationColor, qt_attenuationDistance"
1980 << "\n#if QSSG_ENABLE_DISPERSION\n"
1981 << " , qt_dispersion\n"
1982 << "#endif // QSSG_ENABLE_DISPERSION\n"
1983 << " );\n";
1984 }
1985
1986
1987 // Handle specular and emissive maps which just add additional color
1988 if (samplerState.hasImage(QSSGRenderableImage::Type::Specular) || samplerState.hasImage(QSSGRenderableImage::Type::Emissive)) {
1989 addLocalVariable(fragmentShader, "qt_texture_color", "vec4");
1990
1991 if (samplerState.hasImage(QSSGRenderableImage::Type::Specular)) {
1992 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Specular, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
1993 fragmentShader << " qt_texture_color = texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Specular) << ", "
1994 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Specular) << ");\n";
1995 fragmentShader.addInclude("tonemapping.glsllib");
1996 fragmentShader << " global_specular_light += qt_sRGBToLinear(qt_texture_color.rgb) * qt_specularTint;\n";
1997 fragmentShader << " global_diffuse_light.a *= qt_texture_color.a;\n";
1998 }
1999
2000 if (samplerState.hasImage(QSSGRenderableImage::Type::Emissive)) {
2001 samplerState.generateImageUVAndSampler(QSSGRenderableImage::Type::Emissive, vertexShader, fragmentShader, inKey, passRequirmentState.hasParallaxMapping);
2002 fragmentShader << " qt_texture_color = texture2D(" << samplerState.samplerName(QSSGRenderableImage::Type::Emissive) << ", "
2003 << samplerState.fragCoordsName(QSSGRenderableImage::Type::Emissive) << ");\n";
2004 fragmentShader.addInclude("tonemapping.glsllib");
2005 if (keyProps.m_emissiveSingleChannelEnabled.getValue(inKey)) {
2006 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::EmissiveChannel];
2007 fragmentShader << " qt_global_emission *= qt_sRGBToLinear(vec3(qt_texture_color" <<
2008 channelStr(channelProps, inKey) << "));\n";
2009 } else {
2010 fragmentShader << " qt_global_emission *= qt_sRGBToLinear(qt_texture_color.rgb);\n";
2011 }
2012 }
2013 }
2014
2015 if (passRequirmentState.hasTransmission)
2016 fragmentShader << " global_diffuse_light.rgb = mix(global_diffuse_light.rgb, qt_global_transmission, qt_transmissionFactor);\n";
2017
2018 if (passRequirmentState.isMetallicRoughnessWorkflow) {
2019 fragmentShader << " global_diffuse_light.rgb *= 1.0 - qt_metalnessAmount;\n";
2020 }
2021
2022 if (passRequirmentState.hasFog) {
2023 fragmentShader.addInclude("fog.glsllib");
2024 fragmentShader << " calculateFog(qt_global_emission, global_specular_light, global_diffuse_light.rgb);\n";
2025 }
2026
2027 fragmentShader << " vec4 qt_color_sum = vec4(global_diffuse_light.rgb + global_specular_light + qt_global_emission, global_diffuse_light.a);\n";
2028
2029 // Sheen goes between the base material and the clearcoat, like in glTF
2030 if (passRequirmentState.hasSheen) {
2031 fragmentShader.addInclude("sheen.glsllib");
2032 fragmentShader << " float qt_sheenScaling = qt_sheenAlbedoScaling(qt_sheenColor, clamp(dot(qt_world_normal, qt_view_vector), 0.0, 1.0), qt_sheenRoughness);\n";
2033 fragmentShader << " qt_color_sum.rgb = qt_color_sum.rgb * qt_sheenScaling + qt_global_sheen;\n";
2034 }
2035
2036 if (passRequirmentState.hasClearcoat) {
2037 fragmentShader.addInclude("bsdf.glsllib");
2038 if (hasCustomFrag)
2039 fragmentShader << " float qt_clearcoatFresnelPower = qt_customClearcoatFresnelPower;\n";
2040 else
2041 fragmentShader << " float qt_clearcoatFresnelPower = qt_material_clearcoat_fresnel_power;\n";
2042 fragmentShader << " vec3 qt_clearcoatFresnel = qt_schlick3(qt_clearcoatF0, qt_clearcoatF90, clamp(dot(qt_clearcoatNormal, qt_view_vector), 0.0, 1.0), qt_clearcoatFresnelPower);\n";
2043 if (passRequirmentState.hasClearcoatFresnelScaleBias) {
2044 if (hasCustomFrag) {
2045 fragmentShader << " float qt_clearcoatFresnelScale = qt_customClearcoatFresnelScale;\n";
2046 fragmentShader << " float qt_clearcoatFresnelBias = qt_customClearcoatFresnelBias;\n";
2047 }else {
2048 fragmentShader << " float qt_clearcoatFresnelScale = qt_material_properties5.z;\n";
2049 fragmentShader << " float qt_clearcoatFresnelBias = qt_material_properties5.w;\n";
2050 }
2051 fragmentShader << " qt_clearcoatFresnel = clamp(vec3(qt_clearcoatFresnelBias) + qt_clearcoatFresnelScale * qt_clearcoatFresnel, 0.0, 1.0);\n";
2052 }
2053 fragmentShader << " qt_global_clearcoat = qt_global_clearcoat * qt_clearcoatAmount;\n";
2054 fragmentShader << " qt_color_sum.rgb = qt_color_sum.rgb * (1.0 - qt_clearcoatAmount * qt_clearcoatFresnel) + qt_global_clearcoat;\n";
2055 }
2056
2057 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_customPostProcessor"))) {
2058 // COLOR_SUM, DIFFUSE, SPECULAR, EMISSIVE, UV0, UV1(, SHARED)
2059 fragmentShader << " qt_customPostProcessor(qt_color_sum, global_diffuse_light, global_specular_light, qt_global_emission, qt_texCoord0, qt_texCoord1";
2060 if (usesSharedVar)
2061 fragmentShader << ", qt_customShared);\n";
2062 else
2063 fragmentShader << ");\n";
2064 }
2065 } // end of lighting block
2066
2067 // Outputs
2068 switch (passRequirmentState.passType) {
2069
2071 break;
2073 // Unlit color pass
2074 if (!passRequirmentState.hasLighting)
2075 fragmentShader.append(" vec4 qt_color_sum = vec4(qt_diffuseColor.rgb, qt_diffuseColor.a * qt_objectOpacity);");
2076
2077 if (passRequirmentState.oitMethod == QSSGRenderLayer::OITMethod::WeightedBlended) {
2078 fragmentShader.addInclude("oitweightedblended.glsllib");
2079 fragmentShader.addInclude("tonemapping.glsllib");
2080 fragmentShader.addUniform("qt_cameraPosition", "vec3");
2081 fragmentShader.addUniform("qt_cameraProperties", "vec2");
2082 fragmentShader.append(" float z = abs(gl_FragCoord.z);");
2083 fragmentShader.append(" qt_color_sum.rgb = qt_tonemap(qt_color_sum.rgb) * qt_color_sum.a;");
2084 fragmentShader.append(" fragOutput = qt_color_sum * qt_transparencyWeight(z, qt_color_sum.a, qt_cameraProperties.y);");
2085 fragmentShader.append(" revealageOutput = vec4(qt_color_sum.a);");
2086 } else if (passRequirmentState.oitMethod == QSSGRenderLayer::OITMethod::LinkedList) {
2087 fragmentShader.addInclude("tonemapping.glsllib");
2088 fragmentShader.addUniform("qt_listNodeCount", "uint");
2089 fragmentShader.addUniform("qt_ABufImageWidth", "uint");
2090 fragmentShader.addUniform("qt_viewSize", "ivec2");
2091 if (passRequirmentState.oitMSAA)
2092 fragmentShader.addDefinition("QSSG_MULTISAMPLE", "1");
2093#ifdef QSSG_OIT_USE_BUFFERS
2094 QSSGShaderResourceMergeContext::setAdditionalBufferAmount(3);
2095 fragmentShader.addUniform("qt_samples", "uint");
2096 fragmentShader.addInclude("oitlinkedlist_buf.glsllib");
2097 if (viewCount >= 2)
2098 fragmentShader.append(" fragOutput = qt_oitLinkedList(qt_tonemap(qt_color_sum), qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, qt_viewIndex, qt_samples);");
2099 else
2100 fragmentShader.append(" fragOutput = qt_oitLinkedList(qt_tonemap(qt_color_sum), qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, 0, qt_samples);");
2101#else
2102 fragmentShader.addInclude("oitlinkedlist.glsllib");
2103 if (viewCount >= 2)
2104 fragmentShader.append(" fragOutput = qt_oitLinkedList(qt_tonemap(qt_color_sum), qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, qt_viewIndex);");
2105 else
2106 fragmentShader.append(" fragOutput = qt_oitLinkedList(qt_tonemap(qt_color_sum), qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, 0);");
2107#endif
2108
2109 } else {
2110 fragmentShader.addInclude("tonemapping.glsllib");
2111 fragmentShader.append(" fragOutput = vec4(qt_tonemap(qt_color_sum));");
2112 }
2113 break;
2115 fragmentShader << " vec4 fragOutput = vec4(0.0);\n";
2116 break;
2118 Q_ASSERT(viewCount == 1);
2119 fragmentShader << " // directional shadow pass\n"
2120 << " float qt_shadowDepth = (qt_varDepth + qt_shadowDepthAdjust.x) * qt_shadowDepthAdjust.y;\n"
2121 << " fragOutput = vec4(qt_shadowDepth);\n";
2122 break;
2124 Q_ASSERT(viewCount == 1);
2125 fragmentShader.addUniform("qt_cameraPosition", "vec3");
2126 fragmentShader.addUniform("qt_cameraProperties", "vec2");
2127 fragmentShader << " // omnidirectional shadow pass\n"
2128 << " vec3 qt_shadowCamPos = vec3(qt_cameraPosition.x, qt_cameraPosition.y, qt_cameraPosition.z);\n"
2129 << " float qt_shadowDist = length(qt_varShadowWorldPos - qt_shadowCamPos);\n"
2130 << " qt_shadowDist = (qt_shadowDist - qt_cameraProperties.x) / (qt_cameraProperties.y - qt_cameraProperties.x);\n"
2131 << " fragOutput = vec4(qt_shadowDist, qt_shadowDist, qt_shadowDist, 1.0);\n";
2132 break;
2134 // world space normal in rgb, roughness in alpha
2135 fragmentShader.append(" fragOutput = vec4(qt_world_normal, qt_roughnessAmount);\n");
2136 break;
2138 fragmentShader.append(" vec3 debugOutput = vec3(0.0);\n");
2139 switch (passRequirmentState.debugMode) {
2140 case QSSGRenderLayer::MaterialDebugMode::BaseColor:
2141 fragmentShader.addInclude("tonemapping.glsllib");
2142 fragmentShader.append(" debugOutput += qt_tonemap(qt_diffuseColor.rgb);\n");
2143 break;
2144 case QSSGRenderLayer::MaterialDebugMode::Roughness:
2145 fragmentShader.append(" debugOutput += vec3(qt_roughnessAmount);\n");
2146 break;
2147 case QSSGRenderLayer::MaterialDebugMode::Metalness:
2148 fragmentShader.append(" debugOutput += vec3(qt_metalnessAmount);\n");
2149 break;
2150 case QSSGRenderLayer::MaterialDebugMode::Diffuse:
2151 fragmentShader.addInclude("tonemapping.glsllib");
2152 fragmentShader.append(" debugOutput += qt_tonemap(global_diffuse_light.rgb);\n");
2153 break;
2154 case QSSGRenderLayer::MaterialDebugMode::Specular:
2155 fragmentShader.addInclude("tonemapping.glsllib");
2156 fragmentShader.append(" debugOutput += qt_tonemap(global_specular_light);\n");
2157 break;
2158 case QSSGRenderLayer::MaterialDebugMode::ShadowOcclusion:
2159 // Technically speaking this was just outputing the occlusion value of the last light processed, which is likely not super useful
2160 // So for now this just outputs 1.0 (no occlusion)
2161 fragmentShader.addFunction("debugShadowOcclusion");
2162 vertexShader.generateWorldPosition(inKey);
2163 fragmentShader.append(" debugOutput += vec3(qt_debugShadowOcclusion());\n");
2164 break;
2165 case QSSGRenderLayer::MaterialDebugMode::Emission:
2166 fragmentShader.addInclude("tonemapping.glsllib");
2167 fragmentShader.append(" debugOutput += qt_tonemap(qt_global_emission);\n");
2168 break;
2169 case QSSGRenderLayer::MaterialDebugMode::AmbientOcclusion:
2170 fragmentShader.append(" debugOutput += vec3(qt_aoFactor);\n");
2171 break;
2172 case QSSGRenderLayer::MaterialDebugMode::Normal:
2173 fragmentShader.append(" debugOutput += qt_world_normal * 0.5 + 0.5;\n");
2174 break;
2175 case QSSGRenderLayer::MaterialDebugMode::Tangent:
2176 fragmentShader.append(" debugOutput += qt_tangent * 0.5 + 0.5;\n");
2177 break;
2178 case QSSGRenderLayer::MaterialDebugMode::Binormal:
2179 fragmentShader.append(" debugOutput += qt_binormal * 0.5 + 0.5;\n");
2180 break;
2181 case QSSGRenderLayer::MaterialDebugMode::F0:
2182 if (passRequirmentState.isPbrMaterial)
2183 fragmentShader.append(" debugOutput += qt_f0;");
2184 break;
2185 case QSSGRenderLayer::MaterialDebugMode::None:
2186 Q_UNREACHABLE();
2187 break;
2188 }
2189 fragmentShader.append(" fragOutput = vec4(debugOutput, 1.0);\n");
2190 break;
2192 if (shaderAugmentation.hasUserAugmentation())
2193 fragmentShader << " " << shaderAugmentation.body << ";\n";
2194 break;
2195 }
2196}
2197
2198QSSGRhiShaderPipelinePtr QSSGMaterialShaderGenerator::generateMaterialRhiShader(const QByteArray &inShaderKeyPrefix,
2199 const QByteArray &inCacheKey,
2200 QSSGMaterialVertexPipeline &vertexPipeline,
2201 const QSSGShaderDefaultMaterialKey &key,
2202 const QSSGShaderDefaultMaterialKeyProperties &inProperties,
2203 const QSSGShaderFeatures &inFeatureSet,
2204 const QSSGRenderGraphObject &inMaterial,
2205 QSSGShaderLibraryManager &shaderLibraryManager,
2206 QSSGShaderCache &theCache,
2207 const QSSGUserShaderAugmentation &shaderAugmentation)
2208{
2209 const int viewCount = inFeatureSet.isSet(QSSGShaderFeatures::Feature::DisableMultiView)
2210 ? 1 : inProperties.m_viewCount.getValue(key);
2211
2212 bool perTargetCompilation = false;
2213 // Cull mode None implies doing the gl_FrontFacing-based double sided logic
2214 // in the shader. This may want to ifdef the generated shader code based the
2215 // target shading language. This is only possible if the QShaderBaker is
2216 // told to compile to SPIR-V (and then transpile) separately for each target
2217 // (GLSL, HLSL, etc.), instead of just compiling to SPIR-V once (and
2218 // transpiling the same bytecode to each target language). This takes more
2219 // time, so we only do it for multiview since that's also how the logic is
2220 // going to be written in the generated shader code.
2221 if (viewCount >= 2) {
2222 const bool isDoubleSided = inProperties.m_isDoubleSided.getValue(key);
2223 if (isDoubleSided)
2224 perTargetCompilation = true;
2225 }
2226
2227 // Used as the shader name (in a comment in the generated source, and in
2228 // log messages). The shader caches are keyed on inCacheKey instead, which
2229 // must be the exact same string the caller used for looking up the
2230 // shaders, otherwise the entries stored in the persistent cache would
2231 // never be found. inShaderKeyPrefix can be a static string for default
2232 // materials, but must be unique for different sets of shaders in custom
2233 // materials.
2234 QByteArray materialInfoString = inShaderKeyPrefix;
2235 key.toString(materialInfoString, inProperties);
2236
2237 // the call order is: beginVertex, beginFragment, endVertex, endFragment
2238 vertexPipeline.beginVertexGeneration(key, inFeatureSet, shaderLibraryManager);
2239 generateFragmentShader(vertexPipeline.fragment(), vertexPipeline, key, inProperties, inFeatureSet, inMaterial, shaderAugmentation, shaderLibraryManager);
2240 vertexPipeline.endVertexGeneration();
2241 vertexPipeline.endFragmentGeneration();
2242
2243 return vertexPipeline.programGenerator()->compileGeneratedRhiShader(materialInfoString,
2244 inFeatureSet,
2245 shaderLibraryManager,
2246 theCache,
2247 {},
2248 shaderAugmentation,
2249 viewCount,
2250 perTargetCompilation,
2251 inCacheKey);
2252}
2253
2254static quint32 softShadowQualityToInt(QSSGRenderLight::SoftShadowQuality quality)
2255{
2256 quint32 samplesCount = 0;
2257 switch (quality) {
2258 case QSSGRenderLight::SoftShadowQuality::Hard:
2259 samplesCount = 0;
2260 break;
2261 case QSSGRenderLight::SoftShadowQuality::PCF4:
2262 samplesCount = 4;
2263 break;
2264 case QSSGRenderLight::SoftShadowQuality::PCF8:
2265 samplesCount = 8;
2266 break;
2267 case QSSGRenderLight::SoftShadowQuality::PCF16:
2268 case QSSGRenderLight::SoftShadowQuality::PCF32:
2269 case QSSGRenderLight::SoftShadowQuality::PCF64:
2270 samplesCount = 16;
2271 break;
2272 }
2273
2274 return samplesCount;
2275}
2276
2277void QSSGMaterialShaderGenerator::setRhiMaterialProperties(const QSSGRenderContextInterface &renderContext,
2278 QSSGRhiShaderPipeline &shaders,
2279 char *ubufData,
2280 QSSGRhiGraphicsPipelineState *inPipelineState,
2281 const QSSGRenderGraphObject &inMaterial,
2282 const QSSGShaderDefaultMaterialKey &inKey,
2283 const QSSGShaderDefaultMaterialKeyProperties &inProperties,
2284 const QSSGRenderCameraList &inCameras,
2285 const QSSGRenderMvpArray &inModelViewProjections,
2286 const QMatrix3x3 &inNormalMatrix,
2287 const QMatrix4x4 &inGlobalTransform,
2288 const QMatrix4x4 &clipSpaceCorrMatrix,
2289 const QMatrix4x4 &localInstanceTransform,
2290 const QMatrix4x4 &globalInstanceTransform,
2291 const QSSGDataView<float> &inMorphWeights,
2292 QSSGRenderableImage *inFirstImage,
2293 float inOpacity,
2294 const QSSGLayerRenderData &inRenderProperties,
2295 const QSSGShaderLightListView &inLights,
2296 const QSSGShaderReflectionProbe &reflectionProbe,
2297 bool receivesShadows,
2298 bool receivesReflections,
2299 const QVector2D *shadowDepthAdjust,
2300 QRhiTexture *lightmapTexture)
2301{
2302 QSSGShaderMaterialAdapter *materialAdapter = getMaterialAdapter(inMaterial);
2303 QSSGRhiShaderPipeline::CommonUniformIndices &cui = shaders.commonUniformIndices;
2304
2305 materialAdapter->setCustomPropertyUniforms(ubufData, shaders, renderContext);
2306
2307 const QVector2D camProperties(inCameras[0]->clipPlanes);
2308 shaders.setUniform(ubufData, "qt_cameraProperties", &camProperties, 2 * sizeof(float), &cui.cameraPropertiesIdx);
2309
2310 const int viewCount = inCameras.count();
2311
2312 // Pull the camera transforms from the render data once.
2313 QMatrix4x4 camGlobalTransforms[2] { QMatrix4x4{Qt::Uninitialized}, QMatrix4x4{Qt::Uninitialized} };
2314 if (viewCount < 2) {
2315 camGlobalTransforms[0] = inRenderProperties.getGlobalTransform(*inCameras[0]);
2316 } else {
2317 for (size_t viewIndex = 0; viewIndex != std::size(camGlobalTransforms); ++viewIndex)
2318 camGlobalTransforms[viewIndex] = inRenderProperties.getGlobalTransform(*inCameras[viewIndex]);
2319 }
2320
2321 if (viewCount < 2) {
2322 const QMatrix4x4 &camGlobalTransform = camGlobalTransforms[0];
2323 const QVector3D camGlobalPos = QSSGRenderNode::getGlobalPos(camGlobalTransform);
2324 shaders.setUniform(ubufData, "qt_cameraPosition", &camGlobalPos, 3 * sizeof(float), &cui.cameraPositionIdx);
2325 const QVector3D camDirection = QSSG_GUARD(inRenderProperties.renderedCameraData.has_value())
2326 ? inRenderProperties.renderedCameraData.value()[0].direction
2327 : QVector3D{ 0.0f, 0.0f, -1.0f };
2328 shaders.setUniform(ubufData, "qt_cameraDirection", &camDirection, 3 * sizeof(float), &cui.cameraDirectionIdx);
2329 } else {
2330 QVarLengthArray<QVector3D, 2> camGlobalPos(viewCount);
2331 QVarLengthArray<QVector3D> camDirection(viewCount);
2332 for (size_t viewIndex = 0; viewIndex != std::size(camGlobalTransforms); ++viewIndex) {
2333 const QMatrix4x4 &camGlobalTransform = camGlobalTransforms[viewIndex];
2334 camGlobalPos[viewIndex] = QSSGRenderNode::getGlobalPos(camGlobalTransform);
2335 camDirection[viewIndex] = QSSG_GUARD(inRenderProperties.renderedCameraData.has_value())
2336 ? inRenderProperties.renderedCameraData.value()[viewIndex].direction
2337 : QVector3D{ 0.0f, 0.0f, -1.0f };
2338 }
2339 shaders.setUniformArray(ubufData, "qt_cameraPosition", camGlobalPos.constData(), viewCount, QSSGRenderShaderValue::Vec3, &cui.cameraPositionIdx);
2340 shaders.setUniformArray(ubufData, "qt_cameraDirection", camDirection.constData(), viewCount, QSSGRenderShaderValue::Vec3, &cui.cameraDirectionIdx);
2341 }
2342
2343 const auto globalRenderData = QSSGLayerRenderData::globalRenderProperties(renderContext);
2344
2345 // Only calculate and update Matrix uniforms if they are needed
2346 bool usesProjectionMatrix = false;
2347 bool usesInvProjectionMatrix = false;
2348 bool usesViewProjectionMatrix = false;
2349 bool usesModelViewProjectionMatrix = false;
2350 bool usesNormalMatrix = false;
2351 bool usesParentMatrix = false;
2352
2353 if (inMaterial.type == QSSGRenderGraphObject::Type::CustomMaterial) {
2354 const auto *customMaterial = static_cast<const QSSGRenderCustomMaterial *>(&inMaterial);
2355 usesProjectionMatrix = customMaterial->m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix);
2356 usesInvProjectionMatrix = customMaterial->m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix);
2357 // ### these should use flags like the above two
2358 usesViewProjectionMatrix = true;
2359 }
2360
2361 const bool usesInstancing = inProperties.m_usesInstancing.getValue(inKey);
2362 if (usesInstancing) {
2363 // Instanced calls have to calculate MVP and normalMatrix in the vertex shader
2364 usesViewProjectionMatrix = true;
2365 usesParentMatrix = true;
2366 } else {
2367 usesModelViewProjectionMatrix = true;
2368 usesNormalMatrix = true;
2369 }
2370
2371 if (materialAdapter->isTransmissionEnabled())
2372 usesViewProjectionMatrix = true;
2373
2374 // Update matrix uniforms
2375 if (usesProjectionMatrix || usesInvProjectionMatrix) {
2376 if (viewCount < 2) {
2377 const QMatrix4x4 projection = clipSpaceCorrMatrix * inCameras[0]->projection;
2378 if (usesProjectionMatrix)
2379 shaders.setUniform(ubufData, "qt_projectionMatrix", projection.constData(), 16 * sizeof(float), &cui.projectionMatrixIdx);
2380 if (usesInvProjectionMatrix)
2381 shaders.setUniform(ubufData, "qt_inverseProjectionMatrix", projection.inverted().constData(), 16 * sizeof (float), &cui.inverseProjectionMatrixIdx);
2382 } else {
2383 QVarLengthArray<QMatrix4x4, 2> projections(viewCount);
2384 QVarLengthArray<QMatrix4x4, 2> invertedProjections(viewCount);
2385 for (size_t viewIndex = 0; viewIndex != std::size(camGlobalTransforms); ++viewIndex) {
2386 projections[viewIndex] = clipSpaceCorrMatrix * inCameras[viewIndex]->projection;
2387 if (usesInvProjectionMatrix)
2388 invertedProjections[viewIndex] = projections[viewIndex].inverted();
2389 }
2390 if (usesProjectionMatrix)
2391 shaders.setUniformArray(ubufData, "qt_projectionMatrix", projections.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.projectionMatrixIdx);
2392 if (usesInvProjectionMatrix)
2393 shaders.setUniformArray(ubufData, "qt_inverseProjectionMatrix", invertedProjections.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.inverseProjectionMatrixIdx);
2394 }
2395 }
2396
2397 if (viewCount < 2) {
2398 const QMatrix4x4 viewMatrix = camGlobalTransforms[0].inverted();
2399 shaders.setUniform(ubufData, "qt_viewMatrix", viewMatrix.constData(), 16 * sizeof(float), &cui.viewMatrixIdx);
2400 } else {
2401 QVarLengthArray<QMatrix4x4, 2> viewMatrices(viewCount);
2402 for (size_t viewIndex = 0; viewIndex != std::size(camGlobalTransforms); ++viewIndex)
2403 viewMatrices[viewIndex] = camGlobalTransforms[viewIndex].inverted();
2404 shaders.setUniformArray(ubufData, "qt_viewMatrix", viewMatrices.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.viewMatrixIdx);
2405 }
2406
2407 if (usesViewProjectionMatrix) {
2408 if (viewCount < 2) {
2409 const QMatrix4x4 &camGlobalTransform = camGlobalTransforms[0];
2410 QMatrix4x4 viewProj(Qt::Uninitialized);
2411 inCameras[0]->calculateViewProjectionMatrix(camGlobalTransform, viewProj);
2412 viewProj = clipSpaceCorrMatrix * viewProj;
2413 shaders.setUniform(ubufData, "qt_viewProjectionMatrix", viewProj.constData(), 16 * sizeof(float), &cui.viewProjectionMatrixIdx);
2414 } else {
2415 QVarLengthArray<QMatrix4x4, 2> viewProjections(viewCount);
2416 for (size_t viewIndex = 0; viewIndex != std::size(camGlobalTransforms); ++viewIndex) {
2417 const auto &camGlobalTransform = camGlobalTransforms[viewIndex];
2418 inCameras[viewIndex]->calculateViewProjectionMatrix(camGlobalTransform, viewProjections[viewIndex]);
2419 viewProjections[viewIndex] = clipSpaceCorrMatrix * viewProjections[viewIndex];
2420 }
2421 shaders.setUniformArray(ubufData, "qt_viewProjectionMatrix", viewProjections.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.viewProjectionMatrixIdx);
2422 }
2423 }
2424
2425 // qt_modelMatrix is always available, but differnt when using instancing
2426 if (usesInstancing)
2427 shaders.setUniform(ubufData, "qt_modelMatrix", localInstanceTransform.constData(), 16 * sizeof(float), &cui.modelMatrixIdx);
2428 else
2429 shaders.setUniform(ubufData, "qt_modelMatrix", inGlobalTransform.constData(), 16 * sizeof(float), &cui.modelMatrixIdx);
2430
2431 if (usesModelViewProjectionMatrix) {
2432 if (viewCount < 2) {
2433 QMatrix4x4 mvp { clipSpaceCorrMatrix };
2434 mvp *= inModelViewProjections[0];
2435 shaders.setUniform(ubufData, "qt_modelViewProjection", mvp.constData(), 16 * sizeof(float), &cui.modelViewProjectionIdx);
2436 } else {
2437 QVarLengthArray<QMatrix4x4, 2> mvps(viewCount);
2438 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
2439 mvps[viewIndex] = clipSpaceCorrMatrix * inModelViewProjections[viewIndex];
2440 shaders.setUniformArray(ubufData, "qt_modelViewProjection", mvps.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.modelViewProjectionIdx);
2441 }
2442 }
2443 if (usesNormalMatrix)
2444 shaders.setUniform(ubufData, "qt_normalMatrix", inNormalMatrix.constData(), 12 * sizeof(float), &cui.normalMatrixIdx,
2445 QSSGRhiShaderPipeline::UniformFlag::Mat3); // real size will be 12 floats, setUniform repacks as needed
2446 if (usesParentMatrix)
2447 shaders.setUniform(ubufData, "qt_parentMatrix", globalInstanceTransform.constData(), 16 * sizeof(float));
2448
2449 // Morphing
2450 const qsizetype morphSize = inProperties.m_targetCount.getValue(inKey);
2451 if (morphSize > 0) {
2452 if (inMorphWeights.mSize >= morphSize) {
2453 shaders.setUniformArray(ubufData, "qt_morphWeights", inMorphWeights.mData, morphSize,
2454 QSSGRenderShaderValue::Float, &cui.morphWeightsIdx);
2455 } else {
2456 const QList<float> zeroWeights(morphSize - inMorphWeights.mSize, 0.0f);
2457 QList<float> newWeights(inMorphWeights.mData, inMorphWeights.mData + inMorphWeights.mSize);
2458 newWeights.append(zeroWeights);
2459 shaders.setUniformArray(ubufData, "qt_morphWeights", newWeights.constData(), morphSize,
2460 QSSGRenderShaderValue::Float, &cui.morphWeightsIdx);
2461 }
2462 }
2463
2464 QVector3D theLightAmbientTotal;
2465 quint32 lightCount = 0;
2466 quint32 directionalLightCount = 0;
2467 quint32 shadowCount = 0;
2468 quint32 directionalShadowCount = 0;
2469 QSSGShaderLightsUniformData &lightsUniformData(shaders.lightsUniformData());
2470 QSSGShaderDirectionalLightsUniformData &directionalLightsUniformData(shaders.directionalLightsUniformData());
2471
2472 for (quint32 lightIdx = 0, lightEnd = inLights.size();
2473 lightIdx < lightEnd && lightIdx < QSSG_MAX_NUM_LIGHTS; ++lightIdx)
2474 {
2475 QSSGRenderLight *theLight(inLights[lightIdx].light);
2476
2477 // Gather Common Properties
2478 const bool lightShadows = inLights[lightIdx].shadows;
2479 const float brightness = theLight->m_brightness;
2480 quint32 lightmapState = 0;
2481 if (theLight->m_bakingEnabled) {
2482 if (theLight->m_fullyBaked) {
2483 // Lightmap provides Indirect + Diffuse (we still need to provide specular)
2484 lightmapState = 2;
2485 } else {
2486 // Lightmap provides Indirect (we still provide direct diffuse + specular)
2487 lightmapState = 1;
2488 }
2489 }
2490
2491 const QVector3D diffuseColor(theLight->m_diffuseColor.x() * brightness,
2492 theLight->m_diffuseColor.y() * brightness,
2493 theLight->m_diffuseColor.z() * brightness);
2494 const QVector3D specularColor(theLight->m_specularColor.x() * brightness,
2495 theLight->m_specularColor.y() * brightness,
2496 theLight->m_specularColor.z() * brightness);
2497 const QVector3D direction(inLights[lightIdx].direction);
2498
2499
2500 if (theLight->type == QSSGRenderGraphObject::Type::DirectionalLight) {
2501 // Directional Light
2502 QSSGShaderDirectionalLightData &lightData(directionalLightsUniformData.directionalLightData[directionalLightCount]);
2503 lightData.direction[0] = direction.x();
2504 lightData.direction[1] = direction.y();
2505 lightData.direction[2] = direction.z();
2506 lightData.diffuseColor[0] = diffuseColor.x();
2507 lightData.diffuseColor[1] = diffuseColor.y();
2508 lightData.diffuseColor[2] = diffuseColor.z();
2509 lightData.specularColor[0] = specularColor.x();
2510 lightData.specularColor[1] = specularColor.y();
2511 lightData.specularColor[2] = specularColor.z();
2512 lightData.lightmapState = lightmapState;
2513 if (lightShadows && receivesShadows) {
2514 lightData.enableShadows = 1.0f;
2515 QSSGShadowMapEntry *pEntry = inRenderProperties.getShadowMapManager()->shadowMapEntry(lightIdx);
2516 Q_ASSERT(pEntry);
2517
2518 const quint32 layerCount = pEntry->m_csmNumSplits + 1;
2519
2520 for (quint32 i = 0; i < layerCount; ++i)
2521 memcpy(lightData.matrices[i], pEntry->m_fixedScaleBiasMatrix[i].constData(), 16 * sizeof(float));
2522
2523 lightData.shadowBias = theLight->m_shadowBias;
2524 // If all cascades are inactive, disable shadows for this light, as there are no casters.
2525 const bool noCascades = !(pEntry->m_csmActive[0] || pEntry->m_csmActive[1] || pEntry->m_csmActive[2] || pEntry->m_csmActive[3]);
2526 if (theLight->type == QSSGRenderLight::Type::DirectionalLight && noCascades)
2527 lightData.enableShadows = 0.0f;
2528 lightData.shadowFactor = theLight->m_shadowFactor;
2529 lightData.shadowMapFar = theLight->m_shadowMapFar;
2530 lightData.shadowPcfSamples = softShadowQualityToInt(theLight->m_softShadowQuality);
2531 lightData.shadowPcfFactor = theLight->m_pcfFactor;
2532
2533 for (quint32 i = 0; i < layerCount; ++i) {
2534 const auto &atlasInfo = pEntry->m_atlasInfo[i];
2535 lightData.atlasLocations[i][0] = atlasInfo.uOffset;
2536 lightData.atlasLocations[i][1] = atlasInfo.vOffset;
2537 lightData.atlasLocations[i][2] = atlasInfo.uvScale;
2538 lightData.atlasLocations[i][3] = atlasInfo.layerIndex;
2539 }
2540
2541 lightData.csmNumSplits = pEntry->m_csmNumSplits;
2542 memcpy(lightData.csmSplits, pEntry->m_csmSplits, 4 * sizeof(float));
2543 memcpy(lightData.csmActive, pEntry->m_csmActive, 4 * sizeof(float));
2544 lightData.csmBlendRatio = theLight->m_csmBlendRatio;
2545 for (quint32 i = 0; i < layerCount; ++i)
2546 memcpy(lightData.dimensionsInverted[i], &pEntry->m_dimensionsInverted[i], 4 * sizeof(float));
2547
2548 directionalShadowCount++;
2549 } else {
2550 lightData.enableShadows = 0.0f;
2551 }
2552 directionalLightCount++;
2553 } else {
2554 // Point or Spot Light
2555 QSSGShaderLightData &lightData(lightsUniformData.lightData[lightCount]);
2556 const auto gt = inRenderProperties.getGlobalTransform(*theLight);
2557 const QVector3D globalPos = QSSGRenderNode::getGlobalPos(gt);
2558 lightData.position[0] = globalPos.x();
2559 lightData.position[1] = globalPos.y();
2560 lightData.position[2] = globalPos.z();
2561 lightData.constantAttenuation = QSSGUtils::aux::translateConstantAttenuation(theLight->m_constantFade);
2562 lightData.linearAttenuation = QSSGUtils::aux::translateLinearAttenuation(theLight->m_linearFade);
2563 lightData.quadraticAttenuation = QSSGUtils::aux::translateQuadraticAttenuation(theLight->m_quadraticFade);
2564 lightData.coneAngle = 360.0f;
2565 lightData.direction[0] = direction.x();
2566 lightData.direction[1] = direction.y();
2567 lightData.direction[2] = direction.z();
2568 lightData.diffuseColor[0] = diffuseColor.x();
2569 lightData.diffuseColor[1] = diffuseColor.y();
2570 lightData.diffuseColor[2] = diffuseColor.z();
2571 lightData.specularColor[0] = specularColor.x();
2572 lightData.specularColor[1] = specularColor.y();
2573 lightData.specularColor[2] = specularColor.z();
2574 lightData.lightmapState = lightmapState;
2575 if (theLight->type == QSSGRenderLight::Type::SpotLight) {
2576 // NB: This is how we tell in the shader that this is a spot light
2577 // PointLights have a coneAngle of 360.0f which is greater than 1.0
2578 // Since cos(any value) will be between -1 and 1, we can use this.
2579 const float coneAngle = theLight->m_coneAngle;
2580 const float innerConeAngle = (theLight->m_innerConeAngle > coneAngle) ? coneAngle : theLight->m_innerConeAngle;
2581 lightData.coneAngle = qCos(qDegreesToRadians(coneAngle));
2582 lightData.innerConeAngle = qCos(qDegreesToRadians(innerConeAngle));
2583 }
2584
2585 if (lightShadows && receivesShadows) {
2586 QSSGShadowMapEntry *pEntry = inRenderProperties.getShadowMapManager()->shadowMapEntry(lightIdx);
2587 Q_ASSERT(pEntry);
2588 lightData.enableShadows = 1.0f;
2589 lightData.shadowPcfFactor = theLight->m_pcfFactor;
2590 lightData.shadowPcfSamples = softShadowQualityToInt(theLight->m_softShadowQuality);
2591 lightData.shadowFactor = theLight->m_shadowFactor;
2592 lightData.shadowBias = theLight->m_shadowBias;
2593 lightData.shadowClipNear = 1.0f;
2594 lightData.shadowMapFar = pEntry->m_shadowMapFar;
2595 lightData.shadowTextureSize = pEntry->m_atlasInfo[0].uvScale;
2596
2597 if (theLight->type == QSSGRenderLight::Type::SpotLight) {
2598 // add fixed scale bias matrix
2599 static const QMatrix4x4 bias = {
2600 0.5, 0.0, 0.0, 0.5,
2601 0.0, 0.5, 0.0, 0.5,
2602 0.0, 0.0, 0.5, 0.5,
2603 0.0, 0.0, 0.0, 1.0 };
2604 const QMatrix4x4 m = bias * pEntry->m_lightViewProjection[0];
2605 memcpy(lightData.shadowMatrix, m.constData(), 16 * sizeof(float));
2606 lightData.shadowAtlasUV0[0] = pEntry->m_atlasInfo[0].uOffset;
2607 lightData.shadowAtlasUV0[1] = pEntry->m_atlasInfo[0].vOffset;
2608 lightData.shadowAtlasLayer0 = pEntry->m_atlasInfo[0].layerIndex;
2609
2610 } else {
2611 Q_ASSERT(theLight->type == QSSGRenderLight::Type::PointLight);
2612 memcpy(lightData.shadowMatrix, pEntry->m_lightView.constData(), 16 * sizeof(float));
2613 lightData.shadowAtlasUV0[0] = pEntry->m_atlasInfo[0].uOffset;
2614 lightData.shadowAtlasUV0[1] = pEntry->m_atlasInfo[0].vOffset;
2615 lightData.shadowAtlasLayer0 = pEntry->m_atlasInfo[0].layerIndex;
2616 lightData.shadowAtlasUV1[0] = pEntry->m_atlasInfo[1].uOffset;
2617 lightData.shadowAtlasUV1[1] = pEntry->m_atlasInfo[1].vOffset;
2618 lightData.shadowAtlasLayer1 = pEntry->m_atlasInfo[1].layerIndex;
2619 }
2620 shadowCount++;
2621
2622 } else {
2623 lightData.enableShadows = 0.0f;
2624 }
2625
2626 lightCount++;
2627 }
2628
2629 theLightAmbientTotal += theLight->m_ambientColor;
2630 }
2631
2632 // Shadow Map Atlas Texture (if needed)
2633 if (shadowCount > 0 || directionalShadowCount > 0) {
2634 shaders.setShadowMapAtlasTexture(inRenderProperties.getShadowMapManager()->shadowMapAtlasTexture());
2635 shaders.setShadowMapBlueNoiseTexture(inRenderProperties.getShadowMapManager()->shadowMapBlueNoiseTexture());
2636 } else {
2637 shaders.setShadowMapAtlasTexture(nullptr);
2638 shaders.setShadowMapBlueNoiseTexture(nullptr);
2639 }
2640
2641 const QSSGRhiRenderableTexture *depthTexture = inRenderProperties.getRenderResult(QSSGRenderResult::Key::DepthTexture);
2642 const QSSGRhiRenderableTexture *normalTexture = inRenderProperties.getRenderResult(QSSGRenderResult::Key::NormalTexture);
2643 const QSSGRhiRenderableTexture *ssaoTexture = inRenderProperties.getRenderResult(QSSGRenderResult::Key::AoTexture);
2644 const QSSGRhiRenderableTexture *screenTexture = inRenderProperties.getRenderResult(QSSGRenderResult::Key::ScreenTexture);
2645 const QSSGRhiRenderableTexture *motionVectorTexture = inRenderProperties.getRenderResult(QSSGRenderResult::Key::MotionVectorTexture);
2646
2647 shaders.setDepthTexture(depthTexture->texture);
2648 shaders.setNormalTexture(normalTexture->texture);
2649 shaders.setSsaoTexture(ssaoTexture->texture);
2650 shaders.setScreenTexture(screenTexture->texture);
2651 shaders.setLightmapTexture(lightmapTexture);
2652 shaders.setMotionVectorTexture(motionVectorTexture->texture);
2653
2654#ifdef QSSG_OIT_USE_BUFFERS
2655 shaders.setOITImages((QRhiTexture*)inRenderProperties.getOitRenderContextConst().aBuffer,
2656 (QRhiTexture*)inRenderProperties.getOitRenderContextConst().auxBuffer,
2657 (QRhiTexture*)inRenderProperties.getOitRenderContextConst().counterBuffer);
2658 if (inRenderProperties.getOitRenderContextConst().aBuffer) {
2659#else
2660 const QSSGRhiRenderableTexture *abuf = inRenderProperties.getRenderResult(QSSGRenderResult::Key::ABufferImage);
2661 const QSSGRhiRenderableTexture *aux = inRenderProperties.getRenderResult(QSSGRenderResult::Key::AuxiliaryImage);
2662 const QSSGRhiRenderableTexture *counter = inRenderProperties.getRenderResult(QSSGRenderResult::Key::CounterImage);
2663 shaders.setOITImages(abuf->texture, aux->texture, counter->texture);
2664 if (abuf->texture) {
2665#endif
2666 int abufWidth = RenderHelpers::rhiCalculateABufferSize(inRenderProperties.layer.oitNodeCount);
2667 int listNodeCount = abufWidth * abufWidth;
2668 shaders.setUniform(ubufData, "qt_ABufImageWidth", &abufWidth, sizeof(int), &cui.abufImageWidth);
2669 shaders.setUniform(ubufData, "qt_listNodeCount", &listNodeCount, sizeof(int), &cui.listNodeCount);
2670 int viewSize[2] = {inRenderProperties.layerPrepResult.textureDimensions().width(), inRenderProperties.layerPrepResult.textureDimensions().height()};
2671 shaders.setUniform(ubufData, "qt_viewSize", viewSize, sizeof(int) * 2, &cui.viewSize);
2672 int samples = inPipelineState->samples;
2673 shaders.setUniform(ubufData, "qt_samples", &samples, sizeof(int), &cui.samples);
2674 }
2675
2676 QSSGRenderImageTexture resolvedLayerIbl;
2677 QSSGRenderTextureCoordOp hTile = QSSGRenderTextureCoordOp::ClampToEdge;
2678 QSSGRenderTextureCoordOp vTile = QSSGRenderTextureCoordOp::ClampToEdge;
2679 const QSSGRenderLayer &layer = QSSGLayerRenderData::getCurrent(*renderContext.renderer())->layer;
2680 if (layer.lightProbe) {
2681 resolvedLayerIbl = renderContext.bufferManager()->loadRenderImage(layer.lightProbe, QSSGBufferManager::MipModeBsdf);
2682 hTile = layer.lightProbe->m_horizontalTilingMode;
2683 vTile = layer.lightProbe->m_verticalTilingMode;
2684 } else if (layer.skyMaterial && layer.skyMaterial->enableIBL) {
2685 resolvedLayerIbl = inRenderProperties.skyMaterialTexture;
2686 }
2687
2688 const auto &lightProbeData = layer.lightProbeSettings;
2689
2690 // If the material has its own IBL Override, we should use that image instead.
2691 QSSGRenderImage *materialIblProbe = materialAdapter->iblProbe();
2692
2693 QSSGRenderImageTexture lightProbeTexture;
2694 if (materialIblProbe) {
2695 lightProbeTexture = renderContext.bufferManager()->loadRenderImage(materialIblProbe, QSSGBufferManager::MipModeBsdf);
2696 hTile = materialIblProbe->m_horizontalTilingMode;
2697 vTile = materialIblProbe->m_verticalTilingMode;
2698 } else if (resolvedLayerIbl.m_texture) {
2699 lightProbeTexture = resolvedLayerIbl;
2700 }
2701
2702 if (lightProbeTexture.m_texture) {
2703 const int maxMipLevel = lightProbeTexture.m_mipmapCount - 1;
2704
2705 if (!materialIblProbe && !lightProbeData.probeOrientation.isIdentity()) {
2706 shaders.setUniform(ubufData, "qt_lightProbeOrientation",
2707 lightProbeData.probeOrientation.constData(),
2708 12 * sizeof(float), &cui.lightProbeOrientationIdx,
2709 QSSGRhiShaderPipeline::UniformFlag::Mat3);
2710 }
2711
2712 const float props[4] = { 0.0f, float(maxMipLevel), lightProbeData.probeHorizon, lightProbeData.probeExposure };
2713 shaders.setUniform(ubufData, "qt_lightProbeProperties", props, 4 * sizeof(float), &cui.lightProbePropertiesIdx);
2714
2715 shaders.setLightProbeTexture(lightProbeTexture.m_texture, hTile, vTile);
2716 } else {
2717 // no lightprobe
2718 const float emptyProps[4] = { 0.0f, 0.0f, -1.0f, 0.0f };
2719 shaders.setUniform(ubufData, "qt_lightProbeProperties", emptyProps, 4 * sizeof(float), &cui.lightProbePropertiesIdx);
2720
2721 shaders.setLightProbeTexture(nullptr);
2722 }
2723
2724 if (receivesReflections && reflectionProbe.enabled) {
2725 shaders.setUniform(ubufData, "qt_reflectionProbeCubeMapCenter", &reflectionProbe.probeCubeMapCenter, 3 * sizeof(float), &cui.reflectionProbeCubeMapCenter);
2726 shaders.setUniform(ubufData, "qt_reflectionProbeBoxMin", &reflectionProbe.probeBoxMin, 3 * sizeof(float), &cui.reflectionProbeBoxMin);
2727 shaders.setUniform(ubufData, "qt_reflectionProbeBoxMax", &reflectionProbe.probeBoxMax, 3 * sizeof(float), &cui.reflectionProbeBoxMax);
2728 shaders.setUniform(ubufData, "qt_reflectionProbeCorrection", &reflectionProbe.parallaxCorrection, sizeof(int), &cui.reflectionProbeCorrection);
2729 }
2730
2731 const QVector3D emissiveColor = materialAdapter->emissiveColor();
2732 shaders.setUniform(ubufData, "qt_material_emissive_color", &emissiveColor, 3 * sizeof(float), &cui.material_emissiveColorIdx);
2733
2734 const auto qMix = [](float x, float y, float a) {
2735 return (x * (1.0f - a) + (y * a));
2736 };
2737
2738 const auto qMix3 = [&qMix](const QVector3D &x, const QVector3D &y, float a) {
2739 return QVector3D{qMix(x.x(), y.x(), a), qMix(x.y(), y.y(), a), qMix(x.z(), y.z(), a)};
2740 };
2741
2742 const QVector4D color = materialAdapter->color();
2743 const QVector3D materialSpecularTint = materialAdapter->specularTint();
2744 const QVector3D specularTint = materialAdapter->isPrincipled() ? qMix3(QVector3D(1.0f, 1.0f, 1.0f), color.toVector3D(), materialSpecularTint.x())
2745 : materialSpecularTint;
2746 shaders.setUniform(ubufData, "qt_material_base_color", &color, 4 * sizeof(float), &cui.material_baseColorIdx);
2747
2748 const float ior = materialAdapter->ior();
2749 QVector4D specularColor(specularTint, ior);
2750 shaders.setUniform(ubufData, "qt_material_specular", &specularColor, 4 * sizeof(float), &cui.material_specularIdx);
2751
2752 const bool hasLighting = materialAdapter->hasLighting();
2753 shaders.setLightsEnabled(hasLighting);
2754 if (hasLighting) {
2755 const float lightAndShadowCounts[4] = {
2756 float(lightCount),
2757 float(directionalLightCount),
2758 float(shadowCount),
2759 float(directionalShadowCount)
2760 };
2761 shaders.setUniform(ubufData, "qt_lightAndShadowCounts", &lightAndShadowCounts, 4 * sizeof(float), &cui.lightAndShadowCountsIdx);
2762
2763 const size_t lightDataSize = lightCount * sizeof(QSSGShaderLightData);
2764 const size_t directionalLightDataSize = directionalLightCount * sizeof(QSSGShaderDirectionalLightData);
2765
2766 memcpy(ubufData + shaders.ub0LightDataOffset(), &lightsUniformData, lightDataSize);
2767 memcpy(ubufData + shaders.ub0DirectionalLightDataOffset(), &directionalLightsUniformData, directionalLightDataSize);
2768 }
2769
2770 shaders.setUniform(ubufData, "qt_light_ambient_total", &theLightAmbientTotal, 3 * sizeof(float), &cui.light_ambient_totalIdx);
2771
2772 const float materialProperties[4] = {
2773 materialAdapter->specularAmount(),
2774 materialAdapter->specularRoughness(),
2775 materialAdapter->metalnessAmount(),
2776 inOpacity
2777 };
2778 shaders.setUniform(ubufData, "qt_material_properties", materialProperties, 4 * sizeof(float), &cui.material_propertiesIdx);
2779
2780 const float materialProperties2[4] = {
2781 materialAdapter->fresnelPower(),
2782 materialAdapter->bumpAmount(),
2783 materialAdapter->translucentFallOff(),
2784 materialAdapter->diffuseLightWrap()
2785 };
2786 shaders.setUniform(ubufData, "qt_material_properties2", materialProperties2, 4 * sizeof(float), &cui.material_properties2Idx);
2787
2788 const float materialProperties3[4] = {
2789 materialAdapter->occlusionAmount(),
2790 materialAdapter->alphaCutOff(),
2791 materialAdapter->clearcoatAmount(),
2792 materialAdapter->clearcoatRoughnessAmount()
2793 };
2794 shaders.setUniform(ubufData, "qt_material_properties3", materialProperties3, 4 * sizeof(float), &cui.material_properties3Idx);
2795
2796 const float materialProperties4[4] = {
2797 materialAdapter->heightAmount(),
2798 materialAdapter->minHeightSamples(),
2799 materialAdapter->maxHeightSamples(),
2800 materialAdapter->transmissionFactor()
2801 };
2802 shaders.setUniform(ubufData, "qt_material_properties4", materialProperties4, 4 * sizeof(float), &cui.material_properties4Idx);
2803
2804 const bool hasCustomFrag = materialAdapter->hasCustomShaderSnippet(QSSGShaderCache::ShaderType::Fragment);
2805 if (!hasCustomFrag) {
2806 if (inProperties.m_fresnelScaleBiasEnabled.getValue(inKey) || inProperties.m_clearcoatFresnelScaleBiasEnabled.getValue(inKey)) {
2807 const float materialProperties5[4] = {
2808 materialAdapter->fresnelScale(),
2809 materialAdapter->fresnelBias(),
2810 materialAdapter->clearcoatFresnelScale(),
2811 materialAdapter->clearcoatFresnelBias()
2812 };
2813 shaders.setUniform(ubufData, "qt_material_properties5", materialProperties5, 4 * sizeof(float), &cui.material_properties5Idx);
2814 }
2815
2816 const float material_clearcoat_normal_strength = materialAdapter->clearcoatNormalStrength();
2817 shaders.setUniform(ubufData, "qt_material_clearcoat_normal_strength", &material_clearcoat_normal_strength, sizeof(float), &cui.clearcoatNormalStrengthIdx);
2818
2819 if (materialAdapter->isSheenEnabled()) {
2820 const QVector3D sheenColor = materialAdapter->sheenColor();
2821 shaders.setUniform(ubufData, "qt_material_sheen_color", &sheenColor, 3 * sizeof(float), &cui.sheenColorIdx);
2822 const float sheenRoughness = materialAdapter->sheenRoughness();
2823 shaders.setUniform(ubufData, "qt_material_sheen_roughness", &sheenRoughness, sizeof(float), &cui.sheenRoughnessIdx);
2824 }
2825
2826 if (materialAdapter->isAnisotropyEnabled()) {
2827 const float rotation = materialAdapter->anisotropyRotation();
2828 const QVector3D anisotropy(materialAdapter->anisotropyStrength(), std::cos(rotation), std::sin(rotation));
2829 shaders.setUniform(ubufData, "qt_material_anisotropy", &anisotropy, 3 * sizeof(float), &cui.anisotropyIdx);
2830 }
2831
2832 if (materialAdapter->isIridescenceEnabled()) {
2833 const QVector4D iridescence(materialAdapter->iridescenceFactor(),
2834 materialAdapter->iridescenceIndexOfRefraction(),
2835 materialAdapter->iridescenceThicknessMinimum(),
2836 materialAdapter->iridescenceThicknessMaximum());
2837 shaders.setUniform(ubufData, "qt_material_iridescence", &iridescence, 4 * sizeof(float), &cui.iridescenceIdx);
2838 }
2839
2840 const float material_clearcoat_fresnel_power = materialAdapter->clearcoatFresnelPower();
2841 shaders.setUniform(ubufData, "qt_material_clearcoat_fresnel_power", &material_clearcoat_fresnel_power, sizeof(float), &cui.clearcoatFresnelPowerIdx);
2842 // We only ever use attenuation and thickness uniforms when using transmission
2843 if (materialAdapter->isTransmissionEnabled()) {
2844 const QVector4D attenuationProperties(materialAdapter->attenuationColor(), materialAdapter->attenuationDistance());
2845 shaders.setUniform(ubufData, "qt_material_attenuation", &attenuationProperties, 4 * sizeof(float), &cui.material_attenuationIdx);
2846
2847 const float thickness = materialAdapter->thicknessFactor();
2848 shaders.setUniform(ubufData, "qt_material_thickness", &thickness, sizeof(float), &cui.thicknessFactorIdx);
2849
2850 if (materialAdapter->isDispersionEnabled()) {
2851 const float dispersion = materialAdapter->dispersion();
2852 shaders.setUniform(ubufData, "qt_material_dispersion", &dispersion, sizeof(float), &cui.dispersionIdx);
2853 }
2854 }
2855 }
2856
2857 const float rhiProperties[4] = {
2858 globalRenderData.isYUpInFramebuffer ? 1.0f : -1.0f,
2859 globalRenderData.isYUpInNDC ? 1.0f : -1.0f,
2860 globalRenderData.isClipDepthZeroToOne ? 0.0f : -1.0f,
2861 0.0f // unused
2862 };
2863 shaders.setUniform(ubufData, "qt_rhi_properties", rhiProperties, 4 * sizeof(float), &cui.rhiPropertiesIdx);
2864
2865 qsizetype imageIdx = 0;
2866 for (QSSGRenderableImage *theImage = inFirstImage; theImage; theImage = theImage->m_nextImage, ++imageIdx) {
2867 // we need to map image to uniform name: "image0_rotations", "image0_offsets", etc...
2868 const auto &names = imageStringTable[int(theImage->m_mapType)];
2869 if (imageIdx == cui.imageIndices.size())
2870 cui.imageIndices.append(QSSGRhiShaderPipeline::CommonUniformIndices::ImageIndices());
2871 auto &indices = cui.imageIndices[imageIdx];
2872
2873 const QMatrix4x4 &textureTransform = theImage->m_imageNode.m_textureTransform;
2874 // We separate rotational information from offset information so that just maybe the shader
2875 // will attempt to push less information to the card.
2876 const float *dataPtr(textureTransform.constData());
2877 // The third member of the offsets contains a flag indicating if the texture was
2878 // premultiplied or not.
2879 // We use this to mix the texture alpha.
2880 const float offsets[3] = { dataPtr[12], dataPtr[13], 0.0f /* non-premultiplied */ };
2881 shaders.setUniform(ubufData, names.imageOffsets, offsets, sizeof(offsets), &indices.imageOffsetsUniformIndex);
2882 // Grab just the upper 2x2 rotation matrix from the larger matrix.
2883 const float rotations[4] = { dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5] };
2884 shaders.setUniform(ubufData, names.imageRotations, rotations, sizeof(rotations), &indices.imageRotationsUniformIndex);
2885 }
2886
2887 if (shadowDepthAdjust)
2888 shaders.setUniform(ubufData, "qt_shadowDepthAdjust", shadowDepthAdjust, 2 * sizeof(float), &cui.shadowDepthAdjustIdx);
2889
2890 const bool usesPointsTopology = inProperties.m_usesPointsTopology.getValue(inKey);
2891 if (usesPointsTopology) {
2892 const float pointSize = materialAdapter->pointSize();
2893 shaders.setUniform(ubufData, "qt_materialPointSize", &pointSize, sizeof(float), &cui.pointSizeIdx);
2894 }
2895
2896 // qt_fogColor = (fogColor.x, fogColor.y, fogColor.z, fogDensity)
2897 // qt_fogDepthProperties = (fogDepthBegin, fogDepthEnd, fogDepthCurve, fogDepthEnabled ? 1.0 : 0.0)
2898 // qt_fogHeightProperties = (fogHeightMin, fogHeightMax, fogHeightCurve, fogHeightEnabled ? 1.0 : 0.0)
2899 // qt_fogTransmitProperties = (fogTransmitCurve, 0.0, 0.0, fogTransmitEnabled ? 1.0 : 0.0)
2900 if (inRenderProperties.layer.fog.enabled) {
2901 const float fogColor[4] = {
2902 inRenderProperties.layer.fog.color.x(),
2903 inRenderProperties.layer.fog.color.y(),
2904 inRenderProperties.layer.fog.color.z(),
2905 inRenderProperties.layer.fog.density
2906 };
2907 shaders.setUniform(ubufData, "qt_fogColor", fogColor, 4 * sizeof(float), &cui.fogColorIdx);
2908 const float fogDepthProperties[4] = {
2909 inRenderProperties.layer.fog.depthBegin,
2910 inRenderProperties.layer.fog.depthEnd,
2911 inRenderProperties.layer.fog.depthCurve,
2912 inRenderProperties.layer.fog.depthEnabled ? 1.0f : 0.0f
2913 };
2914 shaders.setUniform(ubufData, "qt_fogDepthProperties", fogDepthProperties, 4 * sizeof(float), &cui.fogDepthPropertiesIdx);
2915 const float fogHeightProperties[4] = {
2916 inRenderProperties.layer.fog.heightMin,
2917 inRenderProperties.layer.fog.heightMax,
2918 inRenderProperties.layer.fog.heightCurve,
2919 inRenderProperties.layer.fog.heightEnabled ? 1.0f : 0.0f
2920 };
2921 shaders.setUniform(ubufData, "qt_fogHeightProperties", fogHeightProperties, 4 * sizeof(float), &cui.fogHeightPropertiesIdx);
2922 const float fogTransmitProperties[4] = {
2923 inRenderProperties.layer.fog.transmitCurve,
2924 0.0f,
2925 0.0f,
2926 inRenderProperties.layer.fog.transmitEnabled ? 1.0f : 0.0f
2927 };
2928 shaders.setUniform(ubufData, "qt_fogTransmitProperties", fogTransmitProperties, 4 * sizeof(float), &cui.fogTransmitPropertiesIdx);
2929 }
2930
2931 inPipelineState->lineWidth = materialAdapter->lineWidth();
2932}
2933
2934QT_END_NAMESPACE
2935
2936QList<QByteArrayView> QtQuick3DEditorHelpers::CustomMaterial::reservedArgumentNames()
2937{
2938 return {std::begin(qssg_shader_arg_names), std::end(qssg_shader_arg_names) };;
2939}
void textureCoordVariableName(char(&outString)[TEXCOORD_VAR_LEN], quint8 uvSet)
void textureCoordVaryingName(char(&outString)[TEXCOORD_VAR_LEN], quint8 uvSet)
static void generateImageUVSampler(QSSGMaterialVertexPipeline &vertexGenerator, QSSGStageGeneratorBase &fragmentShader, const QSSGShaderDefaultMaterialKey &key, const ImageStringSet &names, char(&outString)[TEXCOORD_VAR_LEN], quint8 uvSet=0)
static void generateFragmentDefines(QSSGStageGeneratorBase &fragmentShader, const QSSGShaderDefaultMaterialKey &inKey, const QSSGShaderDefaultMaterialKeyProperties &keyProps, QSSGShaderMaterialAdapter *materialAdapter, QSSGShaderLibraryManager &shaderLibraryManager, const QSSGUserShaderAugmentation &shaderAugmentation)
static void generateImageUVCoordinates(QSSGMaterialVertexPipeline &vertexShader, QSSGStageGeneratorBase &fragmentShader, const QSSGShaderDefaultMaterialKey &key, const ImageStringSet &names, bool forceFragmentShader=false, quint32 uvSet=0, bool reuseImageCoords=false, bool useEnvironmentMapping=false)
static constexpr QByteArrayView qssg_shader_arg_names[]
static QByteArray uvTransform(const QByteArray &imageRotations, const QByteArray &imageOffsets)
static void addLocalVariable(QSSGStageGeneratorBase &inGenerator, const QByteArray &inName, const QByteArray &inType)
static void generateFragmentShader(QSSGStageGeneratorBase &fragmentShader, QSSGMaterialVertexPipeline &vertexShader, const QSSGShaderDefaultMaterialKey &inKey, const QSSGShaderDefaultMaterialKeyProperties &keyProps, const QSSGShaderFeatures &featureSet, const QSSGRenderGraphObject &inMaterial, const QSSGUserShaderAugmentation &shaderAugmentation, QSSGShaderLibraryManager &shaderLibraryManager)
static QSSGShaderMaterialAdapter * getMaterialAdapter(const QSSGRenderGraphObject &inMaterial)
static quint32 softShadowQualityToInt(QSSGRenderLight::SoftShadowQuality quality)
PassRequirmentsState(const QSSGShaderDefaultMaterialKey &inKey, const QSSGShaderDefaultMaterialKeyProperties &keyProps, const QSSGShaderFeatures &featureSet, const SamplerState &samplerState, const QSSGUserShaderAugmentation &shaderAugmentation)
QSSGRenderLayer::MaterialDebugMode debugMode
void generateImageUVAndSampler(QSSGRenderableImage::Type imageType, QSSGMaterialVertexPipeline &vertexShader, QSSGStageGeneratorBase &fragmentShader, const QSSGShaderDefaultMaterialKey &key, bool forceFragmentShader=false)
const char * samplerName(QSSGRenderableImage::Type imageType) const
bool uvCoordinatesGenerated[QSSGShaderDefaultMaterialKeyProperties::ImageMapNames::ImageMapCount]
bool uvGenerated(QSSGShaderDefaultMaterialKeyProperties::ImageMapNames imageType) const
SamplerState(const QSSGShaderDefaultMaterialKey &inKey, const QSSGShaderDefaultMaterialKeyProperties &keyProps)
const QSSGShaderDefaultMaterialKey & m_inKey
std::optional< QSSGShaderDefaultMaterialKeyProperties::ImageMapNames > fromType(QSSGRenderableImage::Type type) const
bool hasImage(QSSGRenderableImage::Type type) const
const QSSGShaderDefaultMaterialKeyProperties & m_keyProps
const char * fragCoordsName(QSSGRenderableImage::Type imageType) const