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