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