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