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