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>
11#include "qssgrendercontextcore.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
44DefineImageStrings(Unknown);
45DefineImageStrings(Diffuse);
46DefineImageStrings(Opacity);
47DefineImageStrings(Specular);
48DefineImageStrings(Emissive);
49DefineImageStrings(Bump);
50DefineImageStrings(SpecularAmountMap);
51DefineImageStrings(Normal);
52DefineImageStrings(Translucency);
53DefineImageStrings(Roughness);
54DefineImageStrings(BaseColor);
55DefineImageStrings(Metalness);
56DefineImageStrings(Occlusion);
57DefineImageStrings(Height);
58DefineImageStrings(Clearcoat);
59DefineImageStrings(ClearcoatRoughness);
60DefineImageStrings(ClearcoatNormal);
61DefineImageStrings(Transmission);
62DefineImageStrings(Thickness);
63
64struct ImageStringSet
65{
66 const char *imageSampler;
67 const char *imageFragCoords;
68 const char *imageFragCoordsTemp;
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
78constexpr ImageStringSet imageStringTable[] {
79 DefineImageStringTableEntry(Unknown),
80 DefineImageStringTableEntry(Diffuse),
81 DefineImageStringTableEntry(Opacity),
82 DefineImageStringTableEntry(Specular),
83 DefineImageStringTableEntry(Emissive),
84 DefineImageStringTableEntry(Bump),
85 DefineImageStringTableEntry(SpecularAmountMap),
86 DefineImageStringTableEntry(Normal),
87 DefineImageStringTableEntry(Translucency),
88 DefineImageStringTableEntry(Roughness),
89 DefineImageStringTableEntry(BaseColor),
90 DefineImageStringTableEntry(Metalness),
91 DefineImageStringTableEntry(Occlusion),
92 DefineImageStringTableEntry(Height),
93 DefineImageStringTableEntry(Clearcoat),
94 DefineImageStringTableEntry(ClearcoatRoughness),
95 DefineImageStringTableEntry(ClearcoatNormal),
96 DefineImageStringTableEntry(Transmission),
97 DefineImageStringTableEntry(Thickness)
98};
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(dst: outString, src: "qt_varTexCoordX", len: 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(dst: outString, src: "qt_texCoordX", len: 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(msg: "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, samplerName: names.imageSampler);
160 fragmentShader.addUniform(name: names.imageSampler, type: "sampler2D");
161 if (!forceFragmentShader) {
162 vertexShader.addUniform(name: names.imageOffsets, type: "vec3");
163 vertexShader.addUniform(name: names.imageRotations, type: "vec4");
164 } else {
165 fragmentShader.addUniform(name: names.imageOffsets, type: "vec3");
166 fragmentShader.addUniform(name: names.imageRotations, type: "vec4");
167 }
168 QByteArray uvTrans = uvTransform(imageRotations: names.imageRotations, imageOffsets: names.imageOffsets);
169 if (image.m_imageNode.m_mappingMode == QSSGRenderImage::MappingModes::Normal) {
170 if (!forceFragmentShader) {
171 vertexShader << uvTrans;
172 vertexShader.addOutgoing(name: names.imageFragCoords, type: "vec2");
173 vertexShader.addFunction(functionName: "getTransformedUVCoords");
174 } else {
175 fragmentShader << uvTrans;
176 fragmentShader.addFunction(functionName: "getTransformedUVCoords");
177 }
178 vertexShader.generateUVCoords(inUVSet: uvSet, inKey: key);
179 if (!forceFragmentShader) {
180 textureCoordVaryingName(outString&: textureCoordName, uvSet);
181 vertexShader << " vec2 " << names.imageFragCoordsTemp << " = qt_getTransformedUVCoords(vec3(" << textureCoordName << ", 1.0), qt_uTransform, qt_vTransform);\n";
182 vertexShader.assignOutput(inVarName: names.imageFragCoords, inVarValueExpr: names.imageFragCoordsTemp);
183 } else {
184 textureCoordVariableName(outString&: 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(name: names.imageOffsets, type: "vec3");
193 fragmentShader.addUniform(name: names.imageRotations, type: "vec4");
194 fragmentShader << uvTrans;
195 vertexShader.generateEnvMapReflection(inKey: key);
196 fragmentShader.addFunction(functionName: "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, samplerName: names.imageSampler);
214 fragmentShader.addUniform(name: names.imageSampler, type: "sampler2D");
215 // NOTE: Actually update the uniform name here
216 textureCoordVariableName(outString, uvSet);
217 vertexGenerator.generateUVCoords(inUVSet: uvSet, inKey: key);
218}
219
220static void outputSpecularEquation(QSSGRenderDefaultMaterial::MaterialSpecularModel inSpecularModel,
221 QSSGStageGeneratorBase &fragmentShader,
222 const QByteArray &inLightDir,
223 const QByteArray &inLightSpecColor)
224{
225 if (inSpecularModel == QSSGRenderDefaultMaterial::MaterialSpecularModel::KGGX) {
226 fragmentShader.addInclude(name: "physGlossyBSDF.glsllib");
227 fragmentShader << " global_specular_light += qt_lightAttenuation * qt_shadow_map_occl * qt_specularAmount"
228 " * qt_kggxGlossyDefaultMtl(qt_world_normal, qt_tangent, -" << inLightDir << ".xyz, qt_view_vector, " << inLightSpecColor << ".rgb, qt_specularTint, qt_roughnessAmount).rgb;\n";
229 } else {
230 fragmentShader.addFunction(functionName: "specularBSDF");
231 fragmentShader << " global_specular_light += qt_lightAttenuation * qt_shadow_map_occl * qt_specularAmount"
232 " * qt_specularBSDF(qt_world_normal, -" << inLightDir << ".xyz, qt_view_vector, " << inLightSpecColor << ".rgb, 2.56 / (qt_roughnessAmount + 0.01)).rgb;\n";
233 }
234}
235
236static void addTranslucencyIrradiance(QSSGStageGeneratorBase &infragmentShader,
237 QSSGRenderableImage *image,
238 const QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames)
239{
240 if (image == nullptr)
241 return;
242
243 infragmentShader.addFunction(functionName: "diffuseReflectionWrapBSDF");
244 infragmentShader << " tmp_light_color = " << lightVarNames.lightColor << ".rgb * (1.0 - qt_metalnessAmount);\n";
245 infragmentShader << " global_diffuse_light.rgb += qt_lightAttenuation * qt_shadow_map_occl * qt_translucent_thickness_exp * qt_diffuseReflectionWrapBSDF(-qt_world_normal, -"
246 << lightVarNames.normalizedDirection << ", tmp_light_color, qt_material_properties2.w).rgb;\n";
247}
248
249using ShadowNameMap = QHash<QPair<qsizetype, quint32>, QSSGMaterialShaderGenerator::ShadowVariableNames>;
250Q_GLOBAL_STATIC(ShadowNameMap, q3ds_shadowMapVariableNames);
251
252static const QSSGMaterialShaderGenerator::ShadowVariableNames& setupShadowMapVariableNames(qsizetype shadowMapIdx, quint32 shadowMapRes)
253{
254 QSSGMaterialShaderGenerator::ShadowVariableNames &names = (*q3ds_shadowMapVariableNames)[{shadowMapIdx, shadowMapRes}];
255 if (names.shadowMapTexture.isEmpty()) {
256 names.shadowCube = QByteArrayLiteral("qt_shadowcube");
257 char buf[std::numeric_limits<qsizetype>::digits10 + 1 + 2 + 1]; // digits10 is one short; 2 for [] and 1 for NUL
258 std::snprintf(s: buf, maxlen: sizeof buf, format: "%lld", qlonglong(shadowMapIdx));
259 names.shadowCube.append(s: buf);
260 names.shadowData = QByteArrayLiteral("ubShadows.shadowData");
261 std::snprintf(s: buf, maxlen: sizeof buf, format: "[%lld]", qlonglong(shadowMapIdx));
262 names.shadowData.append(s: buf);
263 names.shadowMapTexture = QByteArrayLiteral("qt_shadowmap_texture_");
264 std::snprintf(s: buf, maxlen: sizeof buf, format: "%d", shadowMapRes);
265 names.shadowMapTexture.append(s: buf);
266 }
267
268 return names;
269}
270
271// this is for DefaultMaterial only
272static void maybeAddMaterialFresnel(QSSGStageGeneratorBase &fragmentShader,
273 const QSSGShaderDefaultMaterialKeyProperties &keyProps,
274 QSSGDataView<quint32> inKey,
275 bool hasMetalness)
276{
277 if (keyProps.m_fresnelEnabled.getValue(inDataStore: inKey)) {
278 fragmentShader.addInclude(name: "defaultMaterialFresnel.glsllib");
279 fragmentShader << " // Add fresnel ratio\n";
280 if (hasMetalness) { // this won't be hit in practice since DefaultMaterial does not offer metalness as a property
281 fragmentShader << " qt_specularAmount *= qt_defaultMaterialSimpleFresnel(qt_specularBase, qt_metalnessAmount, qt_world_normal, qt_view_vector, "
282 "qt_dielectricSpecular(qt_iOR), qt_material_properties2.x);\n";
283 } else {
284 fragmentShader << " qt_specularAmount *= qt_defaultMaterialSimpleFresnelNoMetalness(qt_world_normal, qt_view_vector, "
285 "qt_dielectricSpecular(qt_iOR), qt_material_properties2.x);\n";
286 }
287 }
288}
289
290static QSSGMaterialShaderGenerator::LightVariableNames setupLightVariableNames(qint32 lightIdx, QSSGRenderLight &inLight)
291{
292 Q_ASSERT(lightIdx > -1);
293 QSSGMaterialShaderGenerator::LightVariableNames names;
294
295 // See funcsampleLightVars.glsllib. Using an instance name (ubLights) is
296 // intentional. The only uniform block that does not have an instance name
297 // is cbMain (the main block with all default and custom material
298 // uniforms). Any other uniform block must have an instance name in order
299 // to avoid trouble with the OpenGL-targeted shaders generated by the
300 // shader pipeline (as those do not use uniform blocks, and in absence of a
301 // block instance name SPIR-Cross generates a struct uniform name based on
302 // whatever SPIR-V ID glslang made up for the variable - this can lead to
303 // clashes between the vertex and fragment shaders if there are blocks with
304 // different names (but no instance names) that are only present in one of
305 // the shaders). For cbMain the issue cannot happen since the exact same
306 // block is present in both shaders. For cbLights it is simple enough to
307 // use the correct prefix right here, so there is no reason not to use an
308 // instance name.
309 QByteArray lightStem = "ubLights.lights";
310 char buf[16];
311 std::snprintf(s: buf, maxlen: 16, format: "[%d].", lightIdx);
312 lightStem.append(s: buf);
313
314 names.lightColor = lightStem;
315 names.lightColor.append(s: "diffuse");
316 names.lightDirection = lightStem;
317 names.lightDirection.append(s: "direction");
318 names.lightSpecularColor = lightStem;
319 names.lightSpecularColor.append(s: "specular");
320 if (inLight.type == QSSGRenderLight::Type::PointLight) {
321 names.lightPos = lightStem;
322 names.lightPos.append(s: "position");
323 names.lightConstantAttenuation = lightStem;
324 names.lightConstantAttenuation.append(s: "constantAttenuation");
325 names.lightLinearAttenuation = lightStem;
326 names.lightLinearAttenuation.append(s: "linearAttenuation");
327 names.lightQuadraticAttenuation = lightStem;
328 names.lightQuadraticAttenuation.append(s: "quadraticAttenuation");
329 } else if (inLight.type == QSSGRenderLight::Type::SpotLight) {
330 names.lightPos = lightStem;
331 names.lightPos.append(s: "position");
332 names.lightConstantAttenuation = lightStem;
333 names.lightConstantAttenuation.append(s: "constantAttenuation");
334 names.lightLinearAttenuation = lightStem;
335 names.lightLinearAttenuation.append(s: "linearAttenuation");
336 names.lightQuadraticAttenuation = lightStem;
337 names.lightQuadraticAttenuation.append(s: "quadraticAttenuation");
338 names.lightConeAngle = lightStem;
339 names.lightConeAngle.append(s: "coneAngle");
340 names.lightInnerConeAngle = lightStem;
341 names.lightInnerConeAngle.append(s: "innerConeAngle");
342 }
343
344 return names;
345}
346
347static void generateShadowMapOcclusion(QSSGStageGeneratorBase &fragmentShader,
348 QSSGMaterialVertexPipeline &vertexShader,
349 quint32 shadowMapIdx,
350 quint32 shadowMapRes,
351 QSSGRenderLight::SoftShadowQuality softShadowQuality,
352 bool inShadowEnabled,
353 QSSGRenderLight::Type inType,
354 const QSSGMaterialShaderGenerator::LightVariableNames &lightVarNames,
355 const QSSGShaderDefaultMaterialKey &inKey)
356{
357 if (inShadowEnabled) {
358 vertexShader.generateWorldPosition(inKey);
359 const auto& names = setupShadowMapVariableNames(shadowMapIdx, shadowMapRes);
360 fragmentShader.addInclude(name: "shadowMapping.glsllib");
361
362 QByteArray sampleFunctionSuffix;
363 switch (softShadowQuality) {
364 case QSSGRenderLight::SoftShadowQuality::Hard:
365 sampleFunctionSuffix += "hard";
366 break;
367 case QSSGRenderLight::SoftShadowQuality::PCF4:
368 sampleFunctionSuffix += "pcf_4";
369 break;
370 case QSSGRenderLight::SoftShadowQuality::PCF8:
371 sampleFunctionSuffix += "pcf_8";
372 break;
373 case QSSGRenderLight::SoftShadowQuality::PCF16:
374 sampleFunctionSuffix += "pcf_16";
375 break;
376 case QSSGRenderLight::SoftShadowQuality::PCF32:
377 sampleFunctionSuffix += "pcf_32";
378 break;
379 case QSSGRenderLight::SoftShadowQuality::PCF64:
380 sampleFunctionSuffix += "pcf_64";
381 break;
382 default:
383 Q_UNREACHABLE();
384 };
385
386 if (inType == QSSGRenderLight::Type::PointLight) {
387 fragmentShader.addUniform(name: names.shadowCube, type: "samplerCube");
388 fragmentShader << " qt_shadow_map_occl = qt_samplePointLight_" << sampleFunctionSuffix << "(" << names.shadowCube << ", " << names.shadowData << ", " << lightVarNames.lightPos << ".xyz, qt_varWorldPos);\n";
389 } else if (inType == QSSGRenderLight::Type::DirectionalLight || inType == QSSGRenderLight::Type::SpotLight) {
390 if (!fragmentShader.m_uniforms.contains(key: names.shadowMapTexture)) {
391 fragmentShader.addUniform(name: names.shadowMapTexture, type: "sampler2DArray");
392 }
393 if (inType == QSSGRenderLight::Type::DirectionalLight) {
394 fragmentShader << " qt_shadow_map_occl = qt_sampleDirectionalLight_" << sampleFunctionSuffix << "(" << names.shadowMapTexture << ", " << names.shadowData << ", qt_zDepthViewSpace, qt_varWorldPos);\n";
395 } else {
396 fragmentShader << " qt_shadow_map_occl = qt_sampleSpotLight_" << sampleFunctionSuffix << "(" << names.shadowMapTexture << ", " << names.shadowData << ", " << lightVarNames.lightPos << ".xyz, qt_varWorldPos, " << lightVarNames.lightDirection << ".xyz, " << lightVarNames.lightConeAngle << ");\n";
397 }
398 } else {
399 Q_UNREACHABLE();
400 }
401 } else {
402 fragmentShader << " qt_shadow_map_occl = 1.0;\n";
403 }
404}
405
406static inline QSSGShaderMaterialAdapter *getMaterialAdapter(const QSSGRenderGraphObject &inMaterial)
407{
408 switch (inMaterial.type) {
409 case QSSGRenderGraphObject::Type::DefaultMaterial:
410 case QSSGRenderGraphObject::Type::PrincipledMaterial:
411 case QSSGRenderGraphObject::Type::SpecularGlossyMaterial:
412 return static_cast<const QSSGRenderDefaultMaterial &>(inMaterial).adapter;
413 case QSSGRenderGraphObject::Type::CustomMaterial:
414 return static_cast<const QSSGRenderCustomMaterial &>(inMaterial).adapter;
415 default:
416 break;
417 }
418 return nullptr;
419}
420
421// NOTE!!!: PLEASE ADD NEW VARS HERE!
422static constexpr QByteArrayView qssg_shader_arg_names[] {
423 { "DIFFUSE" },
424 { "BASE_COLOR" },
425 { "METALNESS" },
426 { "ROUGHNESS" },
427 { "EMISSIVE" },
428 { "SPECULAR_AMOUNT" },
429 { "EMISSIVE_COLOR" },
430 { "LIGHT_COLOR" },
431 { "LIGHT_ATTENUATION" },
432 { "SPOT_FACTOR" },
433 { "SHADOW_CONTRIB" },
434 { "FRESNEL_CONTRIB" },
435 { "TO_LIGHT_DIR" },
436 { "NORMAL" },
437 { "VIEW_VECTOR" },
438 { "TOTAL_AMBIENT_COLOR" },
439 { "COLOR_SUM" },
440 { "BINORMAL" },
441 { "TANGENT" },
442 { "FRESNEL_POWER" },
443 { "INSTANCE_MODEL_MATRIX" },
444 { "INSTANCE_MODELVIEWPROJECTION_MATRIX" },
445 { "UV0" },
446 { "UV1" },
447 { "VERTEX" },
448 { "FRESNEL_SCALE" },
449 { "FRESNEL_BIAS" },
450 { "CLEARCOAT_FRESNEL_POWER" },
451 { "CLEARCOAT_FRESNEL_SCALE" },
452 { "CLEARCOAT_FRESNEL_BIAS" },
453 { "CLEARCOAT_AMOUNT" },
454 { "CLEARCOAT_NORMAL" },
455 { "CLEARCOAT_ROUGHNESS" },
456 { "IOR" },
457 { "TRANSMISSION_FACTOR" },
458 { "THICKNESS_FACTOR" },
459 { "ATTENUATION_COLOR" },
460 { "ATTENUATION_DISTANCE" },
461 { "OCCLUSION_AMOUNT" },
462};
463
464const char *QSSGMaterialShaderGenerator::directionalLightProcessorArgumentList()
465{
466 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";
467}
468
469const char *QSSGMaterialShaderGenerator::pointLightProcessorArgumentList()
470{
471 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";
472}
473
474const char *QSSGMaterialShaderGenerator::spotLightProcessorArgumentList()
475{
476 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";
477}
478
479const char *QSSGMaterialShaderGenerator::ambientLightProcessorArgumentList()
480{
481 return "inout vec3 DIFFUSE, in vec3 TOTAL_AMBIENT_COLOR, in vec3 NORMAL, in vec3 VIEW_VECTOR";
482}
483
484const char *QSSGMaterialShaderGenerator::specularLightProcessorArgumentList()
485{
486 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";
487}
488
489const char *QSSGMaterialShaderGenerator::shadedFragmentMainArgumentList()
490{
491 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";
492}
493
494const char *QSSGMaterialShaderGenerator::postProcessorArgumentList()
495{
496 return "inout vec4 COLOR_SUM, in vec4 DIFFUSE, in vec3 SPECULAR, in vec3 EMISSIVE, in vec2 UV0, in vec2 UV1";
497}
498
499const char *QSSGMaterialShaderGenerator::iblProbeProcessorArgumentList()
500{
501 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";
502}
503
504const char *QSSGMaterialShaderGenerator::vertexMainArgumentList()
505{
506 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";
507}
508
509const char *QSSGMaterialShaderGenerator::vertexInstancedMainArgumentList()
510{
511 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";
512}
513
514#define MAX_MORPH_TARGET 8
515
516static bool hasCustomFunction(const QByteArray &funcName,
517 QSSGShaderMaterialAdapter *materialAdapter,
518 QSSGShaderLibraryManager &shaderLibraryManager)
519{
520 return materialAdapter->hasCustomShaderFunction(shaderType: QSSGShaderCache::ShaderType::Fragment, funcName, shaderLibraryManager);
521}
522
523static void generateTempLightColor(QSSGStageGeneratorBase &fragmentShader,
524 QSSGMaterialShaderGenerator::LightVariableNames& lightVarNames,
525 QSSGShaderMaterialAdapter *materialAdapter)
526{
527 if (materialAdapter->isSpecularGlossy())
528 fragmentShader << " tmp_light_color = " << lightVarNames.lightColor << ".rgb;\n";
529 else
530 fragmentShader << " tmp_light_color = " << lightVarNames.lightColor << ".rgb * (1.0 - qt_metalnessAmount);\n";
531}
532
533static void handleSpecularLight(QSSGStageGeneratorBase &fragmentShader,
534 QSSGMaterialShaderGenerator::LightVariableNames& lightVarNames,
535 QSSGShaderMaterialAdapter *materialAdapter,
536 QSSGShaderLibraryManager &shaderLibraryManager,
537 bool usesSharedVar,
538 bool hasCustomFrag,
539 bool specularLightingEnabled,
540 bool enableClearcoat,
541 bool enableTransmission,
542 bool useNormalizedDirection)
543{
544 QByteArray directionToUse = useNormalizedDirection ? lightVarNames.normalizedDirection : lightVarNames.lightDirection;
545
546 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_specularLightProcessor"), materialAdapter, shaderLibraryManager))
547 {
548 // SPECULAR, LIGHT_COLOR, LIGHT_ATTENUATION, SHADOW_CONTRIB, FRESNEL_CONTRIB, TO_LIGHT_DIR, NORMAL, BASE_COLOR, METALNESS, ROUGHNESS, SPECULAR_AMOUNT, VIEW_VECTOR(, SHARED)
549 fragmentShader << " qt_specularLightProcessor(global_specular_light, " << lightVarNames.lightSpecularColor << ".rgb, qt_lightAttenuation, qt_shadow_map_occl, "
550 << "qt_specularAmount, -" << directionToUse << ".xyz, qt_world_normal, qt_customBaseColor, "
551 << "qt_metalnessAmount, qt_roughnessAmount, qt_customSpecularAmount, qt_view_vector";
552 if (usesSharedVar)
553 fragmentShader << ", qt_customShared);\n";
554 else
555 fragmentShader << ");\n";
556 }
557 else
558 {
559 if (specularLightingEnabled)
560 {
561 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy())
562 {
563 // Principled materials (and Custom without a specular processor function) always use GGX SpecularModel
564 fragmentShader.addFunction(functionName: "specularGGXBSDF");
565 fragmentShader << " global_specular_light += qt_lightAttenuation * qt_shadow_map_occl * qt_specularTint"
566 " * qt_specularGGXBSDF(qt_world_normal, -"
567 << directionToUse << ".xyz, qt_view_vector, "
568 << lightVarNames.lightSpecularColor << ".rgb, qt_f0, qt_f90, qt_roughnessAmount).rgb;\n";
569 }
570 else
571 {
572 outputSpecularEquation(inSpecularModel: materialAdapter->specularModel(), fragmentShader, inLightDir: directionToUse, inLightSpecColor: lightVarNames.lightSpecularColor);
573 }
574
575 if (enableClearcoat)
576 {
577 fragmentShader.addFunction(functionName: "specularGGXBSDF");
578 fragmentShader << " qt_global_clearcoat += qt_lightAttenuation * qt_shadow_map_occl"
579 " * qt_specularGGXBSDF(qt_clearcoatNormal, -"
580 << directionToUse << ".xyz, qt_view_vector, "
581 << lightVarNames.lightSpecularColor << ".rgb, qt_clearcoatF0, qt_clearcoatF90, qt_clearcoatRoughness).rgb;\n";
582 }
583
584 if (enableTransmission)
585 {
586 fragmentShader << " {\n";
587 fragmentShader << " vec3 transmissionRay = qt_getVolumeTransmissionRay(qt_world_normal, qt_view_vector, qt_thicknessFactor, qt_iOR);\n";
588 fragmentShader << " vec3 pointToLight = -" << directionToUse << ".xyz;\n";
589 fragmentShader << " pointToLight -= transmissionRay;\n";
590 fragmentShader << " vec3 l = normalize(pointToLight);\n";
591 fragmentShader << " vec3 intensity = vec3(1.0);\n"; // Directional light is always 1.0
592 fragmentShader << " vec3 transmittedLight = intensity * qt_getPunctualRadianceTransmission(qt_world_normal, "
593 "qt_view_vector, l, qt_roughnessAmount, qt_f0, qt_f90, qt_diffuseColor.rgb, qt_iOR);\n";
594 fragmentShader << " transmittedLight = qt_applyVolumeAttenuation(transmittedLight, length(transmissionRay), "
595 "qt_attenuationColor, qt_attenuationDistance);\n";
596 fragmentShader << " qt_global_transmission += qt_transmissionFactor * transmittedLight;\n";
597 fragmentShader << " }\n";
598 }
599 }
600 }
601}
602
603static void handleDirectionalLight(QSSGStageGeneratorBase &fragmentShader,
604 QSSGMaterialShaderGenerator::LightVariableNames& lightVarNames,
605 bool usesSharedVar,
606 bool hasCustomFrag,
607 QSSGShaderMaterialAdapter *materialAdapter,
608 QSSGShaderLibraryManager &shaderLibraryManager,
609 bool specularLightingEnabled,
610 bool enableClearcoat,
611 bool enableTransmission)
612{
613 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_directionalLightProcessor"), materialAdapter, shaderLibraryManager)) {
614 // DIFFUSE, LIGHT_COLOR, SHADOW_CONTRIB, TO_LIGHT_DIR, NORMAL, BASE_COLOR, METALNESS, ROUGHNESS, VIEW_VECTOR(, SHARED)
615 fragmentShader << " qt_directionalLightProcessor(global_diffuse_light.rgb, tmp_light_color, qt_shadow_map_occl, -"
616 << lightVarNames.lightDirection << ".xyz, qt_world_normal, qt_customBaseColor, "
617 << "qt_metalnessAmount, qt_roughnessAmount, qt_view_vector";
618 if (usesSharedVar)
619 fragmentShader << ", qt_customShared);\n";
620 else
621 fragmentShader << ");\n";
622 } else {
623 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
624 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * qt_shadow_map_occl * "
625 << "qt_diffuseBurleyBSDF(qt_world_normal, -" << lightVarNames.lightDirection << ".xyz, "
626 << "qt_view_vector, tmp_light_color, qt_roughnessAmount).rgb;\n";
627 } else {
628 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * qt_shadow_map_occl * qt_diffuseReflectionBSDF(qt_world_normal, -"
629 << lightVarNames.lightDirection << ".xyz, tmp_light_color).rgb;\n";
630 }
631 }
632
633 handleSpecularLight(fragmentShader,
634 lightVarNames,
635 materialAdapter,
636 shaderLibraryManager,
637 usesSharedVar,
638 hasCustomFrag,
639 specularLightingEnabled,
640 enableClearcoat,
641 enableTransmission,
642 useNormalizedDirection: false);
643}
644
645static void generateDirections(QSSGStageGeneratorBase &fragmentShader,
646 QSSGMaterialShaderGenerator::LightVariableNames& lightVarNames,
647 const QByteArray& lightVarPrefix,
648 QSSGMaterialVertexPipeline &vertexShader,
649 const QSSGShaderDefaultMaterialKey &inKey)
650{
651 vertexShader.generateWorldPosition(inKey);
652
653 lightVarNames.relativeDirection = lightVarPrefix;
654 lightVarNames.relativeDirection.append(s: "relativeDirection");
655
656 lightVarNames.normalizedDirection = lightVarNames.relativeDirection;
657 lightVarNames.normalizedDirection.append(s: "_normalized");
658
659 lightVarNames.relativeDistance = lightVarPrefix;
660 lightVarNames.relativeDistance.append(s: "distance");
661
662 fragmentShader << " vec3 " << lightVarNames.relativeDirection << " = qt_varWorldPos - " << lightVarNames.lightPos << ".xyz;\n"
663 << " float " << lightVarNames.relativeDistance << " = length(" << lightVarNames.relativeDirection << ");\n"
664 << " vec3 " << lightVarNames.normalizedDirection << " = " << lightVarNames.relativeDirection << " / " << lightVarNames.relativeDistance << ";\n";
665
666}
667
668static void handlePointLight(QSSGStageGeneratorBase &fragmentShader,
669 QSSGMaterialShaderGenerator::LightVariableNames& lightVarNames,
670 QSSGShaderMaterialAdapter *materialAdapter,
671 QSSGShaderLibraryManager &shaderLibraryManager,
672 bool usesSharedVar,
673 bool hasCustomFrag,
674 bool specularLightingEnabled,
675 bool enableClearcoat,
676 bool enableTransmission)
677{
678 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_pointLightProcessor"), materialAdapter, shaderLibraryManager)) {
679 // DIFFUSE, LIGHT_COLOR, LIGHT_ATTENUATION, SHADOW_CONTRIB, TO_LIGHT_DIR, NORMAL, BASE_COLOR, METALNESS, ROUGHNESS, VIEW_VECTOR(, SHARED)
680 fragmentShader << " qt_pointLightProcessor(global_diffuse_light.rgb, tmp_light_color, qt_lightAttenuation, qt_shadow_map_occl, -"
681 << lightVarNames.normalizedDirection << ".xyz, qt_world_normal, qt_customBaseColor, "
682 << "qt_metalnessAmount, qt_roughnessAmount, qt_view_vector";
683 if (usesSharedVar)
684 fragmentShader << ", qt_customShared);\n";
685 else
686 fragmentShader << ");\n";
687 } else {
688 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
689 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * qt_lightAttenuation * qt_shadow_map_occl * "
690 << "qt_diffuseBurleyBSDF(qt_world_normal, -" << lightVarNames.normalizedDirection << ".xyz, qt_view_vector, "
691 << "tmp_light_color, qt_roughnessAmount).rgb;\n";
692 } else {
693 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * qt_lightAttenuation * qt_shadow_map_occl * "
694 << "qt_diffuseReflectionBSDF(qt_world_normal, -" << lightVarNames.normalizedDirection << ".xyz, tmp_light_color).rgb;\n";
695 }
696 }
697
698 handleSpecularLight(fragmentShader,
699 lightVarNames,
700 materialAdapter,
701 shaderLibraryManager,
702 usesSharedVar,
703 hasCustomFrag,
704 specularLightingEnabled,
705 enableClearcoat,
706 enableTransmission,
707 useNormalizedDirection: true);
708}
709
710static void handleSpotLight(QSSGStageGeneratorBase &fragmentShader,
711 QSSGMaterialShaderGenerator::LightVariableNames& lightVarNames,
712 const QByteArray& lightVarPrefix,
713 QSSGShaderMaterialAdapter *materialAdapter,
714 QSSGShaderLibraryManager &shaderLibraryManager,
715 bool usesSharedVar,
716 bool hasCustomFrag,
717 bool specularLightingEnabled,
718 bool enableClearcoat,
719 bool enableTransmission)
720{
721 lightVarNames.spotAngle = lightVarPrefix;
722 lightVarNames.spotAngle.append(s: "spotAngle");
723
724 fragmentShader << " float " << lightVarNames.spotAngle << " = dot(" << lightVarNames.normalizedDirection
725 << ", normalize(vec3(" << lightVarNames.lightDirection << ")));\n";
726 fragmentShader << " if (" << lightVarNames.spotAngle << " > " << lightVarNames.lightConeAngle << ") {\n";
727 fragmentShader << " float spotFactor = smoothstep(" << lightVarNames.lightConeAngle
728 << ", " << lightVarNames.lightInnerConeAngle << ", " << lightVarNames.spotAngle
729 << ");\n";
730
731 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_spotLightProcessor"), materialAdapter, shaderLibraryManager)) {
732 // DIFFUSE, LIGHT_COLOR, LIGHT_ATTENUATION, SPOT_FACTOR, SHADOW_CONTRIB, TO_LIGHT_DIR, NORMAL, BASE_COLOR, METALNESS, ROUGHNESS, VIEW_VECTOR(, SHARED)
733 fragmentShader << " qt_spotLightProcessor(global_diffuse_light.rgb, tmp_light_color, qt_lightAttenuation, spotFactor, qt_shadow_map_occl, -"
734 << lightVarNames.normalizedDirection << ".xyz, qt_world_normal, qt_customBaseColor, "
735 << "qt_metalnessAmount, qt_roughnessAmount, qt_view_vector";
736 if (usesSharedVar)
737 fragmentShader << ", qt_customShared);\n";
738 else
739 fragmentShader << ");\n";
740 } else {
741 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
742 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * spotFactor * qt_lightAttenuation * qt_shadow_map_occl * "
743 << "qt_diffuseBurleyBSDF(qt_world_normal, -" << lightVarNames.normalizedDirection << ".xyz, qt_view_vector, "
744 << "tmp_light_color, qt_roughnessAmount).rgb;\n";
745 } else {
746 fragmentShader << " global_diffuse_light.rgb += qt_diffuseColor.rgb * spotFactor * qt_lightAttenuation * qt_shadow_map_occl * "
747 << "qt_diffuseReflectionBSDF(qt_world_normal, -" << lightVarNames.normalizedDirection << ".xyz, tmp_light_color).rgb;\n";
748 }
749 }
750
751 // spotFactor is multipled to qt_lightAttenuation and have an effect on the specularLight.
752 fragmentShader << " qt_lightAttenuation *= spotFactor;\n";
753
754 handleSpecularLight(fragmentShader,
755 lightVarNames,
756 materialAdapter,
757 shaderLibraryManager,
758 usesSharedVar,
759 hasCustomFrag,
760 specularLightingEnabled,
761 enableClearcoat,
762 enableTransmission,
763 useNormalizedDirection: true);
764
765 fragmentShader << " }\n";
766}
767
768static void calculatePointLightAttenuation(QSSGStageGeneratorBase &fragmentShader,
769 QSSGMaterialShaderGenerator::LightVariableNames& lightVarNames)
770{
771 fragmentShader.addFunction(functionName: "calculatePointLightAttenuation");
772
773 fragmentShader << " qt_lightAttenuation = qt_calculatePointLightAttenuation(vec3("
774 << lightVarNames.lightConstantAttenuation << ", " << lightVarNames.lightLinearAttenuation << ", "
775 << lightVarNames.lightQuadraticAttenuation << "), " << lightVarNames.relativeDistance << ");\n";
776}
777
778static void generateMainLightCalculation(QSSGStageGeneratorBase &fragmentShader,
779 QSSGMaterialVertexPipeline &vertexShader,
780 const QSSGShaderDefaultMaterialKey &inKey,
781 const QSSGRenderGraphObject &inMaterial,
782 const QSSGShaderLightListView &lights,
783 QSSGShaderLibraryManager &shaderLibraryManager,
784 QSSGRenderableImage *translucencyImage,
785 bool hasCustomFrag,
786 bool usesSharedVar,
787 bool enableLightmap,
788 bool enableShadowMaps,
789 bool specularLightingEnabled,
790 bool enableClearcoat,
791 bool enableTransmission)
792{
793 QSSGShaderMaterialAdapter *materialAdapter = getMaterialAdapter(inMaterial);
794
795 // Iterate through all lights
796 Q_ASSERT(lights.size() < INT32_MAX);
797
798 int shadowMapCount = 0;
799 bool hasAddedZDepthViewSpaceVariable = false;
800
801 for (qint32 lightIdx = 0; lightIdx < lights.size(); ++lightIdx) {
802 auto &shaderLight = lights[lightIdx];
803 QSSGRenderLight *lightNode = shaderLight.light;
804
805 if (enableLightmap && lightNode->m_fullyBaked)
806 continue;
807
808 auto lightVarNames = setupLightVariableNames(lightIdx, inLight&: *lightNode);
809
810 const bool isDirectional = lightNode->type == QSSGRenderLight::Type::DirectionalLight;
811 const bool isSpot = lightNode->type == QSSGRenderLight::Type::SpotLight;
812 bool castsShadow = enableShadowMaps && lightNode->m_castShadow && shadowMapCount < QSSG_MAX_NUM_SHADOW_MAPS;
813
814 fragmentShader.append(data: "");
815 char lightIdxStr[11];
816 std::snprintf(s: lightIdxStr, maxlen: 11, format: "%d", lightIdx);
817
818 QByteArray lightVarPrefix = "light";
819 lightVarPrefix.append(s: lightIdxStr);
820
821 if (isDirectional && !hasAddedZDepthViewSpaceVariable) {
822 fragmentShader.append(data: "#if QSHADER_VIEW_COUNT >= 2");
823 fragmentShader.append(data: " float qt_zDepthViewSpace = abs((qt_viewMatrix[0] * vec4(qt_varWorldPos, 1.0)).z);");
824 fragmentShader.append(data: "#else");
825 fragmentShader.append(data: " float qt_zDepthViewSpace = abs((qt_viewMatrix * vec4(qt_varWorldPos, 1.0)).z);");
826 fragmentShader.append(data: "#endif");
827 hasAddedZDepthViewSpaceVariable = true;
828 }
829
830 fragmentShader << " //Light " << lightIdxStr << (isDirectional ? " [directional]" : isSpot ? " [spot]" : " [point]") << "\n";
831
832 lightVarPrefix.append(s: "_");
833
834 generateShadowMapOcclusion(fragmentShader, vertexShader, shadowMapIdx: shadowMapCount, shadowMapRes: lightNode->m_shadowMapRes, softShadowQuality: lightNode->m_softShadowQuality, inShadowEnabled: castsShadow, inType: lightNode->type, lightVarNames, inKey);
835
836 generateTempLightColor(fragmentShader, lightVarNames, materialAdapter);
837
838 if (isDirectional) {
839 handleDirectionalLight(fragmentShader,
840 lightVarNames,
841 usesSharedVar,
842 hasCustomFrag,
843 materialAdapter,
844 shaderLibraryManager,
845 specularLightingEnabled,
846 enableClearcoat,
847 enableTransmission);
848 } else {
849 generateDirections(fragmentShader, lightVarNames, lightVarPrefix, vertexShader, inKey);
850
851 calculatePointLightAttenuation(fragmentShader, lightVarNames);
852
853 addTranslucencyIrradiance(infragmentShader&: fragmentShader, image: translucencyImage, lightVarNames);
854
855 if (isSpot) {
856 handleSpotLight(fragmentShader,
857 lightVarNames,
858 lightVarPrefix,
859 materialAdapter,
860 shaderLibraryManager,
861 usesSharedVar,
862 hasCustomFrag,
863 specularLightingEnabled,
864 enableClearcoat,
865 enableTransmission);
866 } else {
867 handlePointLight(fragmentShader,
868 lightVarNames,
869 materialAdapter,
870 shaderLibraryManager,
871 usesSharedVar,
872 hasCustomFrag,
873 specularLightingEnabled,
874 enableClearcoat,
875 enableTransmission);
876 }
877 }
878
879 if (castsShadow)
880 ++shadowMapCount;
881 }
882
883 fragmentShader.append(data: "");
884}
885
886static void generateFragmentShader(QSSGStageGeneratorBase &fragmentShader,
887 QSSGMaterialVertexPipeline &vertexShader,
888 const QSSGShaderDefaultMaterialKey &inKey,
889 const QSSGShaderDefaultMaterialKeyProperties &keyProps,
890 const QSSGShaderFeatures &featureSet,
891 const QSSGRenderGraphObject &inMaterial,
892 const QSSGShaderLightListView &lights,
893 QSSGRenderableImage *firstImage,
894 QSSGShaderLibraryManager &shaderLibraryManager)
895{
896 QSSGShaderMaterialAdapter *materialAdapter = getMaterialAdapter(inMaterial);
897 auto hasCustomFunction = [&shaderLibraryManager, materialAdapter](const QByteArray &funcName) {
898 return materialAdapter->hasCustomShaderFunction(shaderType: QSSGShaderCache::ShaderType::Fragment,
899 funcName,
900 shaderLibraryManager);
901 };
902
903 bool metalnessEnabled = materialAdapter->isMetalnessEnabled(); // always true for Custom, true if > 0 with Principled
904
905 // alwayas true for Custom,
906 // true if vertexColorsEnabled, usesInstancing and blendParticles for others
907 bool vertexColorsEnabled = materialAdapter->isVertexColorsEnabled()
908 || materialAdapter->isVertexColorsMaskEnabled()
909 || keyProps.m_usesInstancing.getValue(inDataStore: inKey)
910 || keyProps.m_blendParticles.getValue(inDataStore: inKey);
911
912 bool hasLighting = materialAdapter->hasLighting();
913 bool isDoubleSided = keyProps.m_isDoubleSided.getValue(inDataStore: inKey);
914 bool hasImage = firstImage != nullptr;
915
916 bool hasIblProbe = keyProps.m_hasIbl.getValue(inDataStore: inKey);
917 bool specularLightingEnabled = metalnessEnabled || materialAdapter->isSpecularEnabled() || hasIblProbe; // always true for Custom, depends for others
918 bool specularAAEnabled = keyProps.m_specularAAEnabled.getValue(inDataStore: inKey);
919 quint32 numMorphTargets = keyProps.m_targetCount.getValue(inDataStore: inKey);
920 // Pull the bump out as
921 QSSGRenderableImage *bumpImage = nullptr;
922 quint32 imageIdx = 0;
923 QSSGRenderableImage *specularAmountImage = nullptr;
924 QSSGRenderableImage *roughnessImage = nullptr;
925 QSSGRenderableImage *metalnessImage = nullptr;
926 QSSGRenderableImage *occlusionImage = nullptr;
927 // normal mapping
928 QSSGRenderableImage *normalImage = nullptr;
929 // translucency map
930 QSSGRenderableImage *translucencyImage = nullptr;
931 // opacity map
932 QSSGRenderableImage *opacityImage = nullptr;
933 // height map
934 QSSGRenderableImage *heightImage = nullptr;
935 // clearcoat maps
936 QSSGRenderableImage *clearcoatImage = nullptr;
937 QSSGRenderableImage *clearcoatRoughnessImage = nullptr;
938 QSSGRenderableImage *clearcoatNormalImage = nullptr;
939 // transmission map
940 QSSGRenderableImage *transmissionImage = nullptr;
941 // thickness
942 QSSGRenderableImage *thicknessImage = nullptr;
943
944 QSSGRenderableImage *baseImage = nullptr;
945
946 // Use shared texcoord when transforms are identity
947 QVector<QSSGRenderableImage *> identityImages;
948 char imageFragCoords[TEXCOORD_VAR_LEN];
949
950 auto channelStr = [](const QSSGShaderKeyTextureChannel &chProp, const QSSGShaderDefaultMaterialKey &inKey) -> QByteArray {
951 QByteArray ret;
952 switch (chProp.getTextureChannel(inKeySet: inKey)) {
953 case QSSGShaderKeyTextureChannel::R:
954 ret.append(s: ".r");
955 break;
956 case QSSGShaderKeyTextureChannel::G:
957 ret.append(s: ".g");
958 break;
959 case QSSGShaderKeyTextureChannel::B:
960 ret.append(s: ".b");
961 break;
962 case QSSGShaderKeyTextureChannel::A:
963 ret.append(s: ".a");
964 break;
965 }
966 return ret;
967 };
968
969 auto maskVariableByVertexColorChannel = [&fragmentShader, materialAdapter]( const QByteArray &maskVariable
970 , const QSSGRenderDefaultMaterial::VertexColorMask &maskEnum ){
971 if (materialAdapter->isVertexColorsMaskEnabled()) {
972 if ( materialAdapter->vertexColorRedMask() & maskEnum )
973 fragmentShader << " " << maskVariable << " *= qt_vertColorMask.r;\n";
974 else if ( materialAdapter->vertexColorGreenMask() & maskEnum )
975 fragmentShader << " " << maskVariable << " *= qt_vertColorMask.g;\n";
976 else if ( materialAdapter->vertexColorBlueMask() & maskEnum )
977 fragmentShader << " " << maskVariable << " *= qt_vertColorMask.b;\n";
978 else if ( materialAdapter->vertexColorAlphaMask() & maskEnum )
979 fragmentShader << " " << maskVariable << " *= qt_vertColorMask.a;\n";
980 }
981 };
982
983 for (QSSGRenderableImage *img = firstImage; img != nullptr; img = img->m_nextImage, ++imageIdx) {
984 if (img->m_imageNode.isImageTransformIdentity())
985 identityImages.push_back(t: img);
986 if (img->m_mapType == QSSGRenderableImage::Type::BaseColor || img->m_mapType == QSSGRenderableImage::Type::Diffuse) {
987 baseImage = img;
988 } else if (img->m_mapType == QSSGRenderableImage::Type::Bump) {
989 bumpImage = img;
990 } else if (img->m_mapType == QSSGRenderableImage::Type::SpecularAmountMap) {
991 specularAmountImage = img;
992 } else if (img->m_mapType == QSSGRenderableImage::Type::Roughness) {
993 roughnessImage = img;
994 } else if (img->m_mapType == QSSGRenderableImage::Type::Metalness) {
995 metalnessImage = img;
996 } else if (img->m_mapType == QSSGRenderableImage::Type::Occlusion) {
997 occlusionImage = img;
998 } else if (img->m_mapType == QSSGRenderableImage::Type::Normal) {
999 normalImage = img;
1000 } else if (img->m_mapType == QSSGRenderableImage::Type::Translucency) {
1001 translucencyImage = img;
1002 } else if (img->m_mapType == QSSGRenderableImage::Type::Opacity) {
1003 opacityImage = img;
1004 } else if (img->m_mapType == QSSGRenderableImage::Type::Height) {
1005 heightImage = img;
1006 } else if (img->m_mapType == QSSGRenderableImage::Type::Clearcoat) {
1007 clearcoatImage = img;
1008 } else if (img->m_mapType == QSSGRenderableImage::Type::ClearcoatRoughness) {
1009 clearcoatRoughnessImage = img;
1010 } else if (img->m_mapType == QSSGRenderableImage::Type::ClearcoatNormal) {
1011 clearcoatNormalImage = img;
1012 } else if (img->m_mapType == QSSGRenderableImage::Type::Transmission) {
1013 transmissionImage = img;
1014 } else if (img->m_mapType == QSSGRenderableImage::Type::Thickness) {
1015 thicknessImage = img;
1016 }
1017 }
1018
1019 const bool isDepthPass = featureSet.isSet(feature: QSSGShaderFeatures::Feature::DepthPass);
1020 const bool isOrthoShadowPass = featureSet.isSet(feature: QSSGShaderFeatures::Feature::OrthoShadowPass);
1021 const bool isPerspectiveShadowPass = featureSet.isSet(feature: QSSGShaderFeatures::Feature::PerspectiveShadowPass);
1022 const bool isOpaqueDepthPrePass = featureSet.isSet(feature: QSSGShaderFeatures::Feature::OpaqueDepthPrePass);
1023 const bool hasIblOrientation = featureSet.isSet(feature: QSSGShaderFeatures::Feature::IblOrientation);
1024 bool enableShadowMaps = featureSet.isSet(feature: QSSGShaderFeatures::Feature::Ssm);
1025 bool enableSSAO = featureSet.isSet(feature: QSSGShaderFeatures::Feature::Ssao);
1026 bool enableLightmap = featureSet.isSet(feature: QSSGShaderFeatures::Feature::Lightmap);
1027 bool hasReflectionProbe = featureSet.isSet(feature: QSSGShaderFeatures::Feature::ReflectionProbe);
1028 bool enableBumpNormal = normalImage || bumpImage;
1029 bool genBumpNormalImageCoords = false;
1030 bool enableParallaxMapping = heightImage != nullptr;
1031 const bool enableClearcoat = materialAdapter->isClearcoatEnabled();
1032 const bool enableTransmission = materialAdapter->isTransmissionEnabled();
1033 const bool enableFresnelScaleBias = materialAdapter->isFresnelScaleBiasEnabled();
1034 const bool enableClearcoatFresnelScaleBias = materialAdapter->isClearcoatFresnelScaleBiasEnabled();
1035
1036 specularLightingEnabled |= specularAmountImage != nullptr;
1037 specularLightingEnabled |= hasReflectionProbe;
1038
1039 const bool hasCustomVert = materialAdapter->hasCustomShaderSnippet(type: QSSGShaderCache::ShaderType::Vertex);
1040 auto debugMode = QSSGRenderLayer::MaterialDebugMode(keyProps.m_debugMode.getValue(inDataStore: inKey));
1041 const bool enableFog = keyProps.m_fogEnabled.getValue(inDataStore: inKey);
1042
1043 const int viewCount = featureSet.isSet(feature: QSSGShaderFeatures::Feature::DisableMultiView)
1044 ? 1 : keyProps.m_viewCount.getValue(inDataStore: inKey);
1045
1046 // Morphing
1047 if (numMorphTargets > 0 || hasCustomVert) {
1048 vertexShader.addDefinition(QByteArrayLiteral("QT_MORPH_MAX_COUNT"),
1049 value: QByteArray::number(numMorphTargets));
1050 quint8 offset;
1051 if ((offset = keyProps.m_targetPositionOffset.getValue(inDataStore: inKey)) < UINT8_MAX) {
1052 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_POSITION_OFFSET"),
1053 value: QByteArray::number(offset));
1054 }
1055 if ((offset = keyProps.m_targetNormalOffset.getValue(inDataStore: inKey)) < UINT8_MAX) {
1056 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_NORMAL_OFFSET"),
1057 value: QByteArray::number(offset));
1058 }
1059 if ((offset = keyProps.m_targetTangentOffset.getValue(inDataStore: inKey)) < UINT8_MAX) {
1060 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_TANGENT_OFFSET"),
1061 value: QByteArray::number(offset));
1062 }
1063 if ((offset = keyProps.m_targetBinormalOffset.getValue(inDataStore: inKey)) < UINT8_MAX) {
1064 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_BINORMAL_OFFSET"),
1065 value: QByteArray::number(offset));
1066 }
1067 if ((offset = keyProps.m_targetTexCoord0Offset.getValue(inDataStore: inKey)) < UINT8_MAX) {
1068 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_TEX0_OFFSET"),
1069 value: QByteArray::number(offset));
1070 }
1071 if ((offset = keyProps.m_targetTexCoord1Offset.getValue(inDataStore: inKey)) < UINT8_MAX) {
1072 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_TEX1_OFFSET"),
1073 value: QByteArray::number(offset));
1074 }
1075 if ((offset = keyProps.m_targetColorOffset.getValue(inDataStore: inKey)) < UINT8_MAX) {
1076 vertexShader.addDefinition(QByteArrayLiteral("QT_TARGET_COLOR_OFFSET"),
1077 value: QByteArray::number(offset));
1078 }
1079 }
1080
1081 bool includeCustomFragmentMain = true;
1082 if (isDepthPass || isOrthoShadowPass || isPerspectiveShadowPass) {
1083 hasLighting = false;
1084 enableSSAO = false;
1085 enableShadowMaps = false;
1086 enableLightmap = false;
1087
1088 metalnessEnabled = false;
1089 specularLightingEnabled = false;
1090
1091 if (!isOpaqueDepthPrePass) {
1092 vertexColorsEnabled = false;
1093 baseImage = nullptr;
1094 includeCustomFragmentMain = false;
1095 }
1096 }
1097
1098 bool includeSSAOVars = enableSSAO || enableShadowMaps;
1099
1100 vertexShader.beginFragmentGeneration(shaderLibraryManager);
1101
1102 // Unshaded custom materials need no code in main (apart from calling qt_customMain)
1103 const bool hasCustomFrag = materialAdapter->hasCustomShaderSnippet(type: QSSGShaderCache::ShaderType::Fragment);
1104 const bool usesSharedVar = materialAdapter->usesSharedVariables();
1105 if (hasCustomFrag && materialAdapter->isUnshaded())
1106 return;
1107
1108 // hasCustomFrag == Shaded custom material from this point on, for Unshaded we returned above
1109
1110 // The fragment or vertex shaders may not use the material_properties or diffuse
1111 // uniforms in all cases but it is simpler to just add them and let the linker strip them.
1112 fragmentShader.addUniform(name: "qt_material_emissive_color", type: "vec3");
1113 fragmentShader.addUniform(name: "qt_material_base_color", type: "vec4");
1114 fragmentShader.addUniform(name: "qt_material_properties", type: "vec4");
1115 fragmentShader.addUniform(name: "qt_material_properties2", type: "vec4");
1116 fragmentShader.addUniform(name: "qt_material_properties3", type: "vec4");
1117 if (enableParallaxMapping || enableTransmission)
1118 fragmentShader.addUniform(name: "qt_material_properties4", type: "vec4");
1119 if (!hasCustomFrag) {
1120 if (enableTransmission) {
1121 fragmentShader.addUniform(name: "qt_material_attenuation", type: "vec4");
1122 fragmentShader.addUniform(name: "qt_material_thickness", type: "float");
1123 }
1124 if (enableFresnelScaleBias || enableClearcoatFresnelScaleBias)
1125 fragmentShader.addUniform(name: "qt_material_properties5", type: "vec4");
1126 fragmentShader.addUniform(name: "qt_material_clearcoat_normal_strength", type: "float");
1127 fragmentShader.addUniform(name: "qt_material_clearcoat_fresnel_power", type: "float");
1128 }
1129
1130 if (vertexColorsEnabled) {
1131 vertexShader.generateVertexColor(inKey);
1132 }else {
1133 fragmentShader.append(data: " vec4 qt_vertColorMask = vec4(1.0);");
1134 fragmentShader.append(data: " vec4 qt_vertColor = vec4(1.0);");
1135 }
1136
1137 if (hasImage && ((!isDepthPass && !isOrthoShadowPass && !isPerspectiveShadowPass) || isOpaqueDepthPrePass)) {
1138 fragmentShader.append(data: " vec3 qt_uTransform;");
1139 fragmentShader.append(data: " vec3 qt_vTransform;");
1140 }
1141
1142 if (hasLighting || hasCustomFrag) {
1143 // Do not move these three. These varyings are exposed to custom material shaders too.
1144 vertexShader.generateViewVector(inKey);
1145 if (keyProps.m_usesProjectionMatrix.getValue(inDataStore: inKey)) {
1146 if (viewCount >= 2)
1147 fragmentShader.addUniformArray(name: "qt_projectionMatrix", type: "mat4", size: viewCount);
1148 else
1149 fragmentShader.addUniform(name: "qt_projectionMatrix", type: "mat4");
1150 }
1151 if (keyProps.m_usesInverseProjectionMatrix.getValue(inDataStore: inKey)) {
1152 if (viewCount >= 2)
1153 fragmentShader.addUniformArray(name: "qt_inverseProjectionMatrix", type: "mat4", size: viewCount);
1154 else
1155 fragmentShader.addUniform(name: "qt_inverseProjectionMatrix", type: "mat4");
1156 }
1157 vertexShader.generateWorldNormal(inKey);
1158 vertexShader.generateWorldPosition(inKey);
1159
1160 const bool usingDefaultMaterialSpecularGGX = !(materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) && materialAdapter->specularModel() == QSSGRenderDefaultMaterial::MaterialSpecularModel::KGGX;
1161 // Note: tangetOrBinormalDebugMode doesn't force generation, it just makes sure that qt_tangent and qt_binormal variables exist
1162 const bool tangentOrBinormalDebugMode = (debugMode == QSSGRenderLayer::MaterialDebugMode::Tangent) || (debugMode == QSSGRenderLayer::MaterialDebugMode::Binormal);
1163 const bool needsTangentAndBinormal = hasCustomFrag || enableParallaxMapping || clearcoatNormalImage || enableBumpNormal || usingDefaultMaterialSpecularGGX || tangentOrBinormalDebugMode;
1164
1165
1166 if (needsTangentAndBinormal) {
1167 bool genTangent = false;
1168 bool genBinormal = false;
1169 vertexShader.generateVarTangentAndBinormal(inKey, genTangent, genBinormal);
1170
1171 if (enableBumpNormal && !genTangent) {
1172 // Generate imageCoords for bump/normal map first.
1173 // Some operations needs to use the TBN transform and if the
1174 // tangent vector is not provided, it is necessary.
1175 auto *bumpNormalImage = bumpImage != nullptr ? bumpImage : normalImage;
1176 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *bumpNormalImage, forceFragmentShader: true, uvSet: bumpNormalImage->m_imageNode.m_indexUV);
1177 genBumpNormalImageCoords = true;
1178
1179 int id = (bumpImage != nullptr) ? int(QSSGRenderableImage::Type::Bump) : int(QSSGRenderableImage::Type::Normal);
1180 const auto &names = imageStringTable[id];
1181 fragmentShader << " vec2 dUVdx = dFdx(" << names.imageFragCoords << ");\n"
1182 << " vec2 dUVdy = dFdy(" << names.imageFragCoords << ");\n";
1183 fragmentShader << " qt_tangent = (dUVdy.y * dFdx(qt_varWorldPos) - dUVdx.y * dFdy(qt_varWorldPos)) / (dUVdx.x * dUVdy.y - dUVdx.y * dUVdy.x);\n"
1184 << " qt_tangent = qt_tangent - dot(qt_world_normal, qt_tangent) * qt_world_normal;\n"
1185 << " qt_tangent = normalize(qt_tangent);\n";
1186 }
1187 if (!genBinormal)
1188 fragmentShader << " qt_binormal = cross(qt_world_normal, qt_tangent);\n";
1189 }
1190
1191 if (isDoubleSided) {
1192 fragmentShader.append(data: "#if QSHADER_HLSL && QSHADER_VIEW_COUNT >= 2");
1193 fragmentShader.append(data: " const float qt_facing = 1.0;");
1194 fragmentShader.append(data: "#else");
1195 fragmentShader.append(data: " const float qt_facing = gl_FrontFacing ? 1.0 : -1.0;");
1196 fragmentShader.append(data: "#endif");
1197 fragmentShader.append(data: " qt_world_normal *= qt_facing;\n");
1198 if (needsTangentAndBinormal) {
1199 fragmentShader.append(data: " qt_tangent *= qt_facing;");
1200 fragmentShader.append(data: " qt_binormal *= qt_facing;");
1201 }
1202 }
1203 }
1204
1205 if (hasCustomFrag) {
1206 // A custom shaded material is effectively a principled material for
1207 // our purposes here. The defaults are different from a
1208 // PrincipledMaterial however, since this is more sensible here.
1209 // (because the shader has to state it to get things)
1210 // These should match the defaults of PrincipledMaterial.
1211 fragmentShader << " float qt_customOcclusionAmount = 1.0;\n";
1212 fragmentShader << " float qt_customIOR = 1.5;\n";
1213 fragmentShader << " float qt_customSpecularAmount = 0.5;\n"; // overrides qt_material_properties.x
1214 fragmentShader << " float qt_customSpecularRoughness = 0.0;\n"; // overrides qt_material_properties.y
1215 fragmentShader << " float qt_customMetalnessAmount = 0.0;\n"; // overrides qt_material_properties.z
1216 fragmentShader << " float qt_customFresnelPower = 5.0;\n"; // overrides qt_material_properties2.x
1217 fragmentShader << " vec4 qt_customBaseColor = vec4(1.0);\n"; // overrides qt_material_base_color
1218 fragmentShader << " vec3 qt_customEmissiveColor = vec3(0.0);\n"; // overrides qt_material_emissive_color
1219 if (enableClearcoat) {
1220 fragmentShader << " float qt_customClearcoatAmount = 0.0;\n";
1221 fragmentShader << " float qt_customClearcoatFresnelPower = 5.0;\n";
1222 fragmentShader << " float qt_customClearcoatRoughness = 0.0;\n";
1223 fragmentShader << " vec3 qt_customClearcoatNormal = qt_world_normal;\n";
1224 if (enableClearcoatFresnelScaleBias) {
1225 fragmentShader << " float qt_customClearcoatFresnelScale = 1.0;\n";
1226 fragmentShader << " float qt_customClearcoatFresnelBias = 0.0;\n";
1227 }
1228 }
1229 if (enableFresnelScaleBias) {
1230 fragmentShader << " float qt_customFresnelScale = 1.0;\n";
1231 fragmentShader << " float qt_customFresnelBias = 0.0;\n";
1232 }
1233
1234 if (enableTransmission) {
1235 fragmentShader << " float qt_customTransmissionFactor = 0.0;\n";
1236 fragmentShader << " float qt_customThicknessFactor = 0.0;\n";
1237 fragmentShader << " vec3 qt_customAttenuationColor = vec3(1.0);\n";
1238 fragmentShader << " float qt_customAttenuationDistance = 0.0;\n";
1239 }
1240 if (usesSharedVar)
1241 fragmentShader << " QT_SHARED_VARS qt_customShared;\n";
1242 // Generate the varyings for UV0 and UV1 since customer materials don't use image
1243 // properties directly.
1244 vertexShader.generateUVCoords(inUVSet: 0, inKey);
1245 vertexShader.generateUVCoords(inUVSet: 1, inKey);
1246 if (includeCustomFragmentMain && hasCustomFunction(QByteArrayLiteral("qt_customMain"))) {
1247 fragmentShader << " qt_customMain(qt_customBaseColor, qt_customEmissiveColor, qt_customMetalnessAmount, qt_customSpecularRoughness,"
1248 " qt_customSpecularAmount, qt_customFresnelPower, qt_world_normal, qt_tangent, qt_binormal,"
1249 " qt_texCoord0, qt_texCoord1, qt_view_vector, qt_customIOR, qt_customOcclusionAmount";
1250 if (enableClearcoat) {
1251 fragmentShader << ", qt_customClearcoatAmount, qt_customClearcoatFresnelPower, qt_customClearcoatRoughness, qt_customClearcoatNormal";
1252 if (enableClearcoatFresnelScaleBias) {
1253 fragmentShader << ", qt_customClearcoatFresnelScale, qt_customClearcoatFresnelBias";
1254 }
1255 }
1256 if (enableFresnelScaleBias) {
1257 fragmentShader << ", qt_customFresnelScale, qt_customFresnelBias";
1258 }
1259 if (enableTransmission) {
1260 fragmentShader << ", qt_customTransmissionFactor, qt_customThicknessFactor, qt_customAttenuationColor, qt_customAttenuationDistance";
1261 }
1262 if (usesSharedVar)
1263 fragmentShader << ", qt_customShared);\n";
1264 else
1265 fragmentShader << ");\n";
1266 }
1267 fragmentShader << " vec4 qt_diffuseColor = qt_customBaseColor * qt_vertColor;\n";
1268 fragmentShader << " vec3 qt_global_emission = qt_customEmissiveColor;\n";
1269 fragmentShader << " float qt_iOR = qt_customIOR;\n";
1270 } else {
1271 fragmentShader << " vec4 qt_diffuseColor = qt_material_base_color * qt_vertColor;\n";
1272 fragmentShader << " vec3 qt_global_emission = qt_material_emissive_color;\n";
1273 if (specularLightingEnabled || hasImage)
1274 fragmentShader << " float qt_iOR = qt_material_specular.w;\n";
1275 }
1276
1277 const bool hasCustomIblProbe = hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_iblProbeProcessor"));
1278
1279 if (isDepthPass)
1280 fragmentShader << " vec4 fragOutput = vec4(0.0);\n";
1281
1282 if (isOrthoShadowPass)
1283 vertexShader.generateDepth();
1284
1285 if (isPerspectiveShadowPass)
1286 vertexShader.generateShadowWorldPosition(inKey);
1287
1288 // !hasLighting does not mean 'no light source'
1289 // it should be KHR_materials_unlit
1290 // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
1291 if (hasLighting) {
1292 if (includeSSAOVars)
1293 fragmentShader.addInclude(name: "ssao.glsllib");
1294
1295 if (enableLightmap) {
1296 vertexShader.generateLightmapUVCoords(inKey);
1297 fragmentShader.addFunction(functionName: "lightmap");
1298 }
1299
1300 fragmentShader.addFunction(functionName: "sampleLightVars");
1301 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy())
1302 fragmentShader.addFunction(functionName: "diffuseBurleyBSDF");
1303 else
1304 fragmentShader.addFunction(functionName: "diffuseReflectionBSDF");
1305
1306 if (enableParallaxMapping) {
1307 // Adjust UV coordinates to account for parallaxMapping before
1308 // reading any other texture.
1309 const bool hasIdentityMap = identityImages.contains(t: heightImage);
1310 if (hasIdentityMap)
1311 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *heightImage, outString&: imageFragCoords, uvSet: heightImage->m_imageNode.m_indexUV);
1312 else
1313 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *heightImage, forceFragmentShader: true, uvSet: heightImage->m_imageNode.m_indexUV);
1314 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Height)];
1315 fragmentShader.addInclude(name: "parallaxMapping.glsllib");
1316 fragmentShader << " float qt_heightAmount = qt_material_properties4.x;\n";
1317 maskVariableByVertexColorChannel( "qt_heightAmount", QSSGRenderDefaultMaterial::HeightAmountMask );
1318 if (viewCount < 2) {
1319 fragmentShader << " qt_texCoord0 = qt_parallaxMapping(" << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ", " << names.imageSampler
1320 <<", qt_tangent, qt_binormal, qt_world_normal, qt_varWorldPos, qt_cameraPosition, qt_heightAmount, qt_material_properties4.y, qt_material_properties4.z);\n";
1321 } else {
1322 fragmentShader << " qt_texCoord0 = qt_parallaxMapping(" << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ", " << names.imageSampler
1323 <<", qt_tangent, qt_binormal, qt_world_normal, qt_varWorldPos, qt_cameraPosition[qt_viewIndex], qt_heightAmount, qt_material_properties4.y, qt_material_properties4.z);\n";
1324 }
1325 }
1326
1327 // Clearcoat Setup (before normalImage code has a change to overwrite qt_world_normal)
1328 if (enableClearcoat) {
1329 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_clearcoatNormal", inType: "vec3");
1330 // Clearcoat normal should be calculated not considering the normalImage for the base material
1331 // If both are to be the same then just set the same normalImage for the base and clearcoat
1332 // This does mean that this value should be calculated before qt_world_normal is overwritten by
1333 // the normalMap.
1334 if (hasCustomFrag) {
1335 fragmentShader << " qt_clearcoatNormal = qt_customClearcoatNormal;\n";
1336 } else {
1337 if (clearcoatNormalImage) {
1338 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *clearcoatNormalImage, forceFragmentShader: enableParallaxMapping, uvSet: clearcoatNormalImage->m_imageNode.m_indexUV);
1339 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::ClearcoatNormal)];
1340 fragmentShader.addFunction(functionName: "sampleNormalTexture");
1341 fragmentShader << " float qt_clearcoat_normal_strength = qt_material_clearcoat_normal_strength;\n";
1342 maskVariableByVertexColorChannel( "qt_clearcoat_normal_strength", QSSGRenderDefaultMaterial::ClearcoatNormalStrengthMask );
1343 fragmentShader << " qt_clearcoatNormal = qt_sampleNormalTexture3(" << names.imageSampler << ", qt_clearcoat_normal_strength, " << names.imageFragCoords << ", qt_tangent, qt_binormal, qt_world_normal);\n";
1344
1345 } else {
1346 // same as qt_world_normal then
1347 fragmentShader << " qt_clearcoatNormal = qt_world_normal;\n";
1348 }
1349 }
1350 }
1351
1352 if (bumpImage != nullptr) {
1353 if (enableParallaxMapping || !genBumpNormalImageCoords) {
1354 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey,
1355 image&: *bumpImage, forceFragmentShader: enableParallaxMapping,
1356 uvSet: bumpImage->m_imageNode.m_indexUV,
1357 reuseImageCoords: genBumpNormalImageCoords);
1358 }
1359 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Bump)];
1360 fragmentShader.addUniform(name: names.imageSamplerSize, type: "vec2");
1361 fragmentShader.append(data: " float qt_bumpAmount = qt_material_properties2.y;\n");
1362 maskVariableByVertexColorChannel( "qt_bumpAmount", QSSGRenderDefaultMaterial::NormalStrengthMask );
1363 fragmentShader.addInclude(name: "defaultMaterialBumpNoLod.glsllib");
1364 fragmentShader << " qt_world_normal = qt_defaultMaterialBumpNoLod(" << names.imageSampler << ", qt_bumpAmount, " << names.imageFragCoords << ", qt_tangent, qt_binormal, qt_world_normal, " << names.imageSamplerSize << ");\n";
1365 } else if (normalImage != nullptr) {
1366 if (enableParallaxMapping || !genBumpNormalImageCoords) {
1367 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey,
1368 image&: *normalImage, forceFragmentShader: enableParallaxMapping,
1369 uvSet: normalImage->m_imageNode.m_indexUV,
1370 reuseImageCoords: genBumpNormalImageCoords);
1371 }
1372 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Normal)];
1373 fragmentShader.append(data: " float qt_normalStrength = qt_material_properties2.y;\n");
1374 maskVariableByVertexColorChannel( "qt_normalStrength", QSSGRenderDefaultMaterial::NormalStrengthMask );
1375 fragmentShader.addFunction(functionName: "sampleNormalTexture");
1376 fragmentShader << " qt_world_normal = qt_sampleNormalTexture3(" << names.imageSampler << ", qt_normalStrength, " << names.imageFragCoords << ", qt_tangent, qt_binormal, qt_world_normal);\n";
1377 }
1378
1379 fragmentShader.append(data: " vec3 tmp_light_color;");
1380 }
1381
1382 if (specularLightingEnabled || hasImage) {
1383 fragmentShader.append(data: " vec3 qt_specularBase;");
1384 fragmentShader.addUniform(name: "qt_material_specular", type: "vec4");
1385 if (hasCustomFrag)
1386 fragmentShader.append(data: " vec3 qt_specularTint = vec3(1.0);");
1387 else
1388 fragmentShader.append(data: " vec3 qt_specularTint = qt_material_specular.rgb;");
1389 }
1390
1391 if (baseImage) {
1392 const bool hasIdentityMap = identityImages.contains(t: baseImage);
1393 if (hasIdentityMap)
1394 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *baseImage, outString&: imageFragCoords, uvSet: baseImage->m_imageNode.m_indexUV);
1395 else
1396 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *baseImage, forceFragmentShader: enableParallaxMapping, uvSet: baseImage->m_imageNode.m_indexUV);
1397
1398 // 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...
1399 const auto &names = imageStringTable[int(baseImage->m_mapType)];
1400 // Diffuse and BaseColor maps need to converted to linear color space
1401 fragmentShader.addInclude(name: "tonemapping.glsllib");
1402 if (materialAdapter->isBaseColorSingleChannelEnabled()) {
1403 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::BaseColorChannel];
1404 fragmentShader << " vec4 qt_base_texture_color = vec4(vec3(texture2D(" << names.imageSampler << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << "), 1.0f);\n";
1405 } else {
1406 fragmentShader << " vec4 qt_base_texture_color = texture2D(" << names.imageSampler << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ");\n";
1407 }
1408 if (!keyProps.m_imageMaps[QSSGShaderDefaultMaterialKeyProperties::BaseColorMap].isLinear(inKeySet: inKey))
1409 fragmentShader << " qt_base_texture_color = qt_sRGBToLinear(qt_base_texture_color);\n";
1410 fragmentShader << " qt_diffuseColor *= qt_base_texture_color;\n";
1411 }
1412
1413 // alpha cutoff
1414 if (materialAdapter->alphaMode() == QSSGRenderDefaultMaterial::MaterialAlphaMode::Mask) {
1415 // The Implementation Notes from
1416 // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#alpha-coverage
1417 // must be met. Hence the discard.
1418 fragmentShader << " if (qt_diffuseColor.a < qt_material_properties3.y) {\n"
1419 << " qt_diffuseColor = vec4(0.0);\n"
1420 << " discard;\n"
1421 << " } else {\n"
1422 << " qt_diffuseColor.a = 1.0;\n"
1423 << " }\n";
1424 } else if (materialAdapter->alphaMode() == QSSGRenderDefaultMaterial::MaterialAlphaMode::Opaque) {
1425 fragmentShader << " qt_diffuseColor.a = 1.0;\n";
1426 }
1427
1428 if (opacityImage) {
1429 const bool hasIdentityMap = identityImages.contains(t: opacityImage);
1430 if (hasIdentityMap)
1431 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *opacityImage, outString&: imageFragCoords, uvSet: opacityImage->m_imageNode.m_indexUV);
1432 else
1433 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *opacityImage, forceFragmentShader: enableParallaxMapping, uvSet: opacityImage->m_imageNode.m_indexUV);
1434
1435 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Opacity)];
1436 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::OpacityChannel];
1437 fragmentShader << " float qt_opacity_map_value = texture2D(" << names.imageSampler << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1438 if ( materialAdapter->isInvertOpacityMapValue() )
1439 fragmentShader << " qt_opacity_map_value = 1.0 - qt_opacity_map_value;\n";
1440 fragmentShader << " qt_objectOpacity *= qt_opacity_map_value;\n";
1441 }
1442
1443 if (hasLighting) {
1444 if (specularLightingEnabled) {
1445 vertexShader.generateViewVector(inKey);
1446 fragmentShader.addUniform(name: "qt_material_properties", type: "vec4");
1447
1448 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy())
1449 fragmentShader << " qt_specularBase = vec3(1.0);\n";
1450 else
1451 fragmentShader << " qt_specularBase = qt_diffuseColor.rgb;\n";
1452 if (hasCustomFrag)
1453 fragmentShader << " float qt_specularFactor = qt_customSpecularAmount;\n";
1454 else
1455 fragmentShader << " float qt_specularFactor = qt_material_properties.x;\n";
1456
1457 maskVariableByVertexColorChannel( "qt_specularFactor", QSSGRenderDefaultMaterial::SpecularAmountMask );
1458 }
1459
1460 // Metalness must be setup fairly earily since so many factors depend on the runtime value
1461 if (hasCustomFrag)
1462 fragmentShader << " float qt_metalnessAmount = qt_customMetalnessAmount;\n";
1463 else if (!materialAdapter->isSpecularGlossy())
1464 fragmentShader << " float qt_metalnessAmount = qt_material_properties.z;\n";
1465 else
1466 fragmentShader << " float qt_metalnessAmount = 0.0;\n";
1467
1468 maskVariableByVertexColorChannel( "qt_metalnessAmount", QSSGRenderDefaultMaterial::MetalnessMask );
1469
1470 if (specularLightingEnabled && metalnessImage) {
1471 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::MetalnessChannel];
1472 const bool hasIdentityMap = identityImages.contains(t: metalnessImage);
1473 if (hasIdentityMap)
1474 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *metalnessImage, outString&: imageFragCoords, uvSet: metalnessImage->m_imageNode.m_indexUV);
1475 else
1476 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *metalnessImage, forceFragmentShader: enableParallaxMapping, uvSet: metalnessImage->m_imageNode.m_indexUV);
1477
1478 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Metalness)];
1479 fragmentShader << " float qt_sampledMetalness = texture2D(" << names.imageSampler << ", "
1480 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1481 fragmentShader << " qt_metalnessAmount = clamp(qt_metalnessAmount * qt_sampledMetalness, 0.0, 1.0);\n";
1482 }
1483
1484 fragmentShader.addUniform(name: "qt_light_ambient_total", type: "vec3");
1485
1486 fragmentShader.append(data: " vec4 global_diffuse_light = vec4(0.0);");
1487
1488 if (enableLightmap) {
1489 fragmentShader << " global_diffuse_light.rgb = qt_lightmap_color(qt_texCoordLightmap) * (1.0 - qt_metalnessAmount) * qt_diffuseColor.rgb;\n";
1490 } else {
1491 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_ambientLightProcessor"))) {
1492 // DIFFUSE, TOTAL_AMBIENT_COLOR, NORMAL, VIEW_VECTOR(, SHARED)
1493 fragmentShader.append(data: " qt_ambientLightProcessor(global_diffuse_light.rgb, qt_light_ambient_total.rgb * (1.0 - qt_metalnessAmount) * qt_diffuseColor.rgb, qt_world_normal, qt_view_vector");
1494 if (usesSharedVar)
1495 fragmentShader << ", qt_customShared);\n";
1496 else
1497 fragmentShader << ");\n";
1498 } else {
1499 fragmentShader.append(data: " global_diffuse_light = vec4(qt_light_ambient_total.rgb * (1.0 - qt_metalnessAmount) * qt_diffuseColor.rgb, 0.0);");
1500 }
1501 }
1502
1503 fragmentShader.append(data: " vec3 global_specular_light = vec3(0.0);");
1504
1505 if (!lights.isEmpty() || hasCustomFrag) {
1506 fragmentShader.append(data: " float qt_shadow_map_occl = 1.0;");
1507 fragmentShader.append(data: " float qt_lightAttenuation = 1.0;");
1508 }
1509
1510 // Fragment lighting means we can perhaps attenuate the specular amount by a texture
1511 // lookup.
1512 if (specularAmountImage) {
1513 const bool hasIdentityMap = identityImages.contains(t: specularAmountImage);
1514 if (hasIdentityMap)
1515 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *specularAmountImage, outString&: imageFragCoords, uvSet: specularAmountImage->m_imageNode.m_indexUV);
1516 else
1517 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *specularAmountImage, forceFragmentShader: enableParallaxMapping, uvSet: specularAmountImage->m_imageNode.m_indexUV);
1518
1519 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::SpecularAmountMap )];
1520
1521 if (materialAdapter->isSpecularAmountSingleChannelEnabled()) {
1522 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::SpecularAmountChannel];
1523 fragmentShader << " vec4 qt_specular_amount_map = vec4(vec3(texture2D(" << names.imageSampler << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << "), 1.0f);\n";
1524 } else {
1525 fragmentShader << " vec4 qt_specular_amount_map = texture2D(" << names.imageSampler << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ");\n";
1526 }
1527 fragmentShader << " qt_specularBase *= qt_sRGBToLinear(qt_specular_amount_map).rgb;\n";
1528 }
1529
1530 if (specularLightingEnabled) {
1531 if (materialAdapter->isSpecularGlossy()) {
1532 fragmentShader << " qt_specularTint *= qt_specularBase;\n";
1533 fragmentShader << " vec3 qt_specularAmount = vec3(1.0);\n";
1534 } else {
1535 fragmentShader << " vec3 qt_specularAmount = qt_specularBase * vec3(qt_metalnessAmount + qt_specularFactor * (1.0 - qt_metalnessAmount));\n";
1536 }
1537 }
1538
1539 if (translucencyImage != nullptr) {
1540 const bool hasIdentityMap = identityImages.contains(t: translucencyImage);
1541 if (hasIdentityMap)
1542 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *translucencyImage, outString&: imageFragCoords);
1543 else
1544 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *translucencyImage, forceFragmentShader: enableParallaxMapping);
1545
1546 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Translucency)];
1547 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::TranslucencyChannel];
1548 fragmentShader << " float qt_translucent_depth_range = texture2D(" << names.imageSampler
1549 << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1550 fragmentShader << " float qt_translucent_thickness = qt_translucent_depth_range * qt_translucent_depth_range;\n";
1551 fragmentShader << " float qt_translucent_thickness_exp = exp(qt_translucent_thickness * qt_material_properties2.z);\n";
1552 }
1553
1554 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_aoFactor", inType: "float");
1555
1556 if (enableSSAO)
1557 fragmentShader.append(data: " qt_aoFactor = qt_screenSpaceAmbientOcclusionFactor();");
1558 else
1559 fragmentShader.append(data: " qt_aoFactor = 1.0;");
1560
1561 if (hasCustomFrag) {
1562 fragmentShader << " float qt_roughnessAmount = qt_customSpecularRoughness;\n";
1563 fragmentShader << " qt_aoFactor *= qt_customOcclusionAmount;\n";
1564 }
1565 else {
1566 fragmentShader << " float qt_roughnessAmount = qt_material_properties.y;\n";
1567 }
1568
1569 maskVariableByVertexColorChannel( "qt_roughnessAmount", QSSGRenderDefaultMaterial::RoughnessMask );
1570
1571
1572 // Occlusion Map
1573 if (occlusionImage) {
1574 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_ao", inType: "float");
1575 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::OcclusionChannel];
1576 const bool hasIdentityMap = identityImages.contains(t: occlusionImage);
1577 if (hasIdentityMap)
1578 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *occlusionImage, outString&: imageFragCoords, uvSet: occlusionImage->m_imageNode.m_indexUV);
1579 else
1580 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *occlusionImage, forceFragmentShader: enableParallaxMapping, uvSet: occlusionImage->m_imageNode.m_indexUV);
1581 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Occlusion)];
1582 fragmentShader << " qt_ao = texture2D(" << names.imageSampler << ", "
1583 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1584 fragmentShader << " qt_aoFactor *= qt_ao * qt_material_properties3.x;\n";
1585 // qt_material_properties3.x is the OcclusionAmount
1586 maskVariableByVertexColorChannel( "qt_aoFactor", QSSGRenderDefaultMaterial::OcclusionAmountMask );
1587 }
1588
1589 if (specularLightingEnabled && roughnessImage) {
1590 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::RoughnessChannel];
1591 const bool hasIdentityMap = identityImages.contains(t: roughnessImage);
1592 if (hasIdentityMap)
1593 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *roughnessImage, outString&: imageFragCoords, uvSet: roughnessImage->m_imageNode.m_indexUV);
1594 else
1595 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *roughnessImage, forceFragmentShader: enableParallaxMapping, uvSet: roughnessImage->m_imageNode.m_indexUV);
1596
1597 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Roughness)];
1598 fragmentShader << " qt_roughnessAmount *= texture2D(" << names.imageSampler << ", "
1599 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1600 }
1601
1602 // Convert Glossy to Roughness
1603 if (materialAdapter->isSpecularGlossy())
1604 fragmentShader << " qt_roughnessAmount = clamp(1.0 - qt_roughnessAmount, 0.0, 1.0);\n";
1605
1606 if (enableClearcoat) {
1607 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_clearcoatAmount", inType: "float");
1608 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_clearcoatRoughness", inType: "float");
1609 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_clearcoatF0", inType: "vec3");
1610 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_clearcoatF90", inType: "vec3");
1611 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_global_clearcoat", inType: "vec3");
1612
1613 if (hasCustomFrag)
1614 fragmentShader << " qt_clearcoatAmount = qt_customClearcoatAmount;\n";
1615 else
1616 fragmentShader << " qt_clearcoatAmount = qt_material_properties3.z;\n";
1617 maskVariableByVertexColorChannel( "qt_clearcoatAmount", QSSGRenderDefaultMaterial::ClearcoatAmountMask );
1618 if (hasCustomFrag)
1619 fragmentShader << " qt_clearcoatRoughness = qt_customClearcoatRoughness;\n";
1620 else
1621 fragmentShader << " qt_clearcoatRoughness = qt_material_properties3.w;\n";
1622 maskVariableByVertexColorChannel( "qt_clearcoatRoughness", QSSGRenderDefaultMaterial::ClearcoatRoughnessAmountMask );
1623 fragmentShader << " qt_clearcoatF0 = vec3(((1.0-qt_iOR) * (1.0-qt_iOR)) / ((1.0+qt_iOR) * (1.0+qt_iOR)));\n";
1624 fragmentShader << " qt_clearcoatF90 = vec3(1.0);\n";
1625 fragmentShader << " qt_global_clearcoat = vec3(0.0);\n";
1626
1627 if (clearcoatImage) {
1628 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::ClearcoatChannel];
1629 const bool hasIdentityMap = identityImages.contains(t: clearcoatImage);
1630 if (hasIdentityMap)
1631 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *clearcoatImage, outString&: imageFragCoords, uvSet: clearcoatImage->m_imageNode.m_indexUV);
1632 else
1633 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *clearcoatImage, forceFragmentShader: enableParallaxMapping, uvSet: clearcoatImage->m_imageNode.m_indexUV);
1634 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Clearcoat)];
1635 fragmentShader << " qt_clearcoatAmount *= texture2D(" << names.imageSampler << ", "
1636 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1637 }
1638
1639 if (clearcoatRoughnessImage) {
1640 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::ClearcoatRoughnessChannel];
1641 const bool hasIdentityMap = identityImages.contains(t: clearcoatRoughnessImage);
1642 if (hasIdentityMap)
1643 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *clearcoatRoughnessImage, outString&: imageFragCoords, uvSet: clearcoatRoughnessImage->m_imageNode.m_indexUV);
1644 else
1645 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *clearcoatRoughnessImage, forceFragmentShader: enableParallaxMapping, uvSet: clearcoatRoughnessImage->m_imageNode.m_indexUV);
1646 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::ClearcoatRoughness)];
1647 fragmentShader << " qt_clearcoatRoughness *= texture2D(" << names.imageSampler << ", "
1648 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1649 fragmentShader << " qt_clearcoatRoughness = clamp(qt_clearcoatRoughness, 0.0, 1.0);\n";
1650 }
1651 }
1652
1653 if (enableTransmission) {
1654 fragmentShader.addInclude(name: "transmission.glsllib");
1655 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_transmissionFactor", inType: "float");
1656 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_global_transmission", inType: "vec3");
1657 // Volume
1658 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_thicknessFactor", inType: "float");
1659 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_attenuationColor", inType: "vec3");
1660 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_attenuationDistance", inType: "float");
1661 fragmentShader << " qt_global_transmission = vec3(0.0);\n";
1662
1663 if (hasCustomFrag) {
1664 fragmentShader << " qt_transmissionFactor = qt_customTransmissionFactor;\n";
1665 fragmentShader << " qt_thicknessFactor = qt_customThicknessFactor;\n";
1666 fragmentShader << " qt_attenuationColor = qt_customAttenuationColor;\n";
1667 fragmentShader << " qt_attenuationDistance = qt_customAttenuationDistance;\n";
1668 } else {
1669 fragmentShader << " qt_transmissionFactor = qt_material_properties4.w;\n";
1670 maskVariableByVertexColorChannel( "qt_transmissionFactor", QSSGRenderDefaultMaterial::TransmissionFactorMask );
1671
1672 if (transmissionImage) {
1673 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::TransmissionChannel];
1674 const bool hasIdentityMap = identityImages.contains(t: transmissionImage);
1675 if (hasIdentityMap)
1676 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *transmissionImage, outString&: imageFragCoords, uvSet: transmissionImage->m_imageNode.m_indexUV);
1677 else
1678 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *transmissionImage, forceFragmentShader: enableParallaxMapping, uvSet: transmissionImage->m_imageNode.m_indexUV);
1679 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Transmission)];
1680 fragmentShader << " qt_transmissionFactor *= texture2D(" << names.imageSampler << ", "
1681 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1682 }
1683
1684 fragmentShader << " qt_thicknessFactor = qt_material_thickness;\n";
1685 maskVariableByVertexColorChannel( "qt_thicknessFactor", QSSGRenderDefaultMaterial::ThicknessFactorMask );
1686 fragmentShader << " qt_attenuationColor = qt_material_attenuation.xyz;\n";
1687 fragmentShader << " qt_attenuationDistance = qt_material_attenuation.w;\n";
1688
1689 if (thicknessImage) {
1690 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::ThicknessChannel];
1691 const bool hasIdentityMap = identityImages.contains(t: thicknessImage);
1692 if (hasIdentityMap)
1693 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *thicknessImage, outString&: imageFragCoords, uvSet: thicknessImage->m_imageNode.m_indexUV);
1694 else
1695 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *thicknessImage, forceFragmentShader: enableParallaxMapping, uvSet: thicknessImage->m_imageNode.m_indexUV);
1696 const auto &names = imageStringTable[int(QSSGRenderableImage::Type::Thickness)];
1697 fragmentShader << " qt_thicknessFactor *= texture2D(" << names.imageSampler << ", "
1698 << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ")" << channelStr(channelProps, inKey) << ";\n";
1699 }
1700 }
1701 }
1702
1703 if (specularLightingEnabled) {
1704 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
1705 fragmentShader.addInclude(name: "principledMaterialFresnel.glsllib");
1706 const bool useF90 = !lights.isEmpty() || enableTransmission;
1707 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_f0", inType: "vec3");
1708 if (useF90)
1709 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_f90", inType: "vec3");
1710 if (materialAdapter->isPrincipled()) {
1711 fragmentShader << " qt_f0 = qt_F0_ior(qt_iOR, qt_metalnessAmount, qt_diffuseColor.rgb);\n";
1712 if (useF90)
1713 fragmentShader << " qt_f90 = vec3(1.0);\n";
1714 } else {
1715 addLocalVariable(inGenerator&: fragmentShader, inName: "qt_reflectance", inType: "float");
1716
1717 fragmentShader << " qt_reflectance = max(max(qt_specularTint.r, qt_specularTint.g), qt_specularTint.b);\n";
1718 fragmentShader << " qt_f0 = qt_specularTint;\n";
1719 fragmentShader << " qt_specularTint = vec3(1.0);\n";
1720 if (useF90)
1721 fragmentShader << " qt_f90 = vec3(clamp(qt_reflectance * 50.0, 0.0, 1.0));\n";
1722 fragmentShader << " qt_diffuseColor.rgb *= (1 - qt_reflectance);\n";
1723 }
1724
1725 if (specularAAEnabled) {
1726 fragmentShader.append(data: " vec3 vNormalWsDdx = dFdx(qt_world_normal.xyz);\n");
1727 fragmentShader.append(data: " vec3 vNormalWsDdy = dFdy(qt_world_normal.xyz);\n");
1728 fragmentShader.append(data: " float flGeometricRoughnessFactor = pow(clamp(max(dot(vNormalWsDdx, vNormalWsDdx), dot(vNormalWsDdy, vNormalWsDdy)), 0.0, 1.0), 0.333);\n");
1729 fragmentShader.append(data: " qt_roughnessAmount = max(flGeometricRoughnessFactor, qt_roughnessAmount);\n");
1730 }
1731
1732 if (hasCustomFrag)
1733 fragmentShader << " float qt_fresnelPower = qt_customFresnelPower;\n";
1734 else
1735 fragmentShader << " float qt_fresnelPower = qt_material_properties2.x;\n";
1736
1737 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
1738 fragmentShader << " vec3 qt_principledMaterialFresnelValue = qt_principledMaterialFresnel(qt_world_normal, qt_view_vector, "
1739 << "qt_f0, qt_roughnessAmount, qt_fresnelPower);\n";
1740 if (enableFresnelScaleBias) {
1741 if (hasCustomFrag) {
1742 fragmentShader << " float qt_fresnelScale = qt_customFresnelScale;\n";
1743 fragmentShader << " float qt_fresnelBias = qt_customFresnelBias;\n";
1744 }else {
1745 fragmentShader << " float qt_fresnelScale = qt_material_properties5.x;\n";
1746 fragmentShader << " float qt_fresnelBias = qt_material_properties5.y;\n";
1747 }
1748 fragmentShader << " qt_principledMaterialFresnelValue = clamp(vec3(qt_fresnelBias) + "
1749 << "qt_fresnelScale * qt_principledMaterialFresnelValue, 0.0, 1.0);\n";
1750 }
1751 fragmentShader << " qt_specularAmount *= qt_principledMaterialFresnelValue;\n";
1752 if (materialAdapter->isPrincipled()) {
1753 // Make sure that we scale the specularTint with repsect to metalness (no tint if qt_metalnessAmount == 1)
1754 // We actually need to do this here because we won't know the final metalness value until this point.
1755 fragmentShader << " qt_specularTint = mix(vec3(1.0), qt_specularTint, 1.0 - qt_metalnessAmount);\n";
1756 }
1757 } else {
1758 fragmentShader << " qt_specularAmount *= qt_principledMaterialFresnel(qt_world_normal, qt_view_vector, "
1759 << "qt_f0, qt_roughnessAmount, qt_fresnelPower);\n";
1760 }
1761 } else {
1762 Q_ASSERT(!hasCustomFrag);
1763 fragmentShader.addInclude(name: "defaultMaterialFresnel.glsllib");
1764 fragmentShader << " qt_diffuseColor.rgb *= (1.0 - qt_dielectricSpecular(qt_iOR)) * (1.0 - qt_metalnessAmount);\n";
1765 maybeAddMaterialFresnel(fragmentShader, keyProps, inKey, hasMetalness: metalnessEnabled);
1766 }
1767 }
1768
1769 if (!lights.isEmpty()) {
1770 generateMainLightCalculation(fragmentShader,
1771 vertexShader,
1772 inKey,
1773 inMaterial,
1774 lights,
1775 shaderLibraryManager,
1776 translucencyImage,
1777 hasCustomFrag,
1778 usesSharedVar,
1779 enableLightmap,
1780 enableShadowMaps,
1781 specularLightingEnabled,
1782 enableClearcoat,
1783 enableTransmission);
1784 }
1785
1786 // The color in rgb is ready, including shadowing, just need to apply
1787 // the ambient occlusion factor. The alpha is the model opacity
1788 // multiplied by the alpha from the material color and/or the vertex colors.
1789 fragmentShader << " global_diffuse_light = vec4(global_diffuse_light.rgb * qt_aoFactor, qt_objectOpacity * qt_diffuseColor.a);\n";
1790
1791 if (hasReflectionProbe) {
1792 vertexShader.generateWorldNormal(inKey);
1793 fragmentShader.addInclude(name: "sampleReflectionProbe.glsllib");
1794
1795 fragmentShader << " vec3 qt_reflectionDiffuse = vec3(0.0);\n";
1796 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
1797 fragmentShader << " qt_reflectionDiffuse = qt_diffuseColor.rgb * (1.0 - qt_specularAmount) * qt_sampleDiffuseReflection(qt_reflectionMap, qt_world_normal).rgb;\n";
1798 } else {
1799 fragmentShader << " qt_reflectionDiffuse = qt_diffuseColor.rgb * qt_sampleDiffuseReflection(qt_reflectionMap, qt_world_normal).rgb;\n";
1800 }
1801
1802 if (specularLightingEnabled) {
1803 fragmentShader << " vec3 qt_reflectionSpecular = vec3(0.0);\n";
1804 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
1805 fragmentShader << " qt_reflectionSpecular = "
1806 << "qt_specularTint * qt_sampleGlossyReflectionPrincipled(qt_reflectionMap, qt_world_normal, qt_view_vector, qt_specularAmount, qt_roughnessAmount).rgb;\n";
1807 } else {
1808 fragmentShader << " qt_reflectionSpecular = qt_specularAmount * "
1809 << "qt_specularTint * qt_sampleGlossyReflection(qt_reflectionMap, qt_world_normal, qt_view_vector, qt_roughnessAmount).rgb;\n";
1810 }
1811 }
1812 if (enableClearcoat) {
1813 fragmentShader << " vec3 qt_iblClearcoat = qt_sampleGlossyReflectionPrincipled(qt_reflectionMap, qt_clearcoatNormal, qt_view_vector, qt_clearcoatF0, qt_clearcoatRoughness).rgb;\n";
1814 }
1815
1816 fragmentShader << " global_diffuse_light.rgb += qt_reflectionDiffuse;\n";
1817 if (specularLightingEnabled)
1818 fragmentShader << " global_specular_light += qt_reflectionSpecular;\n";
1819 if (enableClearcoat)
1820 fragmentShader << " qt_global_clearcoat += qt_iblClearcoat;\n";
1821 } else if (hasIblProbe) {
1822 vertexShader.generateWorldNormal(inKey);
1823 fragmentShader.addInclude(name: "sampleProbe.glsllib");
1824 if (hasCustomIblProbe) {
1825 // DIFFUSE, SPECULAR, BASE_COLOR, AO_FACTOR, SPECULAR_AMOUNT, NORMAL, VIEW_VECTOR, IBL_ORIENTATION(, SHARED)
1826 fragmentShader << " vec3 qt_iblDiffuse = vec3(0.0);\n";
1827 fragmentShader << " vec3 qt_iblSpecular = vec3(0.0);\n";
1828 fragmentShader << " qt_iblProbeProcessor(qt_iblDiffuse, qt_iblSpecular, qt_customBaseColor, qt_aoFactor, qt_specularFactor, qt_roughnessAmount, qt_world_normal, qt_view_vector";
1829 if (hasIblOrientation)
1830 fragmentShader << ", qt_lightProbeOrientation";
1831 else
1832 fragmentShader << ", mat3(1.0)";
1833 if (usesSharedVar)
1834 fragmentShader << ", qt_customShared);\n";
1835 else
1836 fragmentShader << ");\n";
1837 } else {
1838 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy()) {
1839 fragmentShader << " vec3 qt_iblDiffuse = qt_diffuseColor.rgb * (1.0 - qt_specularAmount) * qt_sampleDiffuse(qt_world_normal).rgb;\n";
1840 } else {
1841 fragmentShader << " vec3 qt_iblDiffuse = qt_diffuseColor.rgb * qt_sampleDiffuse(qt_world_normal).rgb;\n";
1842 }
1843 if (specularLightingEnabled) {
1844 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularEnabled()) {
1845 fragmentShader << " vec3 qt_iblSpecular = "
1846 << "qt_specularTint * qt_sampleGlossyPrincipled(qt_world_normal, qt_view_vector, qt_specularAmount, qt_roughnessAmount).rgb;\n";
1847 } else {
1848 fragmentShader << " vec3 qt_iblSpecular = qt_specularAmount * "
1849 << "qt_specularTint * qt_sampleGlossy(qt_world_normal, qt_view_vector, qt_roughnessAmount).rgb;\n";
1850 }
1851 }
1852 if (enableClearcoat) {
1853 fragmentShader << " vec3 qt_iblClearcoat = qt_sampleGlossyPrincipled(qt_clearcoatNormal, qt_view_vector, qt_clearcoatF0, qt_clearcoatRoughness).rgb;\n";
1854 }
1855 }
1856
1857 fragmentShader << " global_diffuse_light.rgb += qt_iblDiffuse * qt_aoFactor;\n";
1858 if (specularLightingEnabled)
1859 fragmentShader << " global_specular_light += qt_iblSpecular * qt_aoFactor;\n";
1860 if (enableClearcoat)
1861 fragmentShader << " qt_global_clearcoat += qt_iblClearcoat * qt_aoFactor;\n";
1862 } else if (hasCustomIblProbe) {
1863 // Prevent breaking the fragment code while seeking uniforms
1864 fragmentShader.addUniform(name: "qt_lightProbe", type: "samplerCube");
1865 fragmentShader.addUniform(name: "qt_lightProbeProperties", type: "vec4");
1866 }
1867
1868 // This can run even without a IBL probe
1869 if (enableTransmission) {
1870 fragmentShader << " qt_global_transmission += qt_transmissionFactor * qt_getIBLVolumeRefraction(qt_world_normal, qt_view_vector, qt_roughnessAmount, "
1871 "qt_diffuseColor.rgb, qt_specularAmount, qt_varWorldPos, qt_iOR, qt_thicknessFactor, qt_attenuationColor, qt_attenuationDistance);\n";
1872 }
1873
1874 if (hasImage) {
1875 bool texColorDeclared = false;
1876 for (QSSGRenderableImage *image = firstImage; image; image = image->m_nextImage) {
1877 // map types other than these 2 are handled elsewhere
1878 if (image->m_mapType != QSSGRenderableImage::Type::Specular
1879 && image->m_mapType != QSSGRenderableImage::Type::Emissive)
1880 {
1881 continue;
1882 }
1883
1884 if (!texColorDeclared) {
1885 fragmentShader.append(data: " vec4 qt_texture_color;");
1886 texColorDeclared = true;
1887 }
1888
1889 const bool hasIdentityMap = identityImages.contains(t: image);
1890 if (hasIdentityMap)
1891 generateImageUVSampler(vertexGenerator&: vertexShader, fragmentShader, key: inKey, image: *image, outString&: imageFragCoords, uvSet: image->m_imageNode.m_indexUV);
1892 else
1893 generateImageUVCoordinates(vertexShader, fragmentShader, key: inKey, image&: *image, forceFragmentShader: enableParallaxMapping, uvSet: image->m_imageNode.m_indexUV);
1894
1895 const auto &names = imageStringTable[int(image->m_mapType)];
1896 fragmentShader << " qt_texture_color = texture2D(" << names.imageSampler
1897 << ", " << (hasIdentityMap ? imageFragCoords : names.imageFragCoords) << ");\n";
1898
1899 switch (image->m_mapType) {
1900 case QSSGRenderableImage::Type::Specular:
1901 fragmentShader.addInclude(name: "tonemapping.glsllib");
1902 fragmentShader.append(data: " global_specular_light += qt_sRGBToLinear(qt_texture_color.rgb) * qt_specularTint;");
1903 fragmentShader.append(data: " global_diffuse_light.a *= qt_texture_color.a;");
1904 break;
1905 case QSSGRenderableImage::Type::Emissive:
1906 fragmentShader.addInclude(name: "tonemapping.glsllib");
1907 if (materialAdapter->isEmissiveSingleChannelEnabled()) {
1908 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::EmissiveChannel];
1909 fragmentShader << " qt_global_emission *= qt_sRGBToLinear(vec3(qt_texture_color" <<
1910 channelStr(channelProps, inKey) << "));\n";
1911 } else {
1912 fragmentShader.append(data: " qt_global_emission *= qt_sRGBToLinear(qt_texture_color.rgb);");
1913 }
1914 break;
1915 default:
1916 Q_ASSERT(false);
1917 break;
1918 }
1919 }
1920 }
1921
1922 if (enableTransmission)
1923 fragmentShader << " global_diffuse_light.rgb = mix(global_diffuse_light.rgb, qt_global_transmission, qt_transmissionFactor);\n";
1924
1925 if (materialAdapter->isPrincipled()) {
1926 fragmentShader << " global_diffuse_light.rgb *= 1.0 - qt_metalnessAmount;\n";
1927 }
1928
1929 if (enableFog) {
1930 fragmentShader.addInclude(name: "fog.glsllib");
1931 fragmentShader << " calculateFog(qt_global_emission, global_specular_light, global_diffuse_light.rgb);\n";
1932 }
1933
1934 fragmentShader << " vec4 qt_color_sum = vec4(global_diffuse_light.rgb + global_specular_light + qt_global_emission, global_diffuse_light.a);\n";
1935
1936 if (enableClearcoat) {
1937 fragmentShader.addInclude(name: "bsdf.glsllib");
1938 if (hasCustomFrag)
1939 fragmentShader << " float qt_clearcoatFresnelPower = qt_customClearcoatFresnelPower;\n";
1940 else
1941 fragmentShader << " float qt_clearcoatFresnelPower = qt_material_clearcoat_fresnel_power;\n";
1942 fragmentShader << " vec3 qt_clearcoatFresnel = qt_schlick3(qt_clearcoatF0, qt_clearcoatF90, clamp(dot(qt_clearcoatNormal, qt_view_vector), 0.0, 1.0), qt_clearcoatFresnelPower);\n";
1943 if (enableClearcoatFresnelScaleBias) {
1944 if (hasCustomFrag) {
1945 fragmentShader << " float qt_clearcoatFresnelScale = qt_customClearcoatFresnelScale;\n";
1946 fragmentShader << " float qt_clearcoatFresnelBias = qt_customClearcoatFresnelBias;\n";
1947 }else {
1948 fragmentShader << " float qt_clearcoatFresnelScale = qt_material_properties5.z;\n";
1949 fragmentShader << " float qt_clearcoatFresnelBias = qt_material_properties5.w;\n";
1950 }
1951 fragmentShader << " qt_clearcoatFresnel = clamp(vec3(qt_clearcoatFresnelBias) + qt_clearcoatFresnelScale * qt_clearcoatFresnel, 0.0, 1.0);\n";
1952 }
1953 fragmentShader << " qt_global_clearcoat = qt_global_clearcoat * qt_clearcoatAmount;\n";
1954 fragmentShader << " qt_color_sum.rgb = qt_color_sum.rgb * (1.0 - qt_clearcoatAmount * qt_clearcoatFresnel) + qt_global_clearcoat;\n";
1955 }
1956
1957 if (hasCustomFrag && hasCustomFunction(QByteArrayLiteral("qt_customPostProcessor"))) {
1958 // COLOR_SUM, DIFFUSE, SPECULAR, EMISSIVE, UV0, UV1(, SHARED)
1959 fragmentShader << " qt_customPostProcessor(qt_color_sum, global_diffuse_light, global_specular_light, qt_global_emission, qt_texCoord0, qt_texCoord1";
1960 if (usesSharedVar)
1961 fragmentShader << ", qt_customShared);\n";
1962 else
1963 fragmentShader << ");\n";
1964 }
1965
1966 Q_ASSERT(!isDepthPass && !isOrthoShadowPass && !isPerspectiveShadowPass);
1967 fragmentShader.addInclude(name: "tonemapping.glsllib");
1968 fragmentShader.append(data: " fragOutput = vec4(qt_tonemap(qt_color_sum));");
1969
1970 // Debug Overrides for viewing various parts of the shading process
1971 if (Q_UNLIKELY(debugMode != QSSGRenderLayer::MaterialDebugMode::None)) {
1972 fragmentShader.append(data: " vec3 debugOutput = vec3(0.0);\n");
1973 switch (debugMode) {
1974 case QSSGRenderLayer::MaterialDebugMode::BaseColor:
1975 fragmentShader.append(data: " debugOutput += qt_tonemap(qt_diffuseColor.rgb);\n");
1976 break;
1977 case QSSGRenderLayer::MaterialDebugMode::Roughness:
1978 fragmentShader.append(data: " debugOutput += vec3(qt_roughnessAmount);\n");
1979 break;
1980 case QSSGRenderLayer::MaterialDebugMode::Metalness:
1981 fragmentShader.append(data: " debugOutput += vec3(qt_metalnessAmount);\n");
1982 break;
1983 case QSSGRenderLayer::MaterialDebugMode::Diffuse:
1984 fragmentShader.append(data: " debugOutput += qt_tonemap(global_diffuse_light.rgb);\n");
1985 break;
1986 case QSSGRenderLayer::MaterialDebugMode::Specular:
1987 fragmentShader.append(data: " debugOutput += qt_tonemap(global_specular_light);\n");
1988 break;
1989 case QSSGRenderLayer::MaterialDebugMode::ShadowOcclusion:
1990 fragmentShader.append(data: " debugOutput += vec3(qt_shadow_map_occl);\n");
1991 break;
1992 case QSSGRenderLayer::MaterialDebugMode::Emission:
1993 fragmentShader.append(data: " debugOutput += qt_tonemap(qt_global_emission);\n");
1994 break;
1995 case QSSGRenderLayer::MaterialDebugMode::AmbientOcclusion:
1996 fragmentShader.append(data: " debugOutput += vec3(qt_aoFactor);\n");
1997 break;
1998 case QSSGRenderLayer::MaterialDebugMode::Normal:
1999 fragmentShader.append(data: " debugOutput += qt_world_normal * 0.5 + 0.5;\n");
2000 break;
2001 case QSSGRenderLayer::MaterialDebugMode::Tangent:
2002 fragmentShader.append(data: " debugOutput += qt_tangent * 0.5 + 0.5;\n");
2003 break;
2004 case QSSGRenderLayer::MaterialDebugMode::Binormal:
2005 fragmentShader.append(data: " debugOutput += qt_binormal * 0.5 + 0.5;\n");
2006 break;
2007 case QSSGRenderLayer::MaterialDebugMode::F0:
2008 if (materialAdapter->isPrincipled() || materialAdapter->isSpecularGlossy())
2009 fragmentShader.append(data: " debugOutput += qt_f0;");
2010 break;
2011 case QSSGRenderLayer::MaterialDebugMode::None:
2012 Q_UNREACHABLE();
2013 break;
2014 }
2015 fragmentShader.append(data: " fragOutput = vec4(debugOutput, 1.0);\n");
2016 }
2017 } else {
2018 if ((isOrthoShadowPass || isPerspectiveShadowPass || isDepthPass) && isOpaqueDepthPrePass) {
2019 fragmentShader << " if ((qt_diffuseColor.a * qt_objectOpacity) < 1.0)\n";
2020 fragmentShader << " discard;\n";
2021 }
2022
2023 if (isOrthoShadowPass) {
2024 Q_ASSERT(viewCount == 1);
2025 fragmentShader.addUniform(name: "qt_shadowDepthAdjust", type: "vec2");
2026 fragmentShader << " // directional shadow pass\n"
2027 << " float qt_shadowDepth = (qt_varDepth + qt_shadowDepthAdjust.x) * qt_shadowDepthAdjust.y;\n"
2028 << " fragOutput = vec4(qt_shadowDepth);\n";
2029 } else if (isPerspectiveShadowPass) {
2030 Q_ASSERT(viewCount == 1);
2031 fragmentShader.addUniform(name: "qt_cameraPosition", type: "vec3");
2032 fragmentShader.addUniform(name: "qt_cameraProperties", type: "vec2");
2033 fragmentShader << " // omnidirectional shadow pass\n"
2034 << " vec3 qt_shadowCamPos = vec3(qt_cameraPosition.x, qt_cameraPosition.y, qt_cameraPosition.z);\n"
2035 << " float qt_shadowDist = length(qt_varShadowWorldPos - qt_shadowCamPos);\n"
2036 << " qt_shadowDist = (qt_shadowDist - qt_cameraProperties.x) / (qt_cameraProperties.y - qt_cameraProperties.x);\n"
2037 << " fragOutput = vec4(qt_shadowDist, qt_shadowDist, qt_shadowDist, 1.0);\n";
2038 } else {
2039 fragmentShader.addInclude(name: "tonemapping.glsllib");
2040 fragmentShader.append(data: " fragOutput = vec4(qt_tonemap(qt_diffuseColor.rgb), qt_diffuseColor.a * qt_objectOpacity);");
2041 }
2042 }
2043}
2044
2045QSSGRhiShaderPipelinePtr QSSGMaterialShaderGenerator::generateMaterialRhiShader(const QByteArray &inShaderKeyPrefix,
2046 QSSGMaterialVertexPipeline &vertexPipeline,
2047 const QSSGShaderDefaultMaterialKey &key,
2048 const QSSGShaderDefaultMaterialKeyProperties &inProperties,
2049 const QSSGShaderFeatures &inFeatureSet,
2050 const QSSGRenderGraphObject &inMaterial,
2051 const QSSGShaderLightListView &inLights,
2052 QSSGRenderableImage *inFirstImage,
2053 QSSGShaderLibraryManager &shaderLibraryManager,
2054 QSSGShaderCache &theCache)
2055{
2056 const int viewCount = inFeatureSet.isSet(feature: QSSGShaderFeatures::Feature::DisableMultiView)
2057 ? 1 : inProperties.m_viewCount.getValue(inDataStore: key);
2058
2059 bool perTargetCompilation = false;
2060 // Cull mode None implies doing the gl_FrontFacing-based double sided logic
2061 // in the shader. This may want to ifdef the generated shader code based the
2062 // target shading language. This is only possible if the QShaderBaker is
2063 // told to compile to SPIR-V (and then transpile) separately for each target
2064 // (GLSL, HLSL, etc.), instead of just compiling to SPIR-V once (and
2065 // transpiling the same bytecode to each target language). This takes more
2066 // time, so we only do it for multiview since that's also how the logic is
2067 // going to be written in the generated shader code.
2068 if (viewCount >= 2) {
2069 const bool isDoubleSided = inProperties.m_isDoubleSided.getValue(inDataStore: key);
2070 if (isDoubleSided)
2071 perTargetCompilation = true;
2072 }
2073
2074 QByteArray materialInfoString; // also serves as the key for the cache in compileGeneratedRhiShader
2075 // inShaderKeyPrefix can be a static string for default materials, but must
2076 // be unique for different sets of shaders in custom materials.
2077 materialInfoString = inShaderKeyPrefix;
2078 key.toString(ioString&: materialInfoString, inProperties);
2079
2080 // the call order is: beginVertex, beginFragment, endVertex, endFragment
2081 vertexPipeline.beginVertexGeneration(inKey: key, inFeatureSet, shaderLibraryManager);
2082 generateFragmentShader(fragmentShader&: vertexPipeline.fragment(), vertexShader&: vertexPipeline, inKey: key, keyProps: inProperties, featureSet: inFeatureSet, inMaterial, lights: inLights, firstImage: inFirstImage, shaderLibraryManager);
2083 vertexPipeline.endVertexGeneration();
2084 vertexPipeline.endFragmentGeneration();
2085
2086 return vertexPipeline.programGenerator()->compileGeneratedRhiShader(inMaterialInfoString: materialInfoString,
2087 inFeatureSet,
2088 shaderLibraryManager,
2089 theCache,
2090 stageFlags: {},
2091 viewCount,
2092 perTargetCompilation);
2093}
2094
2095void QSSGMaterialShaderGenerator::setRhiMaterialProperties(const QSSGRenderContextInterface &renderContext,
2096 QSSGRhiShaderPipeline &shaders,
2097 char *ubufData,
2098 QSSGRhiGraphicsPipelineState *inPipelineState,
2099 const QSSGRenderGraphObject &inMaterial,
2100 const QSSGShaderDefaultMaterialKey &inKey,
2101 const QSSGShaderDefaultMaterialKeyProperties &inProperties,
2102 const QSSGRenderCameraList &inCameras,
2103 const QSSGRenderMvpArray &inModelViewProjections,
2104 const QMatrix3x3 &inNormalMatrix,
2105 const QMatrix4x4 &inGlobalTransform,
2106 const QMatrix4x4 &clipSpaceCorrMatrix,
2107 const QMatrix4x4 &localInstanceTransform,
2108 const QMatrix4x4 &globalInstanceTransform,
2109 const QSSGDataView<float> &inMorphWeights,
2110 QSSGRenderableImage *inFirstImage,
2111 float inOpacity,
2112 const QSSGLayerRenderData &inRenderProperties,
2113 const QSSGShaderLightListView &inLights,
2114 const QSSGShaderReflectionProbe &reflectionProbe,
2115 bool receivesShadows,
2116 bool receivesReflections,
2117 const QVector2D *shadowDepthAdjust,
2118 QRhiTexture *lightmapTexture)
2119{
2120 QSSGShaderMaterialAdapter *materialAdapter = getMaterialAdapter(inMaterial);
2121 QSSGRhiShaderPipeline::CommonUniformIndices &cui = shaders.commonUniformIndices;
2122
2123 materialAdapter->setCustomPropertyUniforms(ubufData, shaderPipeline&: shaders, context: renderContext);
2124
2125 const QVector2D camProperties(inCameras[0]->clipNear, inCameras[0]->clipFar);
2126 shaders.setUniform(ubufData, name: "qt_cameraProperties", data: &camProperties, size: 2 * sizeof(float), storeIndex: &cui.cameraPropertiesIdx);
2127
2128 const int viewCount = inCameras.count();
2129 if (viewCount < 2) {
2130 const QVector3D camGlobalPos = inCameras[0]->getGlobalPos();
2131 shaders.setUniform(ubufData, name: "qt_cameraPosition", data: &camGlobalPos, size: 3 * sizeof(float), storeIndex: &cui.cameraPositionIdx);
2132 const QVector3D camDirection = QSSG_GUARD(inRenderProperties.renderedCameraData.has_value())
2133 ? inRenderProperties.renderedCameraData.value()[0].direction
2134 : QVector3D{ 0.0f, 0.0f, -1.0f };
2135 shaders.setUniform(ubufData, name: "qt_cameraDirection", data: &camDirection, size: 3 * sizeof(float), storeIndex: &cui.cameraDirectionIdx);
2136 } else {
2137 QVarLengthArray<QVector3D, 2> camGlobalPos(viewCount);
2138 QVarLengthArray<QVector3D> camDirection(viewCount);
2139 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
2140 camGlobalPos[viewIndex] = inCameras[viewIndex]->getGlobalPos();
2141 camDirection[viewIndex] = QSSG_GUARD(inRenderProperties.renderedCameraData.has_value())
2142 ? inRenderProperties.renderedCameraData.value()[viewIndex].direction
2143 : QVector3D{ 0.0f, 0.0f, -1.0f };
2144 }
2145 shaders.setUniformArray(ubufData, name: "qt_cameraPosition", data: camGlobalPos.constData(), itemCount: viewCount, type: QSSGRenderShaderValue::Vec3, storeIndex: &cui.cameraPositionIdx);
2146 shaders.setUniformArray(ubufData, name: "qt_cameraDirection", data: camDirection.constData(), itemCount: viewCount, type: QSSGRenderShaderValue::Vec3, storeIndex: &cui.cameraDirectionIdx);
2147 }
2148
2149 const auto globalRenderData = QSSGLayerRenderData::globalRenderProperties(ctx: renderContext);
2150
2151 // Only calculate and update Matrix uniforms if they are needed
2152 bool usesProjectionMatrix = false;
2153 bool usesInvProjectionMatrix = false;
2154 bool usesViewProjectionMatrix = false;
2155 bool usesModelViewProjectionMatrix = false;
2156 bool usesNormalMatrix = false;
2157 bool usesParentMatrix = false;
2158
2159 if (inMaterial.type == QSSGRenderGraphObject::Type::CustomMaterial) {
2160 const auto *customMaterial = static_cast<const QSSGRenderCustomMaterial *>(&inMaterial);
2161 usesProjectionMatrix = customMaterial->m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix);
2162 usesInvProjectionMatrix = customMaterial->m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix);
2163 // ### these should use flags like the above two
2164 usesViewProjectionMatrix = true;
2165 }
2166
2167 const bool usesInstancing = inProperties.m_usesInstancing.getValue(inDataStore: inKey);
2168 if (usesInstancing) {
2169 // Instanced calls have to calculate MVP and normalMatrix in the vertex shader
2170 usesViewProjectionMatrix = true;
2171 usesParentMatrix = true;
2172 } else {
2173 usesModelViewProjectionMatrix = true;
2174 usesNormalMatrix = true;
2175 }
2176
2177 if (materialAdapter->isTransmissionEnabled())
2178 usesViewProjectionMatrix = true;
2179
2180 // Update matrix uniforms
2181 if (usesProjectionMatrix || usesInvProjectionMatrix) {
2182 if (viewCount < 2) {
2183 const QMatrix4x4 projection = clipSpaceCorrMatrix * inCameras[0]->projection;
2184 if (usesProjectionMatrix)
2185 shaders.setUniform(ubufData, name: "qt_projectionMatrix", data: projection.constData(), size: 16 * sizeof(float), storeIndex: &cui.projectionMatrixIdx);
2186 if (usesInvProjectionMatrix)
2187 shaders.setUniform(ubufData, name: "qt_inverseProjectionMatrix", data: projection.inverted().constData(), size: 16 * sizeof (float), storeIndex: &cui.inverseProjectionMatrixIdx);
2188 } else {
2189 QVarLengthArray<QMatrix4x4, 2> projections(viewCount);
2190 QVarLengthArray<QMatrix4x4, 2> invertedProjections(viewCount);
2191 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
2192 projections[viewIndex] = clipSpaceCorrMatrix * inCameras[viewIndex]->projection;
2193 if (usesInvProjectionMatrix)
2194 invertedProjections[viewIndex] = projections[viewIndex].inverted();
2195 }
2196 if (usesProjectionMatrix)
2197 shaders.setUniformArray(ubufData, name: "qt_projectionMatrix", data: projections.constData(), itemCount: viewCount, type: QSSGRenderShaderValue::Matrix4x4, storeIndex: &cui.projectionMatrixIdx);
2198 if (usesInvProjectionMatrix)
2199 shaders.setUniformArray(ubufData, name: "qt_inverseProjectionMatrix", data: invertedProjections.constData(), itemCount: viewCount, type: QSSGRenderShaderValue::Matrix4x4, storeIndex: &cui.inverseProjectionMatrixIdx);
2200 }
2201 }
2202
2203 if (viewCount < 2) {
2204 const QMatrix4x4 viewMatrix = inCameras[0]->globalTransform.inverted();
2205 shaders.setUniform(ubufData, name: "qt_viewMatrix", data: viewMatrix.constData(), size: 16 * sizeof(float), storeIndex: &cui.viewMatrixIdx);
2206 } else {
2207 QVarLengthArray<QMatrix4x4, 2> viewMatrices(viewCount);
2208 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
2209 viewMatrices[viewIndex] = inCameras[viewIndex]->globalTransform.inverted();
2210 shaders.setUniformArray(ubufData, name: "qt_viewMatrix", data: viewMatrices.constData(), itemCount: viewCount, type: QSSGRenderShaderValue::Matrix4x4, storeIndex: &cui.viewMatrixIdx);
2211 }
2212
2213 if (usesViewProjectionMatrix) {
2214 if (viewCount < 2) {
2215 QMatrix4x4 viewProj(Qt::Uninitialized);
2216 inCameras[0]->calculateViewProjectionMatrix(outMatrix&: viewProj);
2217 viewProj = clipSpaceCorrMatrix * viewProj;
2218 shaders.setUniform(ubufData, name: "qt_viewProjectionMatrix", data: viewProj.constData(), size: 16 * sizeof(float), storeIndex: &cui.viewProjectionMatrixIdx);
2219 } else {
2220 QVarLengthArray<QMatrix4x4, 2> viewProjections(viewCount);
2221 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
2222 inCameras[viewIndex]->calculateViewProjectionMatrix(outMatrix&: viewProjections[viewIndex]);
2223 viewProjections[viewIndex] = clipSpaceCorrMatrix * viewProjections[viewIndex];
2224 }
2225 shaders.setUniformArray(ubufData, name: "qt_viewProjectionMatrix", data: viewProjections.constData(), itemCount: viewCount, type: QSSGRenderShaderValue::Matrix4x4, storeIndex: &cui.viewProjectionMatrixIdx);
2226 }
2227 }
2228
2229 // qt_modelMatrix is always available, but differnt when using instancing
2230 if (usesInstancing)
2231 shaders.setUniform(ubufData, name: "qt_modelMatrix", data: localInstanceTransform.constData(), size: 16 * sizeof(float), storeIndex: &cui.modelMatrixIdx);
2232 else
2233 shaders.setUniform(ubufData, name: "qt_modelMatrix", data: inGlobalTransform.constData(), size: 16 * sizeof(float), storeIndex: &cui.modelMatrixIdx);
2234
2235 if (usesModelViewProjectionMatrix) {
2236 if (viewCount < 2) {
2237 QMatrix4x4 mvp { clipSpaceCorrMatrix };
2238 mvp *= inModelViewProjections[0];
2239 shaders.setUniform(ubufData, name: "qt_modelViewProjection", data: mvp.constData(), size: 16 * sizeof(float), storeIndex: &cui.modelViewProjectionIdx);
2240 } else {
2241 QVarLengthArray<QMatrix4x4, 2> mvps(viewCount);
2242 for (int viewIndex = 0; viewIndex < viewCount; ++viewIndex)
2243 mvps[viewIndex] = clipSpaceCorrMatrix * inModelViewProjections[viewIndex];
2244 shaders.setUniformArray(ubufData, name: "qt_modelViewProjection", data: mvps.constData(), itemCount: viewCount, type: QSSGRenderShaderValue::Matrix4x4, storeIndex: &cui.modelViewProjectionIdx);
2245 }
2246 }
2247 if (usesNormalMatrix)
2248 shaders.setUniform(ubufData, name: "qt_normalMatrix", data: inNormalMatrix.constData(), size: 12 * sizeof(float), storeIndex: &cui.normalMatrixIdx,
2249 flags: QSSGRhiShaderPipeline::UniformFlag::Mat3); // real size will be 12 floats, setUniform repacks as needed
2250 if (usesParentMatrix)
2251 shaders.setUniform(ubufData, name: "qt_parentMatrix", data: globalInstanceTransform.constData(), size: 16 * sizeof(float));
2252
2253 // Morphing
2254 const qsizetype morphSize = inProperties.m_targetCount.getValue(inDataStore: inKey);
2255 if (morphSize > 0) {
2256 if (inMorphWeights.mSize >= morphSize) {
2257 shaders.setUniformArray(ubufData, name: "qt_morphWeights", data: inMorphWeights.mData, itemCount: morphSize,
2258 type: QSSGRenderShaderValue::Float, storeIndex: &cui.morphWeightsIdx);
2259 } else {
2260 const QList<float> zeroWeights(morphSize - inMorphWeights.mSize, 0.0f);
2261 QList<float> newWeights(inMorphWeights.mData, inMorphWeights.mData + inMorphWeights.mSize);
2262 newWeights.append(l: zeroWeights);
2263 shaders.setUniformArray(ubufData, name: "qt_morphWeights", data: newWeights.constData(), itemCount: morphSize,
2264 type: QSSGRenderShaderValue::Float, storeIndex: &cui.morphWeightsIdx);
2265 }
2266 }
2267
2268 QVector3D theLightAmbientTotal;
2269 shaders.resetShadowMaps();
2270 float lightColor[QSSG_MAX_NUM_LIGHTS][3];
2271 QSSGShaderLightsUniformData &lightsUniformData(shaders.lightsUniformData());
2272 lightsUniformData.count = 0;
2273 QSSGShaderShadowsUniformData &shadowsUniformData(shaders.shadowsUniformData());
2274 shadowsUniformData.count = 0;
2275
2276 for (quint32 lightIdx = 0, lightEnd = inLights.size();
2277 lightIdx < lightEnd && lightIdx < QSSG_MAX_NUM_LIGHTS; ++lightIdx)
2278 {
2279 QSSGRenderLight *theLight(inLights[lightIdx].light);
2280 const bool lightShadows = inLights[lightIdx].shadows;
2281 const float brightness = theLight->m_brightness;
2282 lightColor[lightIdx][0] = theLight->m_diffuseColor.x() * brightness;
2283 lightColor[lightIdx][1] = theLight->m_diffuseColor.y() * brightness;
2284 lightColor[lightIdx][2] = theLight->m_diffuseColor.z() * brightness;
2285 lightsUniformData.count += 1;
2286 QSSGShaderLightData &lightData(lightsUniformData.lightData[lightIdx]);
2287 const QVector3D &lightSpecular(theLight->m_specularColor);
2288 lightData.specular[0] = lightSpecular.x() * brightness;
2289 lightData.specular[1] = lightSpecular.y() * brightness;
2290 lightData.specular[2] = lightSpecular.z() * brightness;
2291 lightData.specular[3] = 1.0f;
2292 const QVector3D &lightDirection(inLights[lightIdx].direction);
2293 lightData.direction[0] = lightDirection.x();
2294 lightData.direction[1] = lightDirection.y();
2295 lightData.direction[2] = lightDirection.z();
2296 lightData.direction[3] = 1.0f;
2297
2298 // When it comes to receivesShadows, it is a bit tricky: to stay
2299 // compatible with the old, direct OpenGL rendering path (and the
2300 // generated shader code), we will need to ensure the texture
2301 // (shadowmap0, shadowmap1, ...) and sampler bindings are present.
2302 // So receivesShadows must not be included in the following
2303 // condition. Instead, it is the other shadow-related uniforms that
2304 // get an all-zero value, which then ensures no shadow contribution
2305 // for the object in question.
2306
2307 if (lightShadows && shadowsUniformData.count < QSSG_MAX_NUM_SHADOW_MAPS) {
2308 QSSGRhiShadowMapProperties &theShadowMapProperties(shaders.addShadowMap());
2309 QSSGShadowMapEntry *pEntry = inRenderProperties.getShadowMapManager()->shadowMapEntry(lightIdx);
2310 Q_ASSERT(pEntry);
2311
2312 const int shadowMapIdx = shadowsUniformData.count;
2313 const auto& names = setupShadowMapVariableNames(shadowMapIdx, shadowMapRes: theLight->m_shadowMapRes);
2314
2315 QSSGShaderShadowData &shadowData(shadowsUniformData.shadowData[shadowMapIdx]);
2316
2317 if (theLight->type == QSSGRenderLight::Type::DirectionalLight ||
2318 theLight->type == QSSGRenderLight::Type::SpotLight) {
2319 theShadowMapProperties.shadowMapTexture = pEntry->m_rhiDepthTextureArray;
2320 theShadowMapProperties.shadowMapTextureUniformName = names.shadowMapTexture;
2321 } else if (theLight->type == QSSGRenderLight::Type::PointLight) {
2322 theShadowMapProperties.shadowMapTexture = pEntry->m_rhiDepthCube;
2323 theShadowMapProperties.shadowMapTextureUniformName = names.shadowCube;
2324 } else {
2325 Q_UNREACHABLE();
2326 }
2327
2328 if (receivesShadows) {
2329 if (theLight->type == QSSGRenderLight::Type::DirectionalLight ||
2330 theLight->type == QSSGRenderLight::Type::SpotLight) {
2331 // add fixed scale bias matrix
2332 static const QMatrix4x4 bias = {
2333 0.5, 0.0, 0.0, 0.5,
2334 0.0, 0.5, 0.0, 0.5,
2335 0.0, 0.0, 0.5, 0.5,
2336 0.0, 0.0, 0.0, 1.0 };
2337 for (int i = 0; i < 4; i++) {
2338 const QMatrix4x4 m = bias * pEntry->m_lightViewProjection[i];
2339 memcpy(dest: shadowData.matrices[i], src: m.constData(), n: 16 * sizeof(float));
2340 }
2341 } else {
2342 Q_ASSERT(theLight->type == QSSGRenderLight::Type::PointLight);
2343 memcpy(dest: &shadowData.matrices[0], src: pEntry->m_lightView.constData(), n: 16 * sizeof(float));
2344 }
2345
2346 shadowData.bias = theLight->m_shadowBias;
2347 // Optimization: if directional light with no cascades then set shadowFactor to 0 to disable shadow map sampling
2348 const bool noCascades = !(pEntry->m_csmActive[0] || pEntry->m_csmActive[1] || pEntry->m_csmActive[2] || pEntry->m_csmActive[3]);
2349 shadowData.factor = theLight->type == QSSGRenderLight::Type::DirectionalLight && noCascades ? 0.0f : theLight->m_shadowFactor;
2350 shadowData.clipNear = 1.0f;
2351 shadowData.shadowMapFar = pEntry->m_shadowMapFar;
2352 shadowData.isYUp = globalRenderData.isYUpInFramebuffer ? 0.0f : 1.0f;
2353 shadowData.layerIndex = pEntry->m_depthArrayIndex;
2354 shadowData.csmNumSplits = pEntry->m_csmNumSplits;
2355 memcpy(dest: shadowData.csmSplits, src: pEntry->m_csmSplits, n: 4 * sizeof(float));
2356 memcpy(dest: shadowData.csmActive, src: pEntry->m_csmActive, n: 4 * sizeof(float));
2357 shadowData.csmBlendRatio = theLight->m_csmBlendRatio;
2358 for (int i = 0; i < 4; i++) {
2359 QMatrix4x4 inv = pEntry->m_lightViewProjection[i].inverted();
2360 const float x = 0.5f / (inv * QVector4D(1, 0, 0, 0)).length();
2361 const float y = 0.5f / (inv * QVector4D(0, 1, 0, 0)).length();
2362 const float z = 0.5f / (inv * QVector4D(0, 0, 1, 0)).length();
2363 QVector4D dimensionsInverted = QVector4D(x, y, z, 0);
2364 memcpy(dest: shadowData.dimensionsInverted[i], src: &dimensionsInverted, n: 4 * sizeof(float));
2365 }
2366 shadowData.pcfFactor = theLight->m_pcfFactor;
2367 } else {
2368 memset(s: &shadowData, c: '\0', n: sizeof(shadowData));
2369 }
2370
2371 ++shadowsUniformData.count;
2372 }
2373
2374 if (theLight->type == QSSGRenderLight::Type::PointLight
2375 || theLight->type == QSSGRenderLight::Type::SpotLight) {
2376 const QVector3D globalPos = theLight->getGlobalPos();
2377 lightData.position[0] = globalPos.x();
2378 lightData.position[1] = globalPos.y();
2379 lightData.position[2] = globalPos.z();
2380 lightData.position[3] = 1.0f;
2381 lightData.constantAttenuation = QSSGUtils::aux::translateConstantAttenuation(attenuation: theLight->m_constantFade);
2382 lightData.linearAttenuation = QSSGUtils::aux::translateLinearAttenuation(attenuation: theLight->m_linearFade);
2383 lightData.quadraticAttenuation = QSSGUtils::aux::translateQuadraticAttenuation(attenuation: theLight->m_quadraticFade);
2384 lightData.coneAngle = 180.0f;
2385 if (theLight->type == QSSGRenderLight::Type::SpotLight) {
2386 const float coneAngle = theLight->m_coneAngle;
2387 const float innerConeAngle = (theLight->m_innerConeAngle > coneAngle) ?
2388 coneAngle : theLight->m_innerConeAngle;
2389 lightData.coneAngle = qCos(v: qDegreesToRadians(degrees: coneAngle));
2390 lightData.innerConeAngle = qCos(v: qDegreesToRadians(degrees: innerConeAngle));
2391 }
2392 }
2393
2394 theLightAmbientTotal += theLight->m_ambientColor;
2395 }
2396
2397 const QSSGRhiRenderableTexture *depthTexture = inRenderProperties.getRenderResult(id: QSSGFrameData::RenderResult::DepthTexture);
2398 const QSSGRhiRenderableTexture *ssaoTexture = inRenderProperties.getRenderResult(id: QSSGFrameData::RenderResult::AoTexture);
2399 const QSSGRhiRenderableTexture *screenTexture = inRenderProperties.getRenderResult(id: QSSGFrameData::RenderResult::ScreenTexture);
2400
2401 shaders.setDepthTexture(depthTexture->texture);
2402 shaders.setSsaoTexture(ssaoTexture->texture);
2403 shaders.setScreenTexture(screenTexture->texture);
2404 shaders.setLightmapTexture(lightmapTexture);
2405
2406 const QSSGRenderLayer &layer = QSSGLayerRenderData::getCurrent(renderer: *renderContext.renderer())->layer;
2407 QSSGRenderImage *theLightProbe = layer.lightProbe;
2408 const auto &lightProbeData = layer.lightProbeSettings;
2409
2410 // If the material has its own IBL Override, we should use that image instead.
2411 QSSGRenderImage *materialIblProbe = materialAdapter->iblProbe();
2412 if (materialIblProbe)
2413 theLightProbe = materialIblProbe;
2414 QSSGRenderImageTexture lightProbeTexture;
2415 if (theLightProbe)
2416 lightProbeTexture = renderContext.bufferManager()->loadRenderImage(image: theLightProbe, inMipMode: QSSGBufferManager::MipModeBsdf);
2417 if (theLightProbe && lightProbeTexture.m_texture) {
2418 QSSGRenderTextureCoordOp theHorzLightProbeTilingMode = theLightProbe->m_horizontalTilingMode;
2419 QSSGRenderTextureCoordOp theVertLightProbeTilingMode = theLightProbe->m_verticalTilingMode;
2420 const int maxMipLevel = lightProbeTexture.m_mipmapCount - 1;
2421
2422 if (!materialIblProbe && !lightProbeData.probeOrientation.isIdentity()) {
2423 shaders.setUniform(ubufData, name: "qt_lightProbeOrientation",
2424 data: lightProbeData.probeOrientation.constData(),
2425 size: 12 * sizeof(float), storeIndex: &cui.lightProbeOrientationIdx,
2426 flags: QSSGRhiShaderPipeline::UniformFlag::Mat3);
2427 }
2428
2429 const float props[4] = { 0.0f, float(maxMipLevel), lightProbeData.probeHorizon, lightProbeData.probeExposure };
2430 shaders.setUniform(ubufData, name: "qt_lightProbeProperties", data: props, size: 4 * sizeof(float), storeIndex: &cui.lightProbePropertiesIdx);
2431
2432 shaders.setLightProbeTexture(texture: lightProbeTexture.m_texture, hTile: theHorzLightProbeTilingMode, vTile: theVertLightProbeTilingMode);
2433 } else {
2434 // no lightprobe
2435 const float emptyProps[4] = { 0.0f, 0.0f, -1.0f, 0.0f };
2436 shaders.setUniform(ubufData, name: "qt_lightProbeProperties", data: emptyProps, size: 4 * sizeof(float), storeIndex: &cui.lightProbePropertiesIdx);
2437
2438 shaders.setLightProbeTexture(texture: nullptr);
2439 }
2440
2441 if (receivesReflections && reflectionProbe.enabled) {
2442 shaders.setUniform(ubufData, name: "qt_reflectionProbeCubeMapCenter", data: &reflectionProbe.probeCubeMapCenter, size: 3 * sizeof(float), storeIndex: &cui.reflectionProbeCubeMapCenter);
2443 shaders.setUniform(ubufData, name: "qt_reflectionProbeBoxMin", data: &reflectionProbe.probeBoxMin, size: 3 * sizeof(float), storeIndex: &cui.reflectionProbeBoxMin);
2444 shaders.setUniform(ubufData, name: "qt_reflectionProbeBoxMax", data: &reflectionProbe.probeBoxMax, size: 3 * sizeof(float), storeIndex: &cui.reflectionProbeBoxMax);
2445 shaders.setUniform(ubufData, name: "qt_reflectionProbeCorrection", data: &reflectionProbe.parallaxCorrection, size: sizeof(int), storeIndex: &cui.reflectionProbeCorrection);
2446 }
2447
2448 const QVector3D emissiveColor = materialAdapter->emissiveColor();
2449 shaders.setUniform(ubufData, name: "qt_material_emissive_color", data: &emissiveColor, size: 3 * sizeof(float), storeIndex: &cui.material_emissiveColorIdx);
2450
2451 const auto qMix = [](float x, float y, float a) {
2452 return (x * (1.0f - a) + (y * a));
2453 };
2454
2455 const auto qMix3 = [&qMix](const QVector3D &x, const QVector3D &y, float a) {
2456 return QVector3D{qMix(x.x(), y.x(), a), qMix(x.y(), y.y(), a), qMix(x.z(), y.z(), a)};
2457 };
2458
2459 const QVector4D color = materialAdapter->color();
2460 const QVector3D materialSpecularTint = materialAdapter->specularTint();
2461 const QVector3D specularTint = materialAdapter->isPrincipled() ? qMix3(QVector3D(1.0f, 1.0f, 1.0f), color.toVector3D(), materialSpecularTint.x())
2462 : materialSpecularTint;
2463 shaders.setUniform(ubufData, name: "qt_material_base_color", data: &color, size: 4 * sizeof(float), storeIndex: &cui.material_baseColorIdx);
2464
2465 const float ior = materialAdapter->ior();
2466 QVector4D specularColor(specularTint, ior);
2467 shaders.setUniform(ubufData, name: "qt_material_specular", data: &specularColor, size: 4 * sizeof(float), storeIndex: &cui.material_specularIdx);
2468
2469 // metalnessAmount cannot be multiplied in here yet due to custom materials
2470 const bool hasLighting = materialAdapter->hasLighting();
2471 shaders.setLightsEnabled(hasLighting);
2472 if (hasLighting) {
2473 for (int lightIdx = 0; lightIdx < lightsUniformData.count; ++lightIdx) {
2474 QSSGShaderLightData &lightData(lightsUniformData.lightData[lightIdx]);
2475 lightData.diffuse[0] = lightColor[lightIdx][0];
2476 lightData.diffuse[1] = lightColor[lightIdx][1];
2477 lightData.diffuse[2] = lightColor[lightIdx][2];
2478 lightData.diffuse[3] = 1.0f;
2479 }
2480 memcpy(dest: ubufData + shaders.ub0LightDataOffset(), src: &lightsUniformData, n: shaders.ub0LightDataSize());
2481
2482 if (shadowsUniformData.count)
2483 memcpy(dest: ubufData + shaders.ub0ShadowDataOffset(), src: &shadowsUniformData, n: shaders.ub0ShadowDataSize());
2484 }
2485
2486 shaders.setUniform(ubufData, name: "qt_light_ambient_total", data: &theLightAmbientTotal, size: 3 * sizeof(float), storeIndex: &cui.light_ambient_totalIdx);
2487
2488 const float materialProperties[4] = {
2489 materialAdapter->specularAmount(),
2490 materialAdapter->specularRoughness(),
2491 materialAdapter->metalnessAmount(),
2492 inOpacity
2493 };
2494 shaders.setUniform(ubufData, name: "qt_material_properties", data: materialProperties, size: 4 * sizeof(float), storeIndex: &cui.material_propertiesIdx);
2495
2496 const float materialProperties2[4] = {
2497 materialAdapter->fresnelPower(),
2498 materialAdapter->bumpAmount(),
2499 materialAdapter->translucentFallOff(),
2500 materialAdapter->diffuseLightWrap()
2501 };
2502 shaders.setUniform(ubufData, name: "qt_material_properties2", data: materialProperties2, size: 4 * sizeof(float), storeIndex: &cui.material_properties2Idx);
2503
2504 const float materialProperties3[4] = {
2505 materialAdapter->occlusionAmount(),
2506 materialAdapter->alphaCutOff(),
2507 materialAdapter->clearcoatAmount(),
2508 materialAdapter->clearcoatRoughnessAmount()
2509 };
2510 shaders.setUniform(ubufData, name: "qt_material_properties3", data: materialProperties3, size: 4 * sizeof(float), storeIndex: &cui.material_properties3Idx);
2511
2512 const float materialProperties4[4] = {
2513 materialAdapter->heightAmount(),
2514 materialAdapter->minHeightSamples(),
2515 materialAdapter->maxHeightSamples(),
2516 materialAdapter->transmissionFactor()
2517 };
2518 shaders.setUniform(ubufData, name: "qt_material_properties4", data: materialProperties4, size: 4 * sizeof(float), storeIndex: &cui.material_properties4Idx);
2519
2520 const bool hasCustomFrag = materialAdapter->hasCustomShaderSnippet(type: QSSGShaderCache::ShaderType::Fragment);
2521 if (!hasCustomFrag) {
2522 if (inProperties.m_fresnelScaleBiasEnabled.getValue(inDataStore: inKey) || inProperties.m_clearcoatFresnelScaleBiasEnabled.getValue(inDataStore: inKey)) {
2523 const float materialProperties5[4] = {
2524 materialAdapter->fresnelScale(),
2525 materialAdapter->fresnelBias(),
2526 materialAdapter->clearcoatFresnelScale(),
2527 materialAdapter->clearcoatFresnelBias()
2528 };
2529 shaders.setUniform(ubufData, name: "qt_material_properties5", data: materialProperties5, size: 4 * sizeof(float), storeIndex: &cui.material_properties5Idx);
2530 }
2531
2532 const float material_clearcoat_normal_strength = materialAdapter->clearcoatNormalStrength();
2533 shaders.setUniform(ubufData, name: "qt_material_clearcoat_normal_strength", data: &material_clearcoat_normal_strength, size: sizeof(float), storeIndex: &cui.clearcoatNormalStrengthIdx);
2534
2535 const float material_clearcoat_fresnel_power = materialAdapter->clearcoatFresnelPower();
2536 shaders.setUniform(ubufData, name: "qt_material_clearcoat_fresnel_power", data: &material_clearcoat_fresnel_power, size: sizeof(float), storeIndex: &cui.clearcoatFresnelPowerIdx);
2537 // We only ever use attenuation and thickness uniforms when using transmission
2538 if (materialAdapter->isTransmissionEnabled()) {
2539 const QVector4D attenuationProperties(materialAdapter->attenuationColor(), materialAdapter->attenuationDistance());
2540 shaders.setUniform(ubufData, name: "qt_material_attenuation", data: &attenuationProperties, size: 4 * sizeof(float), storeIndex: &cui.material_attenuationIdx);
2541
2542 const float thickness = materialAdapter->thicknessFactor();
2543 shaders.setUniform(ubufData, name: "qt_material_thickness", data: &thickness, size: sizeof(float), storeIndex: &cui.thicknessFactorIdx);
2544 }
2545 }
2546
2547 const float rhiProperties[4] = {
2548 globalRenderData.isYUpInFramebuffer ? 1.0f : -1.0f,
2549 globalRenderData.isYUpInNDC ? 1.0f : -1.0f,
2550 globalRenderData.isClipDepthZeroToOne ? 0.0f : -1.0f,
2551 0.0f // unused
2552 };
2553 shaders.setUniform(ubufData, name: "qt_rhi_properties", data: rhiProperties, size: 4 * sizeof(float), storeIndex: &cui.rhiPropertiesIdx);
2554
2555 qsizetype imageIdx = 0;
2556 for (QSSGRenderableImage *theImage = inFirstImage; theImage; theImage = theImage->m_nextImage, ++imageIdx) {
2557 // we need to map image to uniform name: "image0_rotations", "image0_offsets", etc...
2558 const auto &names = imageStringTable[int(theImage->m_mapType)];
2559 if (imageIdx == cui.imageIndices.size())
2560 cui.imageIndices.append(t: QSSGRhiShaderPipeline::CommonUniformIndices::ImageIndices());
2561 auto &indices = cui.imageIndices[imageIdx];
2562
2563 const QMatrix4x4 &textureTransform = theImage->m_imageNode.m_textureTransform;
2564 // We separate rotational information from offset information so that just maybe the shader
2565 // will attempt to push less information to the card.
2566 const float *dataPtr(textureTransform.constData());
2567 // The third member of the offsets contains a flag indicating if the texture was
2568 // premultiplied or not.
2569 // We use this to mix the texture alpha.
2570 const float offsets[3] = { dataPtr[12], dataPtr[13], 0.0f /* non-premultiplied */ };
2571 shaders.setUniform(ubufData, name: names.imageOffsets, data: offsets, size: sizeof(offsets), storeIndex: &indices.imageOffsetsUniformIndex);
2572 // Grab just the upper 2x2 rotation matrix from the larger matrix.
2573 const float rotations[4] = { dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5] };
2574 shaders.setUniform(ubufData, name: names.imageRotations, data: rotations, size: sizeof(rotations), storeIndex: &indices.imageRotationsUniformIndex);
2575 }
2576
2577 if (shadowDepthAdjust)
2578 shaders.setUniform(ubufData, name: "qt_shadowDepthAdjust", data: shadowDepthAdjust, size: 2 * sizeof(float), storeIndex: &cui.shadowDepthAdjustIdx);
2579
2580 const bool usesPointsTopology = inProperties.m_usesPointsTopology.getValue(inDataStore: inKey);
2581 if (usesPointsTopology) {
2582 const float pointSize = materialAdapter->pointSize();
2583 shaders.setUniform(ubufData, name: "qt_materialPointSize", data: &pointSize, size: sizeof(float), storeIndex: &cui.pointSizeIdx);
2584 }
2585
2586 // qt_fogColor = (fogColor.x, fogColor.y, fogColor.z, fogDensity)
2587 // qt_fogDepthProperties = (fogDepthBegin, fogDepthEnd, fogDepthCurve, fogDepthEnabled ? 1.0 : 0.0)
2588 // qt_fogHeightProperties = (fogHeightMin, fogHeightMax, fogHeightCurve, fogHeightEnabled ? 1.0 : 0.0)
2589 // qt_fogTransmitProperties = (fogTransmitCurve, 0.0, 0.0, fogTransmitEnabled ? 1.0 : 0.0)
2590 if (inRenderProperties.layer.fog.enabled) {
2591 const float fogColor[4] = {
2592 inRenderProperties.layer.fog.color.x(),
2593 inRenderProperties.layer.fog.color.y(),
2594 inRenderProperties.layer.fog.color.z(),
2595 inRenderProperties.layer.fog.density
2596 };
2597 shaders.setUniform(ubufData, name: "qt_fogColor", data: fogColor, size: 4 * sizeof(float), storeIndex: &cui.fogColorIdx);
2598 const float fogDepthProperties[4] = {
2599 inRenderProperties.layer.fog.depthBegin,
2600 inRenderProperties.layer.fog.depthEnd,
2601 inRenderProperties.layer.fog.depthCurve,
2602 inRenderProperties.layer.fog.depthEnabled ? 1.0f : 0.0f
2603 };
2604 shaders.setUniform(ubufData, name: "qt_fogDepthProperties", data: fogDepthProperties, size: 4 * sizeof(float), storeIndex: &cui.fogDepthPropertiesIdx);
2605 const float fogHeightProperties[4] = {
2606 inRenderProperties.layer.fog.heightMin,
2607 inRenderProperties.layer.fog.heightMax,
2608 inRenderProperties.layer.fog.heightCurve,
2609 inRenderProperties.layer.fog.heightEnabled ? 1.0f : 0.0f
2610 };
2611 shaders.setUniform(ubufData, name: "qt_fogHeightProperties", data: fogHeightProperties, size: 4 * sizeof(float), storeIndex: &cui.fogHeightPropertiesIdx);
2612 const float fogTransmitProperties[4] = {
2613 inRenderProperties.layer.fog.transmitCurve,
2614 0.0f,
2615 0.0f,
2616 inRenderProperties.layer.fog.transmitEnabled ? 1.0f : 0.0f
2617 };
2618 shaders.setUniform(ubufData, name: "qt_fogTransmitProperties", data: fogTransmitProperties, size: 4 * sizeof(float), storeIndex: &cui.fogTransmitPropertiesIdx);
2619 }
2620
2621 inPipelineState->lineWidth = materialAdapter->lineWidth();
2622}
2623
2624QT_END_NAMESPACE
2625
2626QList<QByteArrayView> QtQuick3DEditorHelpers::CustomMaterial::reservedArgumentNames()
2627{
2628 return {std::begin(arr: qssg_shader_arg_names), std::end(arr: qssg_shader_arg_names) };;
2629}
2630

source code of qtquick3d/src/runtimerender/qssgrenderdefaultmaterialshadergenerator.cpp