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