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

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