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