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

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