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