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