1 /****************************************************************************
2 **
3 ** Copyright (C) 2008-2012 NVIDIA Corporation.
4 ** Copyright (C) 2019 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of Qt Quick 3D.
8 **
9 ** $QT_BEGIN_LICENSE:GPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 or (at your option) any later version
21 ** approved by the KDE Free Qt Foundation. The licenses are as published by
22 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
23 ** included in the packaging of this file. Please review the following
24 ** information to ensure the GNU General Public License requirements will
25 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 **
27 ** $QT_END_LICENSE$
28 **
29 ****************************************************************************/
30 
31 /* clang-format off */
32 
33 #include <QtQuick3DUtils/private/qssgutils_p.h>
34 
35 #include <QtQuick3DRender/private/qssgrendercontext_p.h>
36 #include <QtQuick3DRender/private/qssgrendershaderprogram_p.h>
37 #include <QtQuick3DRender/private/qssgrendershaderprogram_p.h>
38 
39 #include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
40 #include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
41 #include <QtQuick3DRuntimeRender/private/qssgrendershadercodegeneratorv2_p.h>
42 #include <QtQuick3DRuntimeRender/private/qssgrenderableimage_p.h>
43 #include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
44 #include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
45 #include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
46 #include <QtQuick3DRuntimeRender/private/qssgrendershadowmap_p.h>
47 #include <QtQuick3DRuntimeRender/private/qssgrendercustommaterial_p.h>
48 #include <QtQuick3DRuntimeRender/private/qssgrenderdynamicobjectsystem_p.h>
49 #include <QtQuick3DRuntimeRender/private/qssgrenderlightconstantproperties_p.h>
50 #include <QtQuick3DRuntimeRender/private/qssgrendershaderkeys_p.h>
51 #include <QtQuick3DRuntimeRender/private/qssgrendererimplshaders_p.h>
52 
53 #include <QtCore/QByteArray>
54 
55 QT_BEGIN_NAMESPACE
56 
57 namespace {
58 /**
59  *	Cached light property lookups, used one per light so a shader generator for N
60  *	lights will have an array of N of these lookup objects.
61  */
62 struct QSSGShaderLightProperties
63 {
64     // Color of the light
65     QVector3D lightColor;
66     QSSGLightSourceShader lightData;
67 };
68 
69 struct QSSGShadowMapProperties
70 {
71     QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_shadowmapTexture; ///< shadow texture
72     QSSGRenderCachedShaderProperty<QSSGRenderTextureCube *> m_shadowCubeTexture; ///< shadow cubemap
73     QSSGRenderCachedShaderProperty<QMatrix4x4> m_shadowmapMatrix; ///< world to ligh space transform matrix
74     QSSGRenderCachedShaderProperty<QVector4D> m_shadowmapSettings; ///< shadow rendering settings
75 
76     QSSGShadowMapProperties() = default;
QSSGShadowMapProperties__anon06cffe670111::QSSGShadowMapProperties77     QSSGShadowMapProperties(const QByteArray &shadowmapTextureName,
78                               const QByteArray &shadowcubeTextureName,
79                               const QByteArray &shadowmapMatrixName,
80                               const QByteArray &shadowmapSettingsName,
81                               const QSSGRef<QSSGRenderShaderProgram> &inShader)
82         : m_shadowmapTexture(shadowmapTextureName, inShader)
83         , m_shadowCubeTexture(shadowcubeTextureName, inShader)
84         , m_shadowmapMatrix(shadowmapMatrixName, inShader)
85         , m_shadowmapSettings(shadowmapSettingsName, inShader)
86     {
87     }
88 };
89 
90 /**
91  *	The results of generating a shader.  Caches all possible variable names into
92  *	typesafe objects.
93  */
94 struct QSSGShaderGeneratorGeneratedShader
95 {
96     QAtomicInt ref;
97     QSSGRef<QSSGRenderShaderProgram> m_shader;
98     // Specific properties we know the shader has to have.
99     QSSGRenderCachedShaderProperty<QMatrix4x4> m_mvp;
100     QSSGRenderCachedShaderProperty<QMatrix3x3> m_normalMatrix;
101     QSSGRenderCachedShaderProperty<QMatrix4x4> m_globalTransform;
102     QSSGRenderCachedShaderProperty<QMatrix4x4> m_viewProj;
103     QSSGRenderCachedShaderProperty<QMatrix4x4> m_viewMatrix;
104     QSSGRenderCachedShaderProperty<QVector3D> m_materialDiffuse;
105     QSSGRenderCachedShaderProperty<QVector4D> m_materialProperties;
106     // tint, ior
107     QSSGRenderCachedShaderProperty<QVector4D> m_materialSpecular;
108     QSSGRenderCachedShaderProperty<float> m_bumpAmount;
109     QSSGRenderCachedShaderProperty<float> m_displaceAmount;
110     QSSGRenderCachedShaderProperty<float> m_translucentFalloff;
111     QSSGRenderCachedShaderProperty<float> m_diffuseLightWrap;
112     QSSGRenderCachedShaderProperty<float> m_fresnelPower;
113     QSSGRenderCachedShaderProperty<float> m_occlusionAmount;
114     QSSGRenderCachedShaderProperty<float> m_alphaCutoff;
115     QSSGRenderCachedShaderProperty<QVector4D> m_baseColor;
116     QSSGRenderCachedShaderProperty<QVector3D> m_cameraPosition;
117     QSSGRenderCachedShaderProperty<QVector3D> m_cameraDirection;
118     QVector3D m_lightAmbientTotal;
119     QSSGRenderCachedShaderProperty<QVector3D> m_materialDiffuseLightAmbientTotal;
120     QSSGRenderCachedShaderProperty<QVector2D> m_cameraProperties;
121 
122     QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_depthTexture;
123     QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_aoTexture;
124     QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_lightProbe;
125     QSSGRenderCachedShaderProperty<QVector4D> m_lightProbeProps;
126     QSSGRenderCachedShaderProperty<QVector4D> m_lightProbeOpts;
127     QSSGRenderCachedShaderProperty<QVector4D> m_lightProbeRot;
128     QSSGRenderCachedShaderProperty<QVector4D> m_lightProbeOfs;
129     QSSGRenderCachedShaderProperty<QVector2D> m_lightProbeSize;
130     QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_lightProbe2;
131     QSSGRenderCachedShaderProperty<QVector4D> m_lightProbe2Props;
132     QSSGRenderCachedShaderProperty<QVector2D> m_lightProbe2Size;
133 
134     QSSGRenderCachedShaderBuffer<QSSGRenderShaderConstantBuffer> m_aoShadowParams;
135     QSSGRenderCachedShaderBuffer<QSSGRenderShaderConstantBuffer> m_lightsBuffer;
136 
137     QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *m_lightConstantProperties = nullptr;
138 
139     // Cache the image property name lookups
140     QVector<QSSGShaderTextureProperties> m_images;
141     QVector<QSSGShaderLightProperties> m_lights;
142     // Cache shadow map properties
143     QVector<QSSGShadowMapProperties> m_shadowMaps;
144 
QSSGShaderGeneratorGeneratedShader__anon06cffe670111::QSSGShaderGeneratorGeneratedShader145     QSSGShaderGeneratorGeneratedShader(const QSSGRef<QSSGRenderShaderProgram> &inShader,
146                                          const QSSGRef<QSSGRenderContext> &inContext)
147         : m_shader(inShader)
148         , m_mvp("modelViewProjection", inShader)
149         , m_normalMatrix("normalMatrix", inShader)
150         , m_globalTransform("modelMatrix", inShader)
151         , m_viewProj("viewProjectionMatrix", inShader)
152         , m_viewMatrix("viewMatrix", inShader)
153         , m_materialDiffuse("material_diffuse", inShader)
154         , m_materialProperties("material_properties", inShader)
155         , m_materialSpecular("material_specular", inShader)
156         , m_bumpAmount("bumpAmount", inShader)
157         , m_displaceAmount("displaceAmount", inShader)
158         , m_translucentFalloff("translucentFalloff", inShader)
159         , m_diffuseLightWrap("diffuseLightWrap", inShader)
160         , m_fresnelPower("fresnelPower", inShader)
161         , m_occlusionAmount("occlusionAmount", inShader)
162         , m_alphaCutoff("alphaCutoff", inShader)
163         , m_baseColor("base_color", inShader)
164         , m_cameraPosition("cameraPosition", inShader)
165         , m_cameraDirection("cameraDirection", inShader)
166         , m_materialDiffuseLightAmbientTotal("light_ambient_total", inShader)
167         , m_cameraProperties("cameraProperties", inShader)
168         , m_depthTexture("depthTexture", inShader)
169         , m_aoTexture("aoTexture", inShader)
170         , m_lightProbe("lightProbe", inShader)
171         , m_lightProbeProps("lightProbeProperties", inShader)
172         , m_lightProbeOpts("lightProbeOptions", inShader)
173         , m_lightProbeRot("lightProbeRotation", inShader)
174         , m_lightProbeOfs("lightProbeOffset", inShader)
175         , m_lightProbeSize("lightProbeSize", inShader)
176         , m_lightProbe2("lightProbe2", inShader)
177         , m_lightProbe2Props("lightProbe2Properties", inShader)
178         , m_lightProbe2Size("lightProbe2Size", inShader)
179         , m_aoShadowParams("aoShadow", inShader)
180         , m_lightsBuffer("lightsBuffer", inShader)
181     {
182         Q_UNUSED(inContext)
183     }
~QSSGShaderGeneratorGeneratedShader__anon06cffe670111::QSSGShaderGeneratorGeneratedShader184     ~QSSGShaderGeneratorGeneratedShader() { delete m_lightConstantProperties; }
185 };
186 
187 struct QSSGShaderGenerator : public QSSGDefaultMaterialShaderGeneratorInterface
188 {
189     const QSSGRenderDefaultMaterial *m_currentMaterial;
190 
191     typedef QHash<QSSGRef<QSSGRenderShaderProgram>, QSSGRef<QSSGShaderGeneratorGeneratedShader>> ProgramToShaderMap;
192     ProgramToShaderMap m_programToShaderMap;
193 
194     QSSGRef<QSSGRenderShadowMap> m_shadowMapManager;
195     bool m_lightsAsSeparateUniforms;
196 
197     QByteArray m_imageSampler;
198     QByteArray m_imageFragCoords;
199     QByteArray m_imageOffsets;
200     QByteArray m_imageRotations;
201     QByteArray m_imageTemp;
202     QByteArray m_imageSamplerSize;
203 
204     QByteArray m_lightColor;
205     QByteArray m_lightSpecularColor;
206     QByteArray m_lightAttenuation;
207     QByteArray m_lightConstantAttenuation;
208     QByteArray m_lightLinearAttenuation;
209     QByteArray m_lightQuadraticAttenuation;
210     QByteArray m_normalizedDirection;
211     QByteArray m_lightDirection;
212     QByteArray m_lightPos;
213     QByteArray m_lightUp;
214     QByteArray m_lightRt;
215     QByteArray m_lightConeAngle;
216     QByteArray m_lightInnerConeAngle;
217     QByteArray m_relativeDistance;
218     QByteArray m_relativeDirection;
219     QByteArray m_spotAngle;
220 
221     QByteArray m_shadowMapStem;
222     QByteArray m_shadowCubeStem;
223     QByteArray m_shadowMatrixStem;
224     QByteArray m_shadowCoordStem;
225     QByteArray m_shadowControlStem;
226 
QSSGShaderGenerator__anon06cffe670111::QSSGShaderGenerator227     QSSGShaderGenerator(QSSGRenderContextInterface *inRc)
228         : QSSGDefaultMaterialShaderGeneratorInterface (inRc)
229         , m_shadowMapManager(nullptr)
230         , m_lightsAsSeparateUniforms(false)
231     {
232     }
233 
programGenerator__anon06cffe670111::QSSGShaderGenerator234     QSSGRef<QSSGShaderProgramGeneratorInterface> programGenerator() { return m_programGenerator; }
vertexGenerator__anon06cffe670111::QSSGShaderGenerator235     QSSGDefaultMaterialVertexPipelineInterface &vertexGenerator() { return *m_currentPipeline; }
fragmentGenerator__anon06cffe670111::QSSGShaderGenerator236     QSSGShaderStageGeneratorInterface &fragmentGenerator()
237     {
238         return *m_programGenerator->getStage(QSSGShaderGeneratorStage::Fragment);
239     }
key__anon06cffe670111::QSSGShaderGenerator240     QSSGShaderDefaultMaterialKey &key() { return *m_currentKey; }
material__anon06cffe670111::QSSGShaderGenerator241     const QSSGRenderDefaultMaterial *material() { return m_currentMaterial; }
hasTransparency__anon06cffe670111::QSSGShaderGenerator242     bool hasTransparency() { return m_hasTransparency; }
243 
addFunction__anon06cffe670111::QSSGShaderGenerator244     void addFunction(QSSGShaderStageGeneratorInterface &generator, const QByteArray &functionName)
245     {
246         generator.addFunction(functionName);
247     }
248 
setupImageVariableNames__anon06cffe670111::QSSGShaderGenerator249     void setupImageVariableNames(size_t imageIdx)
250     {
251         QByteArray imageStem = "image";
252         char buf[16];
253         qsnprintf(buf, 16, "%d", int(imageIdx));
254         imageStem.append(buf);
255         imageStem.append("_");
256 
257         m_imageSampler = imageStem;
258         m_imageSampler.append("sampler");
259         m_imageOffsets = imageStem;
260         m_imageOffsets.append("offsets");
261         m_imageRotations = imageStem;
262         m_imageRotations.append("rotations");
263         m_imageFragCoords = imageStem;
264         m_imageFragCoords.append("uv_coords");
265         m_imageSamplerSize = imageStem;
266         m_imageSamplerSize.append("size");
267     }
268 
textureCoordVariableName__anon06cffe670111::QSSGShaderGenerator269     QByteArray textureCoordVariableName(size_t uvSet)
270     {
271         QByteArray texCoordTemp = "varTexCoord";
272         char buf[16];
273         qsnprintf(buf, 16, "%d", int(uvSet));
274         texCoordTemp.append(buf);
275         return texCoordTemp;
276     }
277 
getImageVariableNames__anon06cffe670111::QSSGShaderGenerator278     ImageVariableNames getImageVariableNames(quint32 inIdx) override
279     {
280         setupImageVariableNames(inIdx);
281         ImageVariableNames retval;
282         retval.m_imageSampler = m_imageSampler;
283         retval.m_imageFragCoords = m_imageFragCoords;
284         return retval;
285     }
286 
addLocalVariable__anon06cffe670111::QSSGShaderGenerator287     void addLocalVariable(QSSGShaderStageGeneratorInterface &inGenerator, const QByteArray &inName, const QByteArray &inType)
288     {
289         inGenerator << "    " << inType << " " << inName << ";\n";
290     }
291 
uvTransform__anon06cffe670111::QSSGShaderGenerator292     QByteArray uvTransform()
293     {
294         QByteArray transform;
295         transform = "    uTransform = vec3(" + m_imageRotations + ".x, " + m_imageRotations + ".y, " + m_imageOffsets + ".x);\n";
296         transform += "    vTransform = vec3(" + m_imageRotations + ".z, " + m_imageRotations + ".w, " + m_imageOffsets + ".y);\n";
297         return transform;
298     }
299 
300     bool uvCoordsGenerated[32];
clearUVCoordsGen__anon06cffe670111::QSSGShaderGenerator301     void clearUVCoordsGen()
302     {
303         memset(uvCoordsGenerated, 0, sizeof(uvCoordsGenerated));
304     }
305 
generateImageUVCoordinates__anon06cffe670111::QSSGShaderGenerator306     void generateImageUVCoordinates(QSSGShaderStageGeneratorInterface &inVertexPipeline, quint32 idx, quint32 uvSet, QSSGRenderableImage &image) override
307     {
308         if (uvCoordsGenerated[idx])
309             return;
310         QSSGDefaultMaterialVertexPipelineInterface &vertexShader(
311                 static_cast<QSSGDefaultMaterialVertexPipelineInterface &>(inVertexPipeline));
312         QSSGShaderStageGeneratorInterface &fragmentShader(fragmentGenerator());
313         setupImageVariableNames(idx);
314         QByteArray textureCoordName = textureCoordVariableName(uvSet);
315         fragmentShader.addUniform(m_imageSampler, "sampler2D");
316         vertexShader.addUniform(m_imageOffsets, "vec3");
317         vertexShader.addUniform(m_imageRotations, "vec4");
318         QByteArray uvTrans = uvTransform();
319         if (image.m_image.m_mappingMode == QSSGRenderImage::MappingModes::Normal) {
320             vertexShader << uvTrans;
321             vertexShader.addOutgoing(m_imageFragCoords, "vec2");
322             addFunction(vertexShader, "getTransformedUVCoords");
323             vertexShader.generateUVCoords(key(), uvSet);
324             m_imageTemp = m_imageFragCoords;
325             m_imageTemp.append("temp");
326             vertexShader << "    vec2 " << m_imageTemp << " = getTransformedUVCoords(vec3(" << textureCoordName << ", 1.0), uTransform, vTransform);\n";
327             if (image.m_image.m_textureData.m_textureFlags.isInvertUVCoords())
328                 vertexShader << "    " << m_imageTemp << ".y = 1.0 - " << m_imageTemp << ".y;\n";
329 
330             vertexShader.assignOutput(m_imageFragCoords, m_imageTemp);
331         } else {
332             fragmentShader.addUniform(m_imageOffsets, "vec3");
333             fragmentShader.addUniform(m_imageRotations, "vec4");
334             fragmentShader << uvTrans;
335             vertexShader.generateEnvMapReflection(key());
336             addFunction(fragmentShader, "getTransformedUVCoords");
337             fragmentShader << "    vec2 " << m_imageFragCoords << " = getTransformedUVCoords(environment_map_reflection, uTransform, vTransform);\n";
338             if (image.m_image.m_textureData.m_textureFlags.isInvertUVCoords())
339                 fragmentShader << "    " << m_imageFragCoords << ".y = 1.0 - " << m_imageFragCoords << ".y;\n";
340         }
341         uvCoordsGenerated[idx] = true;
342     }
343 
generateImageUVSampler__anon06cffe670111::QSSGShaderGenerator344     void generateImageUVSampler(quint32 idx, quint32 uvSet = 0)
345     {
346         QSSGShaderStageGeneratorInterface &fragmentShader(fragmentGenerator());
347         setupImageVariableNames(idx);
348         fragmentShader.addUniform(m_imageSampler, "sampler2D");
349         m_imageFragCoords = textureCoordVariableName(uvSet);
350         vertexGenerator().generateUVCoords(key(), uvSet);
351     }
352 
generateImageUVCoordinates__anon06cffe670111::QSSGShaderGenerator353     void generateImageUVCoordinates(quint32 idx, QSSGRenderableImage &image, quint32 uvSet = 0)
354     {
355         generateImageUVCoordinates(vertexGenerator(), idx, uvSet, image);
356     }
357 
outputSpecularEquation__anon06cffe670111::QSSGShaderGenerator358     void outputSpecularEquation(QSSGRenderDefaultMaterial::MaterialSpecularModel inSpecularModel,
359                                 QSSGShaderStageGeneratorInterface &fragmentShader,
360                                 const QByteArray &inLightDir,
361                                 const QByteArray &inLightSpecColor)
362     {
363         switch (inSpecularModel) {
364         case QSSGRenderDefaultMaterial::MaterialSpecularModel::KGGX: {
365             fragmentShader.addInclude("defaultMaterialPhysGlossyBSDF.glsllib");
366             fragmentShader.addUniform("material_specular", "vec4");
367             fragmentShader << "    global_specular_light.rgb += lightAttenuation * specularAmount"
368                               " * kggxGlossyDefaultMtl(world_normal, tangent, -" << inLightDir << ".xyz, view_vector, " << inLightSpecColor << ".rgb, vec3(material_specular.rgb), roughnessAmount).rgb;\n";
369         } break;
370         case QSSGRenderDefaultMaterial::MaterialSpecularModel::KWard: {
371             fragmentShader.addInclude("defaultMaterialPhysGlossyBSDF.glsllib");
372             fragmentShader.addUniform("material_specular", "vec4");
373             fragmentShader << "    global_specular_light.rgb += lightAttenuation * specularAmount"
374                               " * wardGlossyDefaultMtl(world_normal, tangent, -" << inLightDir << ".xyz, view_vector, " << inLightSpecColor << ".rgb, vec3(material_specular.rgb), roughnessAmount).rgb;\n";
375         } break;
376         default:
377             addFunction(fragmentShader, "specularBSDF");
378             fragmentShader << "    global_specular_light.rgb += lightAttenuation * specularAmount"
379                               " * specularBSDF(world_normal, -" << inLightDir << ".xyz, view_vector, " << inLightSpecColor << ".rgb, 2.56 / (roughnessAmount + 0.01)).rgb;\n";
380             break;
381         }
382     }
383 
outputDiffuseAreaLighting__anon06cffe670111::QSSGShaderGenerator384     void outputDiffuseAreaLighting(QSSGShaderStageGeneratorInterface &infragmentShader, const QByteArray &inPos, const QByteArray &inLightPrefix)
385     {
386         m_normalizedDirection = inLightPrefix + "_areaDir";
387         addLocalVariable(infragmentShader, m_normalizedDirection, "vec3");
388         infragmentShader << "    lightAttenuation = calculateDiffuseAreaOld(" << m_lightDirection << ".xyz, " << m_lightPos << ".xyz, " << m_lightUp << ", " << m_lightRt << ", " << inPos << ", " << m_normalizedDirection << ");\n";
389     }
390 
outputSpecularAreaLighting__anon06cffe670111::QSSGShaderGenerator391     void outputSpecularAreaLighting(QSSGShaderStageGeneratorInterface &infragmentShader,
392                                     const QByteArray &inPos,
393                                     const QByteArray &inView,
394                                     const QByteArray &inLightSpecColor)
395     {
396         addFunction(infragmentShader, "sampleAreaGlossyDefault");
397         infragmentShader.addUniform("material_specular", "vec4");
398         infragmentShader << "global_specular_light.rgb += " << inLightSpecColor << ".rgb * lightAttenuation * shadowFac * material_specular.rgb * specularAmount"
399                             " * sampleAreaGlossyDefault(tanFrame, " << inPos << ", " << m_normalizedDirection << ", " << m_lightPos << ".xyz, " << m_lightRt << ".w, " << m_lightUp << ".w, " << inView << ", roughnessAmount).rgb;\n";
400     }
401 
addTranslucencyIrradiance__anon06cffe670111::QSSGShaderGenerator402     void addTranslucencyIrradiance(QSSGShaderStageGeneratorInterface &infragmentShader,
403                                    QSSGRenderableImage *image,
404                                    bool areaLight)
405     {
406         if (image == nullptr)
407             return;
408 
409         addFunction(infragmentShader, "diffuseReflectionWrapBSDF");
410         if (areaLight) {
411             infragmentShader << "    global_diffuse_light.rgb += lightAttenuation * translucent_thickness_exp * diffuseReflectionWrapBSDF(-world_normal, " << m_normalizedDirection << ", " << m_lightColor << ".rgb, diffuseLightWrap).rgb;\n";
412         } else {
413             infragmentShader << "    global_diffuse_light.rgb += lightAttenuation * translucent_thickness_exp * diffuseReflectionWrapBSDF(-world_normal, -" << m_normalizedDirection << ", " << m_lightColor << ".rgb, diffuseLightWrap).rgb;\n";
414         }
415     }
416 
setupShadowMapVariableNames__anon06cffe670111::QSSGShaderGenerator417     void setupShadowMapVariableNames(size_t lightIdx)
418     {
419         m_shadowMapStem = "shadowmap";
420         m_shadowCubeStem = "shadowcube";
421         char buf[16];
422         qsnprintf(buf, 16, "%d", int(lightIdx));
423         m_shadowMapStem.append(buf);
424         m_shadowCubeStem.append(buf);
425         m_shadowMatrixStem = m_shadowMapStem;
426         m_shadowMatrixStem.append("_matrix");
427         m_shadowCoordStem = m_shadowMapStem;
428         m_shadowCoordStem.append("_coord");
429         m_shadowControlStem = m_shadowMapStem;
430         m_shadowControlStem.append("_control");
431     }
432 
addShadowMapContribution__anon06cffe670111::QSSGShaderGenerator433     void addShadowMapContribution(QSSGShaderStageGeneratorInterface &inLightShader, quint32 lightIndex, QSSGRenderLight::Type inType)
434     {
435         setupShadowMapVariableNames(lightIndex);
436 
437         inLightShader.addInclude("shadowMapping.glsllib");
438         if (inType == QSSGRenderLight::Type::Directional) {
439             inLightShader.addUniform(m_shadowMapStem, "sampler2D");
440         } else {
441             inLightShader.addUniform(m_shadowCubeStem, "samplerCube");
442         }
443         inLightShader.addUniform(m_shadowControlStem, "vec4");
444         inLightShader.addUniform(m_shadowMatrixStem, "mat4");
445 
446         /*
447         if ( inType == RenderLightTypes::Area )
448         {
449                 inLightShader << "vec2 " << m_shadowCoordStem << ";" << "\n";
450                 inLightShader << "    shadow_map_occl = sampleParaboloid( " << m_shadowMapStem << ", "
451         << m_shadowControlStem << ", "
452                                                                                 <<
453         m_shadowMatrixStem << ", varWorldPos, vec2(1.0, " << m_shadowControlStem << ".z), "
454                                                                                 << m_shadowCoordStem
455         << " );" << "\n";
456         }
457         else */
458         if (inType != QSSGRenderLight::Type::Directional) {
459             inLightShader << "    shadow_map_occl = sampleCubemap(" << m_shadowCubeStem << ", " << m_shadowControlStem << ", " << m_shadowMatrixStem << ", " << m_lightPos << ".xyz, varWorldPos, vec2(1.0, " << m_shadowControlStem << ".z));\n";
460         } else {
461             inLightShader << "    shadow_map_occl = sampleOrthographic(" << m_shadowMapStem << ", " << m_shadowControlStem << ", " << m_shadowMatrixStem << ", varWorldPos, vec2(1.0, " << m_shadowControlStem << ".z));\n";
462         }
463     }
464 
addDisplacementMappingForDepthPass__anon06cffe670111::QSSGShaderGenerator465     void addDisplacementMappingForDepthPass(QSSGShaderStageGeneratorInterface &inShader) override
466     {
467         inShader.addIncoming("attr_uv0", "vec2");
468         inShader.addIncoming("attr_norm", "vec3");
469         inShader.addUniform("displacementSampler", "sampler2D");
470         inShader.addUniform("displaceAmount", "float");
471         inShader.addUniform("displacementMap_rot", "vec4");
472         inShader.addUniform("displacementMap_offset", "vec3");
473         inShader.addInclude("defaultMaterialFileDisplacementTexture.glsllib");
474 
475         inShader << "    vec3 uTransform = vec3(displacementMap_rot.x, displacementMap_rot.y, displacementMap_offset.x);\n"
476                     "    vec3 vTransform = vec3(displacementMap_rot.z, displacementMap_rot.w, displacementMap_offset.y);\n";
477         addFunction(inShader, "getTransformedUVCoords");
478         inShader << "    vec2 uv_coords = attr_uv0;\n"
479                     "    uv_coords = getTransformedUVCoords(vec3(uv_coords, 1.0), uTransform, vTransform);\n"
480                     "    vec3 displacedPos = defaultMaterialFileDisplacementTexture(displacementSampler , displaceAmount, uv_coords , attr_norm, attr_pos);\n"
481                     "    gl_Position = modelViewProjection * vec4(displacedPos, 1.0);\n";
482     }
483 
addDisplacementImageUniforms__anon06cffe670111::QSSGShaderGenerator484     void addDisplacementImageUniforms(QSSGShaderStageGeneratorInterface &inGenerator,
485                                       quint32 displacementImageIdx,
486                                       QSSGRenderableImage *displacementImage) override
487     {
488         if (displacementImage) {
489             setupImageVariableNames(displacementImageIdx);
490             inGenerator.addInclude("defaultMaterialFileDisplacementTexture.glsllib");
491             inGenerator.addUniform("modelMatrix", "mat4");
492             inGenerator.addUniform("cameraPosition", "vec3");
493             inGenerator.addUniform("displaceAmount", "float");
494             inGenerator.addUniform(m_imageSampler, "sampler2D");
495         }
496     }
497 
maybeAddMaterialFresnel__anon06cffe670111::QSSGShaderGenerator498     void maybeAddMaterialFresnel(QSSGShaderStageGeneratorInterface &fragmentShader, QSSGDataView<quint32> inKey, bool &fragmentHasSpecularAmount, bool hasMetalness)
499     {
500         if (m_defaultMaterialShaderKeyProperties.m_fresnelEnabled.getValue(inKey)) {
501             addSpecularAmount(fragmentShader, fragmentHasSpecularAmount);
502             fragmentShader.addInclude("defaultMaterialFresnel.glsllib");
503             fragmentShader.addUniform("fresnelPower", "float");
504             fragmentShader.addUniform("material_specular", "vec4");
505             if (hasMetalness) {
506                 fragmentShader << "    // Add fresnel ratio\n"
507                                   "    specularAmount *= defaultMaterialSimpleFresnel(specularBase, metalnessAmount, world_normal, view_vector, dielectricSpecular(material_properties.w), fresnelPower);\n";
508             } else {
509                 fragmentShader << "    // Add fresnel ratio\n"
510                                   "    specularAmount *= defaultMaterialSimpleFresnelNoMetalness(world_normal, view_vector, dielectricSpecular(material_properties.w), fresnelPower);\n";
511             }
512         }
513     }
setupLightVariableNames__anon06cffe670111::QSSGShaderGenerator514     void setupLightVariableNames(qint32 lightIdx, QSSGRenderLight &inLight)
515     {
516         Q_ASSERT(lightIdx > -1);
517         if (m_lightsAsSeparateUniforms) {
518             char buf[16];
519             qsnprintf(buf, 16, "light_%d", int(lightIdx));
520             QByteArray lightStem = buf;
521             m_lightColor = lightStem;
522             m_lightColor.append("_diffuse");
523             m_lightDirection = lightStem;
524             m_lightDirection.append("_direction");
525             m_lightSpecularColor = lightStem;
526             m_lightSpecularColor.append("_specular");
527             if (inLight.m_lightType == QSSGRenderLight::Type::Point) {
528                 m_lightPos = lightStem;
529                 m_lightPos.append("_position");
530                 m_lightAttenuation = lightStem;
531                 m_lightAttenuation.append("_attenuation");
532             } else if (inLight.m_lightType == QSSGRenderLight::Type::Area) {
533                 m_lightPos = lightStem;
534                 m_lightPos.append("_position");
535                 m_lightUp = lightStem;
536                 m_lightUp.append("_up");
537                 m_lightRt = lightStem;
538                 m_lightRt.append("_right");
539             } else if (inLight.m_lightType == QSSGRenderLight::Type::Spot) {
540                 m_lightPos = lightStem;
541                 m_lightPos.append("_position");
542                 m_lightAttenuation = lightStem;
543                 m_lightAttenuation.append("_attenuation");
544                 m_lightConeAngle = lightStem;
545                 m_lightConeAngle.append("_coneAngle");
546                 m_lightInnerConeAngle = lightStem;
547                 m_lightInnerConeAngle.append("_innerConeAngle");
548             }
549         } else {
550             QByteArray lightStem = "lights";
551             char buf[16];
552             qsnprintf(buf, 16, "[%d].", int(lightIdx));
553             lightStem.append(buf);
554 
555             m_lightColor = lightStem;
556             m_lightColor.append("diffuse");
557             m_lightDirection = lightStem;
558             m_lightDirection.append("direction");
559             m_lightSpecularColor = lightStem;
560             m_lightSpecularColor.append("specular");
561             if (inLight.m_lightType == QSSGRenderLight::Type::Point) {
562                 m_lightPos = lightStem;
563                 m_lightPos.append("position");
564                 m_lightConstantAttenuation = lightStem;
565                 m_lightConstantAttenuation.append("constantAttenuation");
566                 m_lightLinearAttenuation = lightStem;
567                 m_lightLinearAttenuation.append("linearAttenuation");
568                 m_lightQuadraticAttenuation = lightStem;
569                 m_lightQuadraticAttenuation.append("quadraticAttenuation");
570             } else if (inLight.m_lightType == QSSGRenderLight::Type::Area) {
571                 m_lightPos = lightStem;
572                 m_lightPos.append("position");
573                 m_lightUp = lightStem;
574                 m_lightUp.append("up");
575                 m_lightRt = lightStem;
576                 m_lightRt.append("right");
577             } else if (inLight.m_lightType == QSSGRenderLight::Type::Spot) {
578                 m_lightPos = lightStem;
579                 m_lightPos.append("position");
580                 m_lightConstantAttenuation = lightStem;
581                 m_lightConstantAttenuation.append("constantAttenuation");
582                 m_lightLinearAttenuation = lightStem;
583                 m_lightLinearAttenuation.append("linearAttenuation");
584                 m_lightQuadraticAttenuation = lightStem;
585                 m_lightQuadraticAttenuation.append("quadraticAttenuation");
586                 m_lightConeAngle = lightStem;
587                 m_lightConeAngle.append("coneAngle");
588                 m_lightInnerConeAngle = lightStem;
589                 m_lightInnerConeAngle.append("innerConeAngle");
590             }
591         }
592     }
593 
addDisplacementMapping__anon06cffe670111::QSSGShaderGenerator594     void addDisplacementMapping(QSSGDefaultMaterialVertexPipelineInterface &inShader)
595     {
596         inShader.addIncoming("attr_uv0", "vec2");
597         inShader.addIncoming("attr_norm", "vec3");
598         inShader.addUniform("displacementSampler", "sampler2D");
599         inShader.addUniform("displaceAmount", "float");
600         inShader.addUniform("displacementMap_rot", "vec4");
601         inShader.addUniform("displacementMap_offset", "vec3");
602         inShader.addInclude("defaultMaterialFileDisplacementTexture.glsllib");
603 
604         inShader << "    vec3 uTransform = vec3(displacementMap_rot.x, displacementMap_rot.y, displacementMap_offset.x);\n"
605                     "    vec3 vTransform = vec3(displacementMap_rot.z, displacementMap_rot.w, displacementMap_offset.y);\n";
606         addFunction(inShader, "getTransformedUVCoords");
607         inShader.generateUVCoords(key());
608         inShader << "    varTexCoord0 = getTransformedUVCoords(vec3(varTexCoord0, 1.0), uTransform, vTransform);\n"
609                     "    vec3 displacedPos = defaultMaterialFileDisplacementTexture(displacementSampler , displaceAmount, varTexCoord0 , attr_norm, attr_pos);\n"
610                     "    gl_Position = modelViewProjection * vec4(displacedPos, 1.0);\n";
611     }
612 
generateTextureSwizzle__anon06cffe670111::QSSGShaderGenerator613     void generateTextureSwizzle(QSSGRenderTextureSwizzleMode swizzleMode, QByteArray &texSwizzle, QByteArray &lookupSwizzle)
614     {
615         QSSGRenderContextTypes deprecatedContextFlags(QSSGRenderContextType::GL2 | QSSGRenderContextType::GLES2);
616 
617         if (!(deprecatedContextFlags & m_renderContext->renderContext()->renderContextType())) {
618             switch (swizzleMode) {
619             case QSSGRenderTextureSwizzleMode::L8toR8:
620             case QSSGRenderTextureSwizzleMode::L16toR16:
621                 texSwizzle.append(".rgb");
622                 lookupSwizzle.append(".rrr");
623                 break;
624             case QSSGRenderTextureSwizzleMode::L8A8toRG8:
625                 texSwizzle.append(".rgba");
626                 lookupSwizzle.append(".rrrg");
627                 break;
628             case QSSGRenderTextureSwizzleMode::A8toR8:
629                 texSwizzle.append(".a");
630                 lookupSwizzle.append(".r");
631                 break;
632             default:
633                 break;
634             }
635         }
636     }
637 
638     ///< get the light constant buffer and generate if necessary
getLightConstantBuffer__anon06cffe670111::QSSGShaderGenerator639     QSSGRef<QSSGRenderConstantBuffer> getLightConstantBuffer(qint32 inLightCount)
640     {
641         Q_ASSERT(inLightCount >= 0);
642         const QSSGRef<QSSGRenderContext> &theContext = m_renderContext->renderContext();
643 
644         // we assume constant buffer support
645         Q_ASSERT(theContext->supportsConstantBuffer());
646 
647         // we only create if if we have lights
648         if (!inLightCount || !theContext->supportsConstantBuffer())
649             return nullptr;
650 
651         static const QByteArray theName = QByteArrayLiteral("lightsBuffer");
652         QSSGRef<QSSGRenderConstantBuffer> pCB = theContext->getConstantBuffer(theName);
653         if (pCB)
654             return pCB;
655 
656         // create
657         const size_t size = sizeof(QSSGLightSourceShader) * QSSG_MAX_NUM_LIGHTS + (4 * sizeof(qint32));
658         quint8 stackData[size];
659         memset(stackData, 0, 4 * sizeof(qint32));
660         // QSSGLightSourceShader *s = new (stackData + 4*sizeof(qint32)) QSSGLightSourceShader[QSSG_MAX_NUM_LIGHTS];
661         QSSGByteView cBuffer(stackData, size);
662         pCB = *m_constantBuffers.insert(theName, new QSSGRenderConstantBuffer(theContext, theName, QSSGRenderBufferUsageType::Static, cBuffer));
663         if (Q_UNLIKELY(!pCB)) {
664             Q_ASSERT(false);
665             return nullptr;
666         }
667 
668         return pCB;
669 
670     }
671 
setImageShaderVariables__anon06cffe670111::QSSGShaderGenerator672     void setImageShaderVariables(const QSSGRef<QSSGShaderGeneratorGeneratedShader> &inShader, QSSGRenderableImage &inImage, quint32 idx)
673     {
674         size_t numImageVariables = inShader->m_images.size();
675         for (size_t namesIdx = numImageVariables; namesIdx <= idx; ++namesIdx) {
676             setupImageVariableNames(idx);
677             inShader->m_images.push_back(
678                     QSSGShaderTextureProperties(inShader->m_shader, m_imageSampler, m_imageOffsets, m_imageRotations, m_imageSamplerSize));
679         }
680         QSSGShaderTextureProperties &theShaderProps = inShader->m_images[idx];
681         const QMatrix4x4 &textureTransform = inImage.m_image.m_textureTransform;
682         // We separate rotational information from offset information so that just maybe the shader
683         // will attempt to push less information to the card.
684         const float *dataPtr(textureTransform.constData());
685         // The third member of the offsets contains a flag indicating if the texture was
686         // premultiplied or not.
687         // We use this to mix the texture alpha.
688         QVector3D offsets(dataPtr[12], dataPtr[13], inImage.m_image.m_textureData.m_textureFlags.isPreMultiplied() ? 1.0f : 0.0f);
689         // Grab just the upper 2x2 rotation matrix from the larger matrix.
690         QVector4D rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]);
691 
692         // The image horizontal and vertical tiling modes need to be set here, before we set texture
693         // on the shader.
694         // because setting the image on the texture forces the textue to bind and immediately apply
695         // any tex params.
696         const QSSGRef<QSSGRenderTexture2D> &imageTexture = inImage.m_image.m_textureData.m_texture;
697         imageTexture->setTextureWrapS(inImage.m_image.m_horizontalTilingMode);
698         imageTexture->setTextureWrapT(inImage.m_image.m_verticalTilingMode);
699         theShaderProps.sampler.set(imageTexture.data());
700 
701         // If item size has changed, force sampler parameters update.
702         bool forceParamsUpdate = inImage.m_image.m_flags.testFlag(QSSGRenderImage::Flag::ItemSizeDirty);
703         if (forceParamsUpdate) {
704             imageTexture->setsamplerParamsDirty();
705             inImage.m_image.m_flags.setFlag(QSSGRenderImage::Flag::ItemSizeDirty, false);
706         }
707 
708         theShaderProps.offsets.set(offsets);
709         theShaderProps.rotations.set(rotations);
710         theShaderProps.size.set(QVector2D(imageTexture->textureDetails().width, imageTexture->textureDetails().height));
711     }
712 
generateShadowMapOcclusion__anon06cffe670111::QSSGShaderGenerator713     void generateShadowMapOcclusion(quint32 lightIdx, bool inShadowEnabled, QSSGRenderLight::Type inType)
714     {
715         if (inShadowEnabled) {
716             vertexGenerator().generateWorldPosition();
717             addShadowMapContribution(fragmentGenerator(), lightIdx, inType);
718             /*
719             VertexGenerator().AddUniform( m_ShadowMatrixStem, "mat4" );
720             VertexGenerator().AddOutgoing( m_ShadowCoordStem, "vec4" );
721             VertexGenerator() << "    vec4 local_" << m_ShadowCoordStem << " = " << m_ShadowMatrixStem
722             << " * vec4(local_model_world_position, 1.0);" << "\n";
723             m_TempStr.assign( "local_" );
724             m_TempStr.append( m_ShadowCoordStem );
725             VertexGenerator().AssignOutput( m_ShadowCoordStem, m_TempStr );
726             */
727         } else {
728             fragmentGenerator() << "    shadow_map_occl = 1.0;\n";
729         }
730     }
731 
generateVertexShader__anon06cffe670111::QSSGShaderGenerator732     void generateVertexShader(const QSSGShaderDefaultMaterialKey &inKey)
733     {
734         // vertex displacement
735         quint32 imageIdx = 0;
736         QSSGRenderableImage *displacementImage = nullptr;
737         quint32 displacementImageIdx = 0;
738 
739         for (QSSGRenderableImage *img = m_firstImage; img != nullptr; img = img->m_nextImage, ++imageIdx) {
740             if (img->m_mapType == QSSGImageMapTypes::Displacement) {
741                 displacementImage = img;
742                 displacementImageIdx = imageIdx;
743                 break;
744             }
745         }
746 
747         // the pipeline opens/closes up the shaders stages
748         vertexGenerator().beginVertexGeneration(inKey, displacementImageIdx, displacementImage);
749     }
750 
addSpecularAmount__anon06cffe670111::QSSGShaderGenerator751     void addSpecularAmount(QSSGShaderStageGeneratorInterface &fragmentShader, bool &fragmentHasSpecularAmount, bool reapply = false)
752     {
753         if (!fragmentHasSpecularAmount)
754             fragmentShader << "    vec3 specularAmount = specularBase * vec3(material_properties.z + material_properties.x * (1.0 - material_properties.z));\n";
755         else if (reapply)
756             fragmentShader << "    specularAmount = specularBase * vec3(material_properties.z + material_properties.x * (1.0 - material_properties.z));\n";
757         fragmentHasSpecularAmount = true;
758     }
759 
generateFragmentShader__anon06cffe670111::QSSGShaderGenerator760     void generateFragmentShader(QSSGShaderDefaultMaterialKey &inKey)
761     {
762         const bool metalnessEnabled = material()->isMetalnessEnabled();
763         const bool specularEnabled = material()->isSpecularEnabled();
764         bool vertexColorsEnabled = material()->isVertexColorsEnabled();
765         bool specularLightingEnabled = metalnessEnabled || specularEnabled;
766 
767         const auto &keyProps = m_defaultMaterialShaderKeyProperties;
768 
769         bool hasLighting = material()->hasLighting();
770         bool isDoubleSided = keyProps.m_isDoubleSided.getValue(inKey);
771         bool hasImage = m_firstImage != nullptr;
772 
773         bool hasIblProbe = keyProps.m_hasIbl.getValue(inKey);
774         bool hasEmissiveMap = false;
775         bool hasLightmaps = false;
776         bool hasBaseColorMap = false;
777         // Pull the bump out as
778         QSSGRenderableImage *bumpImage = nullptr;
779         quint32 imageIdx = 0;
780         quint32 bumpImageIdx = 0;
781         QSSGRenderableImage *specularAmountImage = nullptr;
782         quint32 specularAmountImageIdx = 0;
783         QSSGRenderableImage *roughnessImage = nullptr;
784         quint32 roughnessImageIdx = 0;
785         QSSGRenderableImage *metalnessImage = nullptr;
786         quint32 metalnessImageIdx = 0;
787         QSSGRenderableImage *occlusionImage = nullptr;
788         quint32 occlusionImageIdx = 0;
789         // normal mapping
790         QSSGRenderableImage *normalImage = nullptr;
791         quint32 normalImageIdx = 0;
792         // translucency map
793         QSSGRenderableImage *translucencyImage = nullptr;
794         quint32 translucencyImageIdx = 0;
795         // lightmaps
796         QSSGRenderableImage *lightmapIndirectImage = nullptr;
797         quint32 lightmapIndirectImageIdx = 0;
798         QSSGRenderableImage *lightmapRadiosityImage = nullptr;
799         quint32 lightmapRadiosityImageIdx = 0;
800         QSSGRenderableImage *lightmapShadowImage = nullptr;
801         quint32 lightmapShadowImageIdx = 0;
802         const bool supportStandardDerivatives = m_renderContext->renderContext()->supportsStandardDerivatives();
803 
804         QSSGRenderableImage *baseImage = nullptr;
805         quint32 baseImageIdx = 0;
806 
807         // Use shared texcoord when transforms are identity
808         QVector<QSSGRenderableImage *> identityImages;
809 
810         Q_UNUSED(lightmapShadowImage)
811         Q_UNUSED(lightmapShadowImageIdx)
812         Q_UNUSED(supportStandardDerivatives)
813 
814         auto channelStr = [](const QSSGShaderKeyTextureChannel &chProp, const QSSGShaderDefaultMaterialKey &inKey) -> QByteArray {
815             QByteArray ret;
816             switch (chProp.getTextureChannel(inKey)) {
817             case QSSGShaderKeyTextureChannel::R:
818                 ret.append(".r");
819                 break;
820             case QSSGShaderKeyTextureChannel::G:
821                 ret.append(".g");
822                 break;
823             case QSSGShaderKeyTextureChannel::B:
824                 ret.append(".b");
825                 break;
826             case QSSGShaderKeyTextureChannel::A:
827                 ret.append(".a");
828                 break;
829             }
830             return ret;
831         };
832 
833         // Reset uv cooordinate generation
834         clearUVCoordsGen();
835 
836         for (QSSGRenderableImage *img = m_firstImage; img != nullptr; img = img->m_nextImage, ++imageIdx) {
837             if (img->m_image.isImageTransformIdentity())
838                 identityImages.push_back(img);
839             if (img->m_mapType == QSSGImageMapTypes::BaseColor || img->m_mapType == QSSGImageMapTypes::Diffuse) {
840                 hasBaseColorMap = img->m_mapType == QSSGImageMapTypes::BaseColor;
841                 baseImage = img;
842                 baseImageIdx = imageIdx;
843             } else if (img->m_mapType == QSSGImageMapTypes::Bump) {
844                 bumpImage = img;
845                 bumpImageIdx = imageIdx;
846             } else if (img->m_mapType == QSSGImageMapTypes::SpecularAmountMap) {
847                 specularAmountImage = img;
848                 specularAmountImageIdx = imageIdx;
849             } else if (img->m_mapType == QSSGImageMapTypes::Roughness) {
850                 roughnessImage = img;
851                 roughnessImageIdx = imageIdx;
852             } else if (img->m_mapType == QSSGImageMapTypes::Metalness) {
853                 metalnessImage = img;
854                 metalnessImageIdx = imageIdx;
855             } else if (img->m_mapType == QSSGImageMapTypes::Occlusion) {
856                 occlusionImage = img;
857                 occlusionImageIdx = imageIdx;
858             } else if (img->m_mapType == QSSGImageMapTypes::Normal) {
859                 normalImage = img;
860                 normalImageIdx = imageIdx;
861             } else if (img->m_mapType == QSSGImageMapTypes::Translucency) {
862                 translucencyImage = img;
863                 translucencyImageIdx = imageIdx;
864             } else if (img->m_mapType == QSSGImageMapTypes::Emissive) {
865                 hasEmissiveMap = true;
866             } else if (img->m_mapType == QSSGImageMapTypes::LightmapIndirect) {
867                 lightmapIndirectImage = img;
868                 lightmapIndirectImageIdx = imageIdx;
869                 hasLightmaps = true;
870             } else if (img->m_mapType == QSSGImageMapTypes::LightmapRadiosity) {
871                 lightmapRadiosityImage = img;
872                 lightmapRadiosityImageIdx = imageIdx;
873                 hasLightmaps = true;
874             } else if (img->m_mapType == QSSGImageMapTypes::LightmapShadow) {
875                 lightmapShadowImage = img;
876                 lightmapShadowImageIdx = imageIdx;
877                 hasLightmaps = true;
878             }
879         }
880 
881         bool enableSSAO = false;
882         bool enableSSDO = false;
883         bool enableShadowMaps = false;
884         bool enableBumpNormal = normalImage || bumpImage;
885         specularLightingEnabled |= specularAmountImage != nullptr;
886 
887         for (qint32 idx = 0; idx < m_currentFeatureSet.size(); ++idx) {
888             const auto &name = m_currentFeatureSet.at(idx).name;
889             if (name == QSSGShaderDefines::asString(QSSGShaderDefines::Ssao))
890                 enableSSAO = m_currentFeatureSet.at(idx).enabled;
891             else if (name == QSSGShaderDefines::asString(QSSGShaderDefines::Ssdo))
892                 enableSSDO = m_currentFeatureSet.at(idx).enabled;
893             else if (name == QSSGShaderDefines::asString(QSSGShaderDefines::Ssm))
894                 enableShadowMaps = m_currentFeatureSet.at(idx).enabled;
895         }
896 
897         bool includeSSAOSSDOVars = enableSSAO || enableSSDO || enableShadowMaps;
898 
899         vertexGenerator().beginFragmentGeneration();
900         QSSGShaderStageGeneratorInterface &fragmentShader(fragmentGenerator());
901         QSSGDefaultMaterialVertexPipelineInterface &vertexShader(vertexGenerator());
902 
903         // The fragment or vertex shaders may not use the material_properties or diffuse
904         // uniforms in all cases but it is simpler to just add them and let the linker strip them.
905         fragmentShader.addUniform("material_diffuse", "vec3");
906         fragmentShader.addUniform("base_color", "vec4");
907         fragmentShader.addUniform("material_properties", "vec4");
908 
909         // !hasLighting does not mean 'no light source'
910         // it should be KHR_materials_unlit
911         // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_unlit
912         if (hasLighting) {
913             // All these are needed for SSAO
914             if (includeSSAOSSDOVars)
915                 fragmentShader.addInclude("SSAOCustomMaterial.glsllib");
916 
917             if (hasIblProbe)
918                 fragmentShader.addInclude("sampleProbe.glsllib");
919 
920             if (!m_lightsAsSeparateUniforms)
921                 addFunction(fragmentShader, "sampleLightVars");
922             addFunction(fragmentShader, "diffuseReflectionBSDF");
923 
924             if (hasLightmaps)
925                 fragmentShader.addInclude("evalLightmaps.glsllib");
926 
927             // view_vector, varWorldPos, world_normal are all used if there is a specular map
928             // in addition to if there is specular lighting.  So they are lifted up here, always
929             // generated.
930             // we rely on the linker to strip out what isn't necessary instead of explicitly stripping
931             // it for code simplicity.
932             if (hasImage) {
933                 fragmentShader.append("    vec3 uTransform;");
934                 fragmentShader.append("    vec3 vTransform;");
935             }
936 
937             vertexShader.generateViewVector();
938             vertexShader.generateWorldNormal(inKey);
939             vertexShader.generateWorldPosition();
940 
941             if (includeSSAOSSDOVars || specularEnabled || metalnessEnabled || hasIblProbe || enableBumpNormal)
942                 vertexShader.generateVarTangentAndBinormal(inKey);
943 
944             // You do bump or normal mapping but not both
945             if (bumpImage != nullptr) {
946                 generateImageUVCoordinates(bumpImageIdx, *bumpImage);
947                 fragmentShader.addUniform("bumpAmount", "float");
948 
949                 fragmentShader.addUniform(m_imageSamplerSize, "vec2");
950                 fragmentShader.addInclude("defaultMaterialBumpNoLod.glsllib");
951                 fragmentShader << "    world_normal = defaultMaterialBumpNoLod(" << m_imageSampler << ", bumpAmount, " << m_imageFragCoords << ", tangent, binormal, world_normal, " << m_imageSamplerSize << ");\n";
952                 // Do gram schmidt
953                 fragmentShader << "    binormal = normalize(cross(world_normal, tangent));\n";
954                 fragmentShader << "    tangent = normalize(cross(binormal, world_normal));\n";
955 
956             } else if (normalImage != nullptr) {
957                 generateImageUVCoordinates(normalImageIdx, *normalImage);
958 
959                 fragmentShader.addFunction("sampleNormalTexture");
960                 fragmentShader.addUniform("bumpAmount", "float");
961 
962                 fragmentShader << "    world_normal = sampleNormalTexture(" << m_imageSampler << ", bumpAmount, " << m_imageFragCoords << ", tangent, binormal, world_normal);\n";
963                 // Do gram schmidt
964                 fragmentShader << "    binormal = normalize(cross(world_normal, tangent));\n";
965                 fragmentShader << "    tangent = normalize(cross(binormal, world_normal));\n";
966             }
967 
968             if (isDoubleSided) {
969                 fragmentShader.addInclude("doubleSided.glsllib");
970                 fragmentShader.append("    world_normal = adjustNormalForFace(world_normal, varWorldPos);\n");
971             }
972 
973             if (includeSSAOSSDOVars || specularEnabled || metalnessEnabled || hasIblProbe || enableBumpNormal)
974                 fragmentShader << "    mat3 tanFrame = mat3(tangent, binormal, world_normal);\n";
975 
976             if (hasEmissiveMap)
977                 fragmentShader.append("    vec3 global_emission = material_diffuse.rgb;");
978 
979             if (specularLightingEnabled)
980                 fragmentShader.append("    vec3 specularBase;");
981         }
982 
983         if (vertexColorsEnabled)
984             vertexShader.generateVertexColor(inKey);
985         else
986             fragmentShader.append("    vec4 vertColor = vec4(1.0);");
987 
988         bool fragmentHasSpecularAmount = false;
989 
990         fragmentShader << "    vec4 diffuseColor = base_color * vertColor;\n";
991 
992         if (baseImage) {
993             QByteArray texSwizzle;
994             QByteArray lookupSwizzle;
995 
996             // NoLighting also needs to fetch baseImage
997             if (!hasLighting) {
998                 fragmentShader.append("    vec3 uTransform;");
999                 fragmentShader.append("    vec3 vTransform;");
1000             }
1001 
1002             if (identityImages.contains(baseImage))
1003                 generateImageUVSampler(baseImageIdx);
1004             else
1005                 generateImageUVCoordinates(baseImageIdx, *baseImage);
1006             generateTextureSwizzle(baseImage->m_image.m_textureData.m_texture->textureSwizzleMode(), texSwizzle, lookupSwizzle);
1007 
1008             fragmentShader << "    vec4 base_texture_color" << texSwizzle << " = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ")" << lookupSwizzle << ";\n";
1009             fragmentShader << "    diffuseColor *= base_texture_color;\n";
1010         }
1011 
1012         if (hasLighting) {
1013             fragmentShader.addUniform("light_ambient_total", "vec3");
1014 
1015             fragmentShader.append("    vec4 global_diffuse_light = vec4(light_ambient_total.rgb * diffuseColor.rgb, diffuseColor.a);");
1016             fragmentShader.append("    vec3 global_specular_light = vec3(0.0, 0.0, 0.0);");
1017             fragmentShader.append("    float shadow_map_occl = 1.0;");
1018 
1019             if (specularLightingEnabled) {
1020                 fragmentShader << "    specularBase = diffuseColor.rgb;\n";
1021                 vertexShader.generateViewVector();
1022                 fragmentShader.addUniform("material_properties", "vec4");
1023                 addSpecularAmount(fragmentShader, fragmentHasSpecularAmount);
1024             }
1025 
1026             if (lightmapIndirectImage != nullptr) {
1027                 if (identityImages.contains(lightmapIndirectImage))
1028                     generateImageUVSampler(lightmapIndirectImageIdx, 1);
1029                 else
1030                     generateImageUVCoordinates(lightmapIndirectImageIdx, *lightmapIndirectImage, 1);
1031 
1032 
1033                 fragmentShader << "    vec4 indirect_light = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ");\n";
1034                 fragmentShader << "    global_diffuse_light += indirect_light;\n";
1035                 if (specularLightingEnabled)
1036                     fragmentShader << "    global_specular_light += indirect_light.rgb * specularAmount;\n";
1037             }
1038 
1039             if (lightmapRadiosityImage != nullptr) {
1040                 if (identityImages.contains(lightmapRadiosityImage))
1041                     generateImageUVSampler(lightmapRadiosityImageIdx, 1);
1042                 else
1043                     generateImageUVCoordinates(lightmapRadiosityImageIdx, *lightmapRadiosityImage, 1);
1044 
1045                 fragmentShader << "    vec4 direct_light = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ");\n";
1046                 fragmentShader << "    global_diffuse_light += direct_light;\n";
1047                 if (specularLightingEnabled)
1048                     fragmentShader << "    global_specular_light += direct_light.rgb * specularAmount;\n";
1049             }
1050 
1051             if (translucencyImage != nullptr) {
1052                 fragmentShader.addUniform("translucentFalloff", "float");
1053                 fragmentShader.addUniform("diffuseLightWrap", "float");
1054 
1055                 if (identityImages.contains(translucencyImage))
1056                     generateImageUVSampler(translucencyImageIdx);
1057                 else
1058                     generateImageUVCoordinates(translucencyImageIdx, *translucencyImage);
1059 
1060                 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::TranslucencyChannel];
1061                 fragmentShader << "    float translucent_depth_range = texture2D(" << m_imageSampler
1062                                << ", " << m_imageFragCoords << ")" << channelStr(channelProps, inKey) << ";\n";
1063                 fragmentShader << "    float translucent_thickness = translucent_depth_range * translucent_depth_range;\n";
1064                 fragmentShader << "    float translucent_thickness_exp = exp(translucent_thickness * translucentFalloff);\n";
1065             }
1066 
1067             fragmentShader.append("    float lightAttenuation = 1.0;");
1068 
1069             addLocalVariable(fragmentShader, "aoFactor", "float");
1070 
1071             if (enableSSAO)
1072                 fragmentShader.append("    aoFactor = customMaterialAO();");
1073             else
1074                 fragmentShader.append("    aoFactor = 1.0;");
1075 
1076             addLocalVariable(fragmentShader, "shadowFac", "float");
1077 
1078             // Fragment lighting means we can perhaps attenuate the specular amount by a texture
1079             // lookup.
1080             if (specularAmountImage) {
1081                 addSpecularAmount(fragmentShader, fragmentHasSpecularAmount);
1082 
1083                 if (identityImages.contains(specularAmountImage))
1084                     generateImageUVSampler(specularAmountImageIdx);
1085                 else
1086                     generateImageUVCoordinates(specularAmountImageIdx, *specularAmountImage);
1087                 fragmentShader << "    specularBase *= texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ").rgb;\n";
1088             }
1089 
1090             fragmentShader << "    float roughnessAmount = material_properties.y;\n";
1091             if (specularLightingEnabled && roughnessImage) {
1092                 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::RoughnessChannel];
1093                 if (identityImages.contains(roughnessImage))
1094                     generateImageUVSampler(roughnessImageIdx);
1095                 else
1096                     generateImageUVCoordinates(roughnessImageIdx, *roughnessImage);
1097                 fragmentShader << "    roughnessAmount *= texture2D(" << m_imageSampler << ", "
1098                                << m_imageFragCoords << ")" << channelStr(channelProps, inKey) << ";\n";
1099             }
1100 
1101             fragmentShader << "    float metalnessAmount = material_properties.z;\n";
1102             if (specularLightingEnabled && metalnessImage) {
1103                 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::MetalnessChannel];
1104                 if (identityImages.contains(metalnessImage))
1105                     generateImageUVSampler(metalnessImageIdx);
1106                 else
1107                     generateImageUVCoordinates(metalnessImageIdx, *metalnessImage);
1108                 fragmentShader << "    float sampledMetalness = texture2D(" << m_imageSampler << ", "
1109                                << m_imageFragCoords << ")" << channelStr(channelProps, inKey) << ";\n";
1110                 fragmentShader << "    metalnessAmount = clamp(metalnessAmount * sampledMetalness, 0.0, 1.0);\n";
1111                 addSpecularAmount(fragmentShader, fragmentHasSpecularAmount, true);
1112             }
1113             fragmentShader.addInclude("defaultMaterialFresnel.glsllib");
1114             fragmentShader << "    float ds = dielectricSpecular(material_properties.w);\n";
1115             fragmentShader << "    diffuseColor.rgb *= (1.0 - ds) * (1.0 - metalnessAmount);\n";
1116             if (specularLightingEnabled) {
1117                 if (!hasBaseColorMap && material()->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
1118                     fragmentShader << "    float lum = dot(base_color.rgb, vec3(0.21, 0.72, 0.07));\n"
1119                                       "    specularBase += (lum > 0.0) ? (base_color.rgb) / lum : vec3(1.0);\n";
1120                 }
1121 
1122                 maybeAddMaterialFresnel(fragmentShader, inKey, fragmentHasSpecularAmount, metalnessEnabled);
1123             }
1124 
1125 
1126             // Iterate through all lights
1127             Q_ASSERT(m_lights.size() < INT32_MAX);
1128             for (qint32 lightIdx = 0; lightIdx < m_lights.size(); ++lightIdx) {
1129                 QSSGRenderLight *lightNode = m_lights[lightIdx];
1130                 setupLightVariableNames(lightIdx, *lightNode);
1131                 bool isDirectional = lightNode->m_lightType == QSSGRenderLight::Type::Directional;
1132                 bool isArea = lightNode->m_lightType == QSSGRenderLight::Type::Area;
1133                 bool isSpot = lightNode->m_lightType == QSSGRenderLight::Type::Spot;
1134                 bool isShadow = enableShadowMaps && lightNode->m_castShadow;
1135 
1136                 fragmentShader.append("");
1137                 char buf[11];
1138                 snprintf(buf, 11, "%d", lightIdx);
1139 
1140                 QByteArray tempStr = "light";
1141                 tempStr.append(buf);
1142 
1143                 fragmentShader << "    //Light " << buf << "\n"
1144                                   "    lightAttenuation = 1.0;\n";
1145                 if (isDirectional) {
1146                     if (m_lightsAsSeparateUniforms) {
1147                         fragmentShader.addUniform(m_lightDirection, "vec4");
1148                         fragmentShader.addUniform(m_lightColor, "vec4");
1149                     }
1150 
1151                     if (enableSSDO)
1152                         fragmentShader << "    shadowFac = customMaterialShadow(" << m_lightDirection << ".xyz, varWorldPos);\n";
1153                     else
1154                         fragmentShader << "    shadowFac = 1.0;\n";
1155 
1156                     generateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, lightNode->m_lightType);
1157 
1158                     if (specularLightingEnabled && enableShadowMaps && isShadow)
1159                         fragmentShader << "    lightAttenuation *= shadow_map_occl;\n";
1160 
1161                     fragmentShader << "    global_diffuse_light.rgb += diffuseColor.rgb * shadowFac * shadow_map_occl * diffuseReflectionBSDF(world_normal, -" << m_lightDirection << ".xyz, " << m_lightColor << ".rgb).rgb;\n";
1162 
1163                     if (specularLightingEnabled) {
1164                         if (m_lightsAsSeparateUniforms)
1165                             fragmentShader.addUniform(m_lightSpecularColor, "vec4");
1166                         outputSpecularEquation(material()->specularModel, fragmentShader, m_lightDirection, m_lightSpecularColor);
1167                     }
1168                 } else if (isArea) {
1169                     if (m_lightsAsSeparateUniforms) {
1170                         fragmentShader.addUniform(m_lightColor, "vec4");
1171                         fragmentShader.addUniform(m_lightPos, "vec4");
1172                         fragmentShader.addUniform(m_lightDirection, "vec4");
1173                         fragmentShader.addUniform(m_lightUp, "vec4");
1174                         fragmentShader.addUniform(m_lightRt, "vec4");
1175                     } else {
1176                         addFunction(fragmentShader, "areaLightVars");
1177                     }
1178                     addFunction(fragmentShader, "calculateDiffuseAreaOld");
1179                     vertexShader.generateWorldPosition();
1180                     generateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, lightNode->m_lightType);
1181 
1182                     // Debug measure to make sure paraboloid sampling was projecting to the right
1183                     // location
1184                     // fragmentShader << "    global_diffuse_light.rg += " << m_ShadowCoordStem << ";"
1185                     // << "\n";
1186                     m_normalizedDirection = tempStr;
1187                     m_normalizedDirection.append("_Frame");
1188 
1189                     addLocalVariable(fragmentShader, m_normalizedDirection, "mat3");
1190                     fragmentShader << "    " << m_normalizedDirection << " = mat3(" << m_lightRt << ".xyz, " << m_lightUp << ".xyz, -" << m_lightDirection << ".xyz);\n";
1191 
1192                     if (enableSSDO)
1193                         fragmentShader << "    shadowFac = shadow_map_occl * customMaterialShadow(" << m_lightDirection << ".xyz, varWorldPos);\n";
1194                     else
1195                         fragmentShader << "    shadowFac = shadow_map_occl;\n";
1196 
1197                     if (specularLightingEnabled) {
1198                         vertexShader.generateViewVector();
1199                         if (m_lightsAsSeparateUniforms)
1200                             fragmentShader.addUniform(m_lightSpecularColor, "vec4");
1201                         outputSpecularAreaLighting(fragmentShader, "varWorldPos", "view_vector", m_lightSpecularColor);
1202                     }
1203 
1204                     outputDiffuseAreaLighting(fragmentShader, "varWorldPos", tempStr);
1205                     fragmentShader << "    lightAttenuation *= shadowFac;\n";
1206 
1207                     addTranslucencyIrradiance(fragmentShader, translucencyImage, true);
1208 
1209                     fragmentShader << "    global_diffuse_light.rgb += diffuseColor.rgb * lightAttenuation * diffuseReflectionBSDF(world_normal, " << m_normalizedDirection << ", " << m_lightColor << ".rgb).rgb;\n";
1210                 } else {
1211                     vertexShader.generateWorldPosition();
1212 
1213                     if (m_lightsAsSeparateUniforms) {
1214                         fragmentShader.addUniform(m_lightColor, "vec4");
1215                         fragmentShader.addUniform(m_lightPos, "vec4");
1216                         if (isSpot)
1217                             fragmentShader.addUniform(m_lightDirection, "vec4");
1218                     }
1219 
1220                     m_relativeDirection = tempStr;
1221                     m_relativeDirection.append("_relativeDirection");
1222 
1223                     m_normalizedDirection = m_relativeDirection;
1224                     m_normalizedDirection.append("_normalized");
1225 
1226                     m_relativeDistance = tempStr;
1227                     m_relativeDistance.append("_distance");
1228 
1229                     fragmentShader << "    vec3 " << m_relativeDirection << " = varWorldPos - " << m_lightPos << ".xyz;\n"
1230                                       "    float " << m_relativeDistance << " = length(" << m_relativeDirection << ");\n"
1231                                       "    vec3 " << m_normalizedDirection << " = " << m_relativeDirection << " / " << m_relativeDistance << ";\n";
1232 
1233                     if (isSpot) {
1234                         m_spotAngle = tempStr;
1235                         m_spotAngle.append("_spotAngle");
1236 
1237                         if (m_lightsAsSeparateUniforms) {
1238                             fragmentShader.addUniform(m_lightConeAngle, "float");
1239                             fragmentShader.addUniform(m_lightInnerConeAngle, "float");
1240                         }
1241                         fragmentShader << "    float " << m_spotAngle << " = dot(" << m_normalizedDirection
1242                                        << ", normalize(vec3(" << m_lightDirection << ")));\n";
1243                         fragmentShader << "    if (" << m_spotAngle << " > " << m_lightConeAngle << ") {\n";
1244                     }
1245 
1246                     generateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, lightNode->m_lightType);
1247 
1248                     if (enableSSDO) {
1249                         fragmentShader << "    shadowFac = shadow_map_occl * customMaterialShadow(" << m_normalizedDirection << ", varWorldPos);\n";
1250                     } else {
1251                         fragmentShader << "    shadowFac = shadow_map_occl;\n";
1252                     }
1253 
1254                     addFunction(fragmentShader, "calculatePointLightAttenuation");
1255 
1256                     if (m_lightsAsSeparateUniforms) {
1257                         fragmentShader.addUniform(m_lightAttenuation, "vec3");
1258                         fragmentShader << "    lightAttenuation = shadowFac * calculatePointLightAttenuation(vec3(" << m_lightAttenuation << ".x, " << m_lightAttenuation << ".y, " << m_lightAttenuation << ".z), " << m_relativeDistance << ");\n";
1259                     } else {
1260                         fragmentShader << "    lightAttenuation = shadowFac * calculatePointLightAttenuation(vec3(" << m_lightConstantAttenuation << ", " << m_lightLinearAttenuation << ", " << m_lightQuadraticAttenuation << "), " << m_relativeDistance << ");\n";
1261                     }
1262 
1263                     addTranslucencyIrradiance(fragmentShader, translucencyImage, false);
1264 
1265                     if (isSpot) {
1266                         fragmentShader << "    float spotFactor = smoothstep(" << m_lightConeAngle
1267                                        << ", " << m_lightInnerConeAngle << ", " << m_spotAngle
1268                                        << ");\n";
1269                         fragmentShader << "    global_diffuse_light.rgb += diffuseColor.rgb * spotFactor * ";
1270                     } else {
1271                         fragmentShader << "    global_diffuse_light.rgb += diffuseColor.rgb * ";
1272                     }
1273                     fragmentShader << "lightAttenuation * diffuseReflectionBSDF(world_normal, -"
1274                                    << m_normalizedDirection << ", "
1275                                    << m_lightColor << ".rgb).rgb;\n";
1276 
1277                     if (specularLightingEnabled) {
1278                         if (m_lightsAsSeparateUniforms)
1279                             fragmentShader.addUniform(m_lightSpecularColor, "vec4");
1280                         outputSpecularEquation(material()->specularModel, fragmentShader, m_normalizedDirection, m_lightSpecularColor);
1281                     }
1282 
1283                     if (isSpot)
1284                         fragmentShader << "    }\n";
1285                 }
1286             }
1287 
1288             // This may be confusing but the light colors are already modulated by the base
1289             // material color.
1290             // Thus material color is the base material color * material emissive.
1291             // Except material_color.a *is* the actual opacity factor.
1292             // Furthermore objectOpacity is something that may come from the vertex pipeline or
1293             // somewhere else.
1294             // We leave it up to the vertex pipeline to figure it out.
1295             fragmentShader << "    global_diffuse_light = vec4(global_diffuse_light.rgb * aoFactor, objectOpacity * diffuseColor.a);\n"
1296                               "    global_specular_light = vec3(global_specular_light.rgb);\n";
1297             if (!hasEmissiveMap)
1298                 fragmentShader << "    global_diffuse_light.rgb += diffuseColor.rgb * material_diffuse.rgb;\n";
1299 
1300             // since we already modulate our material diffuse color
1301             // into the light color we will miss it entirely if no IBL
1302             // or light is used
1303             if (hasLightmaps && !(m_lights.size() || hasIblProbe))
1304                 fragmentShader << "    global_diffuse_light.rgb *= diffuseColor.rgb;\n";
1305 
1306             if (hasIblProbe) {
1307                 vertexShader.generateWorldNormal(inKey);
1308 
1309                 fragmentShader << "    global_diffuse_light.rgb += diffuseColor.rgb * aoFactor * sampleDiffuse(tanFrame).rgb;\n";
1310 
1311                 if (specularLightingEnabled) {
1312                     fragmentShader.addUniform("material_specular", "vec4");
1313                     fragmentShader << "    global_specular_light.rgb += specularAmount * vec3(material_specular.rgb) * sampleGlossy(tanFrame, view_vector, roughnessAmount).rgb;\n";
1314                 }
1315             }
1316 
1317             if (hasImage) {
1318                 fragmentShader.append("    vec4 texture_color;");
1319                 quint32 idx = 0;
1320                 for (QSSGRenderableImage *image = m_firstImage; image; image = image->m_nextImage, ++idx) {
1321                     // Various maps are handled on a different locations
1322                     if (image->m_mapType == QSSGImageMapTypes::Bump || image->m_mapType == QSSGImageMapTypes::Normal
1323                         || image->m_mapType == QSSGImageMapTypes::Displacement || image->m_mapType == QSSGImageMapTypes::SpecularAmountMap
1324                         || image->m_mapType == QSSGImageMapTypes::Roughness || image->m_mapType == QSSGImageMapTypes::Translucency
1325                         || image->m_mapType == QSSGImageMapTypes::Metalness || image->m_mapType == QSSGImageMapTypes::Occlusion
1326                         || image->m_mapType == QSSGImageMapTypes::LightmapIndirect
1327                         || image->m_mapType == QSSGImageMapTypes::LightmapRadiosity) {
1328                         continue;
1329                     }
1330 
1331                     QByteArray texSwizzle;
1332                     QByteArray lookupSwizzle;
1333 
1334                     if (identityImages.contains(image))
1335                         generateImageUVSampler(idx);
1336                     else
1337                         generateImageUVCoordinates(idx, *image);
1338                     generateTextureSwizzle(image->m_image.m_textureData.m_texture->textureSwizzleMode(), texSwizzle, lookupSwizzle);
1339 
1340                     fragmentShader << "    texture_color" << texSwizzle << " = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ")" << lookupSwizzle << ";\n";
1341 
1342                     if (image->m_image.m_textureData.m_textureFlags.isPreMultiplied())
1343                         fragmentShader << "    texture_color.rgb = texture_color.a > 0.0 ? texture_color.rgb / texture_color.a : vec3(0.0);\n";
1344 
1345                     // These mapping types honestly don't make a whole ton of sense to me.
1346                     switch (image->m_mapType) {
1347                     case QSSGImageMapTypes::BaseColor:
1348                         // color already taken care of
1349                         if (material()->alphaMode == QSSGRenderDefaultMaterial::MaterialAlphaMode::Mask) {
1350                             // The rendered output is either fully opaque or fully transparent depending on the alpha
1351                             // value and the specified alpha cutoff value.
1352                             fragmentShader.addUniform("alphaCutoff", "float");
1353                             fragmentShader << "    if ((texture_color.a * base_color.a) < alphaCutoff) {\n"
1354                                               "        fragOutput = vec4(0);\n"
1355                                               "        return;\n"
1356                                               "    }\n";
1357                         }
1358                         break;
1359                     case QSSGImageMapTypes::Diffuse: // assume images are premultiplied.
1360                         // color already taken care of
1361                         fragmentShader.append("    global_diffuse_light.a *= base_color.a * texture_color.a;");
1362                         break;
1363                     case QSSGImageMapTypes::LightmapShadow:
1364                         // We use image offsets.z to switch between incoming premultiplied textures or
1365                         // not premultiplied textures.
1366                         // If Z is 1, then we assume the incoming texture is already premultiplied, else
1367                         // we just read the rgb value.
1368                         fragmentShader.append("    global_diffuse_light *= texture_color;");
1369                         break;
1370                     case QSSGImageMapTypes::Specular:
1371                         fragmentShader.addUniform("material_specular", "vec4");
1372                         if (fragmentHasSpecularAmount) {
1373                             fragmentShader.append("    global_specular_light.rgb += specularAmount * texture_color.rgb * material_specular.rgb;");
1374                         } else {
1375                             fragmentShader.append("    global_specular_light.rgb += texture_color.rgb * material_specular.rgb;");
1376                         }
1377                         fragmentShader.append("    global_diffuse_light.a *= texture_color.a;");
1378                         break;
1379                     case QSSGImageMapTypes::Opacity:
1380                     {
1381                         const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::OpacityChannel];
1382                         fragmentShader << "    global_diffuse_light.a *= texture_color" << channelStr(channelProps, inKey) << ";\n";
1383                         break;
1384                     }
1385                     case QSSGImageMapTypes::Emissive:
1386                         fragmentShader.append("    global_emission *= texture_color.rgb * texture_color.a;");
1387                         break;
1388                     default:
1389                         Q_ASSERT(false); // fallthrough intentional
1390                     }
1391                 }
1392             }
1393 
1394             // Occlusion Map
1395             if (occlusionImage) {
1396                 fragmentShader.addUniform("occlusionAmount", "float");
1397                 const auto &channelProps = keyProps.m_textureChannels[QSSGShaderDefaultMaterialKeyProperties::OcclusionChannel];
1398                 if (identityImages.contains(occlusionImage))
1399                     generateImageUVSampler(occlusionImageIdx);
1400                 else
1401                     generateImageUVCoordinates(occlusionImageIdx, *occlusionImage);
1402                 fragmentShader << "    float ao = texture2D(" << m_imageSampler << ", "
1403                                << m_imageFragCoords << ")" << channelStr(channelProps, inKey) << ";\n";
1404                 fragmentShader << "    global_diffuse_light.rgb = mix(global_diffuse_light.rgb, global_diffuse_light.rgb * ao, occlusionAmount);\n";
1405             }
1406 
1407             if (hasEmissiveMap)
1408                 fragmentShader.append("    global_diffuse_light.rgb += global_emission.rgb;");
1409 
1410             if (hasBaseColorMap && specularLightingEnabled) {
1411                 fragmentShader.addInclude("luminance.glsllib");
1412                 fragmentShader << "    float lum = luminance(specularBase);\n"
1413                                   "    global_specular_light.rgb *= (lum > 0.0) ? specularBase / lum : vec3(1.0);\n";
1414             }
1415 
1416             // Ensure the rgb colors are in range.
1417             fragmentShader.append("    fragOutput = vec4(clamp(global_diffuse_light.rgb + global_specular_light.rgb, 0.0, 1.0), global_diffuse_light.a);");
1418         } else {
1419             fragmentShader.append("    fragOutput = vec4(diffuseColor.rgb, diffuseColor.a * objectOpacity);");
1420         }
1421 
1422         if (vertexGenerator().hasActiveWireframe()) {
1423             fragmentShader << "    vec3 edgeDistance = varEdgeDistance * gl_FragCoord.w;\n"
1424                               "    float d = min(min(edgeDistance.x, edgeDistance.y), edgeDistance.z);\n"
1425                               "    float mixVal = smoothstep(0.0, 1.0, d);\n" // line width 1.0
1426                               "    fragOutput = mix(vec4(0.0, 1.0, 0.0, 1.0), fragOutput, mixVal);\n";
1427         }
1428     }
1429 
generateMaterialShader__anon06cffe670111::QSSGShaderGenerator1430     QSSGRef<QSSGRenderShaderProgram> generateMaterialShader(const QByteArray &inShaderPrefix)
1431     {
1432         // build a string that allows us to print out the shader we are generating to the log.
1433         // This is time consuming but I feel like it doesn't happen all that often and is very
1434         // useful to users
1435         // looking at the log file.
1436 
1437         QByteArray generatedShaderString;
1438         generatedShaderString = inShaderPrefix;
1439 
1440         QSSGShaderDefaultMaterialKey theKey(key());
1441         theKey.toString(generatedShaderString, m_defaultMaterialShaderKeyProperties);
1442 
1443         m_lightsAsSeparateUniforms = !m_renderContext->renderContext()->supportsConstantBuffer();
1444 
1445         generateVertexShader(theKey);
1446         generateFragmentShader(theKey);
1447 
1448         vertexGenerator().endVertexGeneration(false);
1449         vertexGenerator().endFragmentGeneration(false);
1450 
1451         return programGenerator()->compileGeneratedShader(generatedShaderString, QSSGShaderCacheProgramFlags(), m_currentFeatureSet);
1452     }
1453 
generateShader__anon06cffe670111::QSSGShaderGenerator1454     QSSGRef<QSSGRenderShaderProgram> generateShader(const QSSGRenderGraphObject &inMaterial,
1455                                                         QSSGShaderDefaultMaterialKey inShaderDescription,
1456                                                         QSSGShaderStageGeneratorInterface &inVertexPipeline,
1457                                                         const ShaderFeatureSetList &inFeatureSet,
1458                                                         const QVector<QSSGRenderLight *> &inLights,
1459                                                         QSSGRenderableImage *inFirstImage,
1460                                                         bool inHasTransparency,
1461                                                         const QByteArray &inVertexPipelineName,
1462                                                         const QByteArray &) override
1463     {
1464         Q_ASSERT(inMaterial.type == QSSGRenderGraphObject::Type::DefaultMaterial || inMaterial.type == QSSGRenderGraphObject::Type::PrincipledMaterial);
1465         m_currentMaterial = static_cast<const QSSGRenderDefaultMaterial *>(&inMaterial);
1466         m_currentKey = &inShaderDescription;
1467         m_currentPipeline = static_cast<QSSGDefaultMaterialVertexPipelineInterface *>(&inVertexPipeline);
1468         m_currentFeatureSet = inFeatureSet;
1469         m_lights = inLights;
1470         m_firstImage = inFirstImage;
1471         m_hasTransparency = inHasTransparency;
1472 
1473         return generateMaterialShader(inVertexPipelineName);
1474     }
1475 
getShaderForProgram__anon06cffe670111::QSSGShaderGenerator1476     const QSSGRef<QSSGShaderGeneratorGeneratedShader> &getShaderForProgram(const QSSGRef<QSSGRenderShaderProgram> &inProgram)
1477     {
1478         auto inserter = m_programToShaderMap.constFind(inProgram);
1479         if (inserter == m_programToShaderMap.constEnd())
1480             inserter = m_programToShaderMap.insert(inProgram,
1481                                                    QSSGRef<QSSGShaderGeneratorGeneratedShader>(
1482                                                            new QSSGShaderGeneratorGeneratedShader(inProgram,
1483                                                                                                     m_renderContext->renderContext())));
1484 
1485         return inserter.value();
1486     }
1487 
setGlobalProperties__anon06cffe670111::QSSGShaderGenerator1488     void setGlobalProperties(const QSSGRef<QSSGRenderShaderProgram> &inProgram,
1489                              const QSSGRenderLayer & /*inLayer*/,
1490                              QSSGRenderCamera &inCamera,
1491                              const QVector3D &inCameraDirection,
1492                              const QVector<QSSGRenderLight *> &inLights,
1493                              const QVector<QVector3D> &inLightDirections,
1494                              const QSSGRef<QSSGRenderShadowMap> &inShadowMapManager,
1495                              bool receivesShadows = true)
1496     {
1497         const QSSGRef<QSSGShaderGeneratorGeneratedShader> &shader(getShaderForProgram(inProgram));
1498         m_renderContext->renderContext()->setActiveShader(inProgram);
1499 
1500         m_shadowMapManager = inShadowMapManager;
1501 
1502         QSSGRenderCamera &theCamera(inCamera);
1503         shader->m_cameraPosition.set(theCamera.getGlobalPos());
1504         shader->m_cameraDirection.set(inCameraDirection);
1505 
1506         QMatrix4x4 viewProj;
1507         if (shader->m_viewProj.isValid()) {
1508             theCamera.calculateViewProjectionMatrix(viewProj);
1509             shader->m_viewProj.set(viewProj);
1510         }
1511 
1512         if (shader->m_viewMatrix.isValid()) {
1513             viewProj = theCamera.globalTransform.inverted();
1514             shader->m_viewMatrix.set(viewProj);
1515         }
1516 
1517         // update the constant buffer
1518         shader->m_aoShadowParams.set();
1519         // We can't cache light properties because they can change per object.
1520         QVector3D theLightAmbientTotal = QVector3D(0, 0, 0);
1521         size_t numShaderLights = shader->m_lights.size();
1522         size_t numShadowLights = shader->m_shadowMaps.size();
1523         for (quint32 lightIdx = 0, shadowMapIdx = 0, lightEnd = inLights.size(); lightIdx < lightEnd && lightIdx < QSSG_MAX_NUM_LIGHTS;
1524              ++lightIdx) {
1525             QSSGRenderLight *theLight(inLights[lightIdx]);
1526             if (lightIdx >= numShaderLights) {
1527                 shader->m_lights.push_back(QSSGShaderLightProperties());
1528                 ++numShaderLights;
1529             }
1530             if (shadowMapIdx >= numShadowLights && numShadowLights < QSSG_MAX_NUM_SHADOWS && receivesShadows) {
1531                 if (theLight->m_scope == nullptr && theLight->m_castShadow) {
1532                     // PKC TODO : Fix multiple shadow issues.
1533                     // Need to know when the list of lights changes order, and clear shadow maps
1534                     // when that happens.
1535                     setupShadowMapVariableNames(lightIdx);
1536                     shader->m_shadowMaps.push_back(
1537                             QSSGShadowMapProperties(m_shadowMapStem, m_shadowCubeStem, m_shadowMatrixStem, m_shadowControlStem, inProgram));
1538                 }
1539             }
1540             Q_ASSERT(lightIdx < numShaderLights);
1541             QSSGShaderLightProperties &theLightProperties(shader->m_lights[lightIdx]);
1542             float brightness = aux::translateBrightness(theLight->m_brightness);
1543 
1544             // setup light data
1545             theLightProperties.lightColor = theLight->m_diffuseColor * brightness;
1546             theLightProperties.lightData.specular = QVector4D(theLight->m_specularColor * brightness, 1.0);
1547             theLightProperties.lightData.direction = QVector4D(inLightDirections[lightIdx], 1.0);
1548 
1549             // TODO : This does potentially mean that we can create more shadow map entries than
1550             // we can actually use at once.
1551             if ((theLight->m_scope == nullptr) && (theLight->m_castShadow && receivesShadows && inShadowMapManager)) {
1552                 QSSGShadowMapProperties &theShadowMapProperties(shader->m_shadowMaps[shadowMapIdx++]);
1553                 QSSGShadowMapEntry *pEntry = inShadowMapManager->getShadowMapEntry(lightIdx);
1554                 if (pEntry) {
1555                     // add fixed scale bias matrix
1556                     QMatrix4x4 bias = { 0.5, 0.0, 0.0, 0.5,
1557                                         0.0, 0.5, 0.0, 0.5,
1558                                         0.0, 0.0, 0.5, 0.5,
1559                                         0.0, 0.0, 0.0, 1.0 };
1560 
1561                     if (theLight->m_lightType != QSSGRenderLight::Type::Directional) {
1562                         theShadowMapProperties.m_shadowCubeTexture.set(pEntry->m_depthCube.data());
1563                         theShadowMapProperties.m_shadowmapMatrix.set(pEntry->m_lightView);
1564                     } else {
1565                         theShadowMapProperties.m_shadowmapTexture.set(pEntry->m_depthMap.data());
1566                         theShadowMapProperties.m_shadowmapMatrix.set(bias * pEntry->m_lightVP);
1567                     }
1568 
1569                     theShadowMapProperties.m_shadowmapSettings.set(
1570                             QVector4D(theLight->m_shadowBias, theLight->m_shadowFactor, theLight->m_shadowMapFar, 0.0f));
1571                 } else {
1572                     // if we have a light casting shadow we should find an entry
1573                     Q_ASSERT(false);
1574                 }
1575             }
1576 
1577             if (theLight->m_lightType == QSSGRenderLight::Type::Point
1578                     || theLight->m_lightType == QSSGRenderLight::Type::Spot) {
1579                 theLightProperties.lightData.position = QVector4D(theLight->getGlobalPos(), 1.0);
1580                 theLightProperties.lightData.constantAttenuation = aux::translateConstantAttenuation(theLight->m_constantFade);
1581                 theLightProperties.lightData.linearAttenuation = aux::translateLinearAttenuation(theLight->m_linearFade);
1582                 theLightProperties.lightData.quadraticAttenuation = aux::translateQuadraticAttenuation(theLight->m_quadraticFade);
1583                 theLightProperties.lightData.coneAngle = 180.0f;
1584                 if (theLight->m_lightType == QSSGRenderLight::Type::Spot) {
1585                     theLightProperties.lightData.coneAngle
1586                             = qCos(qDegreesToRadians(theLight->m_coneAngle));
1587                     float innerConeAngle = theLight->m_innerConeAngle;
1588                     if (theLight->m_innerConeAngle > theLight->m_coneAngle)
1589                         innerConeAngle = theLight->m_coneAngle;
1590                     theLightProperties.lightData.innerConeAngle
1591                             = qCos(qDegreesToRadians(innerConeAngle));
1592                 }
1593             } else if (theLight->m_lightType == QSSGRenderLight::Type::Area) {
1594                 theLightProperties.lightData.position = QVector4D(theLight->getGlobalPos(), 1.0);
1595 
1596                 QVector3D upDir = mat33::transform(mat44::getUpper3x3(theLight->globalTransform), QVector3D(0, 1, 0));
1597                 QVector3D rtDir = mat33::transform(mat44::getUpper3x3(theLight->globalTransform), QVector3D(1, 0, 0));
1598 
1599                 theLightProperties.lightData.up = QVector4D(upDir, theLight->m_areaHeight);
1600                 theLightProperties.lightData.right = QVector4D(rtDir, theLight->m_areaWidth);
1601             }
1602             theLightAmbientTotal += theLight->m_ambientColor;
1603         }
1604         shader->m_lightAmbientTotal = theLightAmbientTotal;
1605     }
1606 
1607     // Also sets the blend function on the render context.
setMaterialProperties__anon06cffe670111::QSSGShaderGenerator1608     void setMaterialProperties(const QSSGRef<QSSGRenderShaderProgram> &inProgram,
1609                                const QSSGRenderDefaultMaterial &inMaterial,
1610                                const QVector2D &inCameraVec,
1611                                const QMatrix4x4 &inModelViewProjection,
1612                                const QMatrix3x3 &inNormalMatrix,
1613                                const QMatrix4x4 &inGlobalTransform,
1614                                QSSGRenderableImage *inFirstImage,
1615                                float inOpacity,
1616                                const QSSGRef<QSSGRenderTexture2D> &inDepthTexture,
1617                                const QSSGRef<QSSGRenderTexture2D> &inSSaoTexture,
1618                                QSSGRenderImage *inLightProbe,
1619                                QSSGRenderImage *inLightProbe2,
1620                                float inProbeHorizon,
1621                                float inProbeBright,
1622                                float inProbe2Window,
1623                                float inProbe2Pos,
1624                                float inProbe2Fade,
1625                                float inProbeFOV)
1626     {
1627 
1628         const QSSGRef<QSSGRenderContext> &context = m_renderContext->renderContext();
1629         const QSSGRef<QSSGShaderGeneratorGeneratedShader> &shader = getShaderForProgram(inProgram);
1630         shader->m_mvp.set(inModelViewProjection);
1631         shader->m_normalMatrix.set(inNormalMatrix);
1632         shader->m_globalTransform.set(inGlobalTransform);
1633         shader->m_depthTexture.set(inDepthTexture.data());
1634 
1635         shader->m_aoTexture.set(inSSaoTexture.data());
1636 
1637         QSSGRenderImage *theLightProbe = inLightProbe;
1638         QSSGRenderImage *theLightProbe2 = inLightProbe2;
1639 
1640         // If the material has its own IBL Override, we should use that image instead.
1641         const bool hasIblProbe = inMaterial.iblProbe != nullptr;
1642         const bool useMaterialIbl = hasIblProbe ? (inMaterial.iblProbe->m_textureData.m_texture != nullptr) : false;
1643         if (useMaterialIbl)
1644             theLightProbe = inMaterial.iblProbe;
1645 
1646         if (theLightProbe) {
1647             if (theLightProbe->m_textureData.m_texture) {
1648                 QSSGRenderTextureCoordOp theHorzLightProbeTilingMode = QSSGRenderTextureCoordOp::Repeat;
1649                 QSSGRenderTextureCoordOp theVertLightProbeTilingMode = theLightProbe->m_verticalTilingMode;
1650                 theLightProbe->m_textureData.m_texture->setTextureWrapS(theHorzLightProbeTilingMode);
1651                 theLightProbe->m_textureData.m_texture->setTextureWrapT(theVertLightProbeTilingMode);
1652                 const QMatrix4x4 &textureTransform = theLightProbe->m_textureTransform;
1653                 // We separate rotational information from offset information so that just maybe the
1654                 // shader
1655                 // will attempt to push less information to the card.
1656                 const float *dataPtr(textureTransform.constData());
1657                 // The third member of the offsets contains a flag indicating if the texture was
1658                 // premultiplied or not.
1659                 // We use this to mix the texture alpha.
1660                 QVector4D offsets(dataPtr[12],
1661                                   dataPtr[13],
1662                                   theLightProbe->m_textureData.m_textureFlags.isPreMultiplied() ? 1.0f : 0.0f,
1663                                   (float)theLightProbe->m_textureData.m_texture->numMipmaps());
1664 
1665                 // Grab just the upper 2x2 rotation matrix from the larger matrix.
1666                 QVector4D rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]);
1667 
1668                 shader->m_lightProbeRot.set(rotations);
1669                 shader->m_lightProbeOfs.set(offsets);
1670 
1671                 if ((!inMaterial.iblProbe) && (inProbeFOV < 180.f)) {
1672                     shader->m_lightProbeOpts.set(QVector4D(0.01745329251994329547f * inProbeFOV, 0.0f, 0.0f, 0.0f));
1673                 }
1674 
1675                 // Also make sure to add the secondary texture, but it should only be added if the
1676                 // primary
1677                 // (i.e. background) texture is also there.
1678                 if (theLightProbe2 && theLightProbe2->m_textureData.m_texture) {
1679                     theLightProbe2->m_textureData.m_texture->setTextureWrapS(theHorzLightProbeTilingMode);
1680                     theLightProbe2->m_textureData.m_texture->setTextureWrapT(theVertLightProbeTilingMode);
1681                     shader->m_lightProbe2.set(theLightProbe2->m_textureData.m_texture.data());
1682                     shader->m_lightProbe2Props.set(QVector4D(inProbe2Window, inProbe2Pos, inProbe2Fade, 1.0f));
1683 
1684                     const QMatrix4x4 &xform2 = theLightProbe2->m_textureTransform;
1685                     const float *dataPtr(xform2.constData());
1686                     shader->m_lightProbeProps.set(QVector4D(dataPtr[12], dataPtr[13], inProbeHorizon, inProbeBright * 0.01f));
1687                 } else {
1688                     shader->m_lightProbe2Props.set(QVector4D(0.0f, 0.0f, 0.0f, 0.0f));
1689                     shader->m_lightProbeProps.set(QVector4D(0.0f, 0.0f, inProbeHorizon, inProbeBright * 0.01f));
1690                 }
1691                 const QSSGRef<QSSGRenderTexture2D> &textureImage = theLightProbe->m_textureData.m_texture;
1692                 shader->m_lightProbe.set(textureImage.data());
1693                 shader->m_lightProbeSize.set(
1694                         QVector2D(textureImage->textureDetails().width, textureImage->textureDetails().height));
1695             } else {
1696                 shader->m_lightProbeProps.set(QVector4D(0.0f, 0.0f, -1.0f, 0.0f));
1697                 shader->m_lightProbe2Props.set(QVector4D(0.0f, 0.0f, 0.0f, 0.0f));
1698             }
1699         } else {
1700             shader->m_lightProbeProps.set(QVector4D(0.0f, 0.0f, -1.0f, 0.0f));
1701             shader->m_lightProbe2Props.set(QVector4D(0.0f, 0.0f, 0.0f, 0.0f));
1702         }
1703 
1704         const auto &color = inMaterial.color;
1705         shader->m_materialDiffuse.set(inMaterial.emissiveColor);
1706 
1707         const auto qMix = [](float x, float y, float a) {
1708             return (x * (1.0f - a) + (y * a));
1709         };
1710 
1711         const auto qMix3 = [&qMix](const QVector3D &x, const QVector3D &y, float a) {
1712             return QVector3D{qMix(x.x(), y.x(), a), qMix(x.y(), y.y(), a), qMix(x.z(), y.z(), a)};
1713         };
1714 
1715         const auto &specularTint = (inMaterial.type == QSSGRenderGraphObject::Type::PrincipledMaterial) ? qMix3(QVector3D(1.0f, 1.0f, 1.0f), color.toVector3D(), inMaterial.specularTint.x())
1716                                                                                                         : inMaterial.specularTint;
1717 
1718         shader->m_baseColor.set(color);
1719         shader->m_materialSpecular.set(QVector4D(specularTint, inMaterial.ior));
1720         shader->m_cameraProperties.set(inCameraVec);
1721         shader->m_fresnelPower.set(inMaterial.fresnelPower);
1722 
1723         const bool hasLighting = inMaterial.lighting != QSSGRenderDefaultMaterial::MaterialLighting::NoLighting;
1724         if (hasLighting) {
1725             if (context->supportsConstantBuffer()) {
1726                 const QSSGRef<QSSGRenderConstantBuffer> &pLightCb = getLightConstantBuffer(shader->m_lights.size());
1727                 // if we have lights we need a light buffer
1728                 Q_ASSERT(shader->m_lights.size() == 0 || pLightCb);
1729 
1730                 for (qint32 idx = 0, end = shader->m_lights.size(); idx < end && pLightCb; ++idx) {
1731                     auto &lightProp = shader->m_lights[idx];
1732                     lightProp.lightData.diffuse = QVector4D(lightProp.lightColor, 1.0);
1733 
1734                     // this is our final change update memory
1735                     pLightCb->updateRaw(quint32(idx) * sizeof(QSSGLightSourceShader) + (4 * sizeof(qint32)), toByteView(lightProp.lightData));
1736                 }
1737                 // update light buffer to hardware
1738                 if (pLightCb) {
1739                     qint32 cgLights = shader->m_lights.size();
1740                     pLightCb->updateRaw(0, toByteView(cgLights));
1741                     shader->m_lightsBuffer.set();
1742                 }
1743             } else {
1744                 QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *pLightConstants = getLightConstantProperties(shader);
1745 
1746                 // if we have lights we need a light buffer
1747                 Q_ASSERT(shader->m_lights.size() == 0 || pLightConstants);
1748 
1749                 for (qint32 idx = 0, end = shader->m_lights.size(); idx < end && pLightConstants; ++idx) {
1750                     auto &lightProp = shader->m_lights[idx];
1751                     lightProp.lightData.diffuse = QVector4D(lightProp.lightColor, 1.0);
1752                 }
1753                 // update light buffer to hardware
1754                 if (pLightConstants)
1755                     pLightConstants->updateLights(shader);
1756             }
1757         }
1758 
1759         shader->m_materialDiffuseLightAmbientTotal.set(shader->m_lightAmbientTotal);
1760         shader->m_materialProperties.set(QVector4D(inMaterial.specularAmount, inMaterial.specularRoughness, inMaterial.metalnessAmount, inOpacity));
1761         shader->m_bumpAmount.set(inMaterial.bumpAmount);
1762         shader->m_displaceAmount.set(inMaterial.displaceAmount);
1763         shader->m_translucentFalloff.set(inMaterial.translucentFalloff);
1764         shader->m_diffuseLightWrap.set(inMaterial.diffuseLightWrap);
1765         shader->m_occlusionAmount.set(inMaterial.occlusionAmount);
1766         shader->m_alphaCutoff.set(inMaterial.alphaCutoff);
1767 
1768         quint32 imageIdx = 0;
1769         for (QSSGRenderableImage *theImage = inFirstImage; theImage; theImage = theImage->m_nextImage, ++imageIdx)
1770             setImageShaderVariables(shader, *theImage, imageIdx);
1771 
1772         QSSGRenderBlendFunctionArgument blendFunc;
1773         QSSGRenderBlendEquationArgument blendEqua(QSSGRenderBlendEquation::Add, QSSGRenderBlendEquation::Add);
1774         // The blend function goes:
1775         // src op
1776         // dst op
1777         // src alpha op
1778         // dst alpha op
1779         // All of our shaders produce non-premultiplied values.
1780         switch (inMaterial.blendMode) {
1781         case QSSGRenderDefaultMaterial::MaterialBlendMode::Screen:
1782             blendFunc = QSSGRenderBlendFunctionArgument(QSSGRenderSrcBlendFunc::One,
1783                                                           QSSGRenderDstBlendFunc::OneMinusSrcColor,
1784                                                           QSSGRenderSrcBlendFunc::One,
1785                                                           QSSGRenderDstBlendFunc::OneMinusSrcColor);
1786             break;
1787         case QSSGRenderDefaultMaterial::MaterialBlendMode::Multiply:
1788             blendFunc = QSSGRenderBlendFunctionArgument(QSSGRenderSrcBlendFunc::DstColor,
1789                                                           QSSGRenderDstBlendFunc::Zero,
1790                                                           QSSGRenderSrcBlendFunc::One,
1791                                                           QSSGRenderDstBlendFunc::One);
1792             break;
1793         case QSSGRenderDefaultMaterial::MaterialBlendMode::Overlay:
1794             // SW fallback is not using blend equation
1795             // note blend func is not used here anymore
1796             if (context->supportsAdvancedBlendHW() || context->supportsAdvancedBlendHwKHR())
1797                 blendEqua = QSSGRenderBlendEquationArgument(QSSGRenderBlendEquation::Overlay, QSSGRenderBlendEquation::Overlay);
1798             break;
1799         case QSSGRenderDefaultMaterial::MaterialBlendMode::ColorBurn:
1800             // SW fallback is not using blend equation
1801             // note blend func is not used here anymore
1802             if (context->supportsAdvancedBlendHW() || context->supportsAdvancedBlendHwKHR())
1803                 blendEqua = QSSGRenderBlendEquationArgument(QSSGRenderBlendEquation::ColorBurn,
1804                                                               QSSGRenderBlendEquation::ColorBurn);
1805             break;
1806         case QSSGRenderDefaultMaterial::MaterialBlendMode::ColorDodge:
1807             // SW fallback is not using blend equation
1808             // note blend func is not used here anymore
1809             if (context->supportsAdvancedBlendHW() || context->supportsAdvancedBlendHwKHR())
1810                 blendEqua = QSSGRenderBlendEquationArgument(QSSGRenderBlendEquation::ColorDodge,
1811                                                               QSSGRenderBlendEquation::ColorDodge);
1812             break;
1813         default:
1814             blendFunc = QSSGRenderBlendFunctionArgument(QSSGRenderSrcBlendFunc::SrcAlpha,
1815                                                           QSSGRenderDstBlendFunc::OneMinusSrcAlpha,
1816                                                           QSSGRenderSrcBlendFunc::One,
1817                                                           QSSGRenderDstBlendFunc::OneMinusSrcAlpha);
1818             break;
1819         }
1820         context->setBlendFunction(blendFunc);
1821         context->setBlendEquation(blendEqua);
1822     }
setMaterialProperties__anon06cffe670111::QSSGShaderGenerator1823     void setMaterialProperties(const QSSGRef<QSSGRenderShaderProgram> &inProgram,
1824                                const QSSGRenderGraphObject &inMaterial,
1825                                const QVector2D &inCameraVec,
1826                                const QMatrix4x4 &inModelViewProjection,
1827                                const QMatrix3x3 &inNormalMatrix,
1828                                const QMatrix4x4 &inGlobalTransform,
1829                                QSSGRenderableImage *inFirstImage,
1830                                float inOpacity,
1831                                const QSSGLayerGlobalRenderProperties &inRenderProperties,
1832                                bool receivesShadows) override
1833     {
1834         const QSSGRenderDefaultMaterial &theMaterial(static_cast<const QSSGRenderDefaultMaterial &>(inMaterial));
1835         Q_ASSERT(inMaterial.type == QSSGRenderGraphObject::Type::DefaultMaterial || inMaterial.type == QSSGRenderGraphObject::Type::PrincipledMaterial);
1836 
1837 
1838         setGlobalProperties(inProgram,
1839                             inRenderProperties.layer,
1840                             inRenderProperties.camera,
1841                             inRenderProperties.cameraDirection,
1842                             inRenderProperties.lights,
1843                             inRenderProperties.lightDirections,
1844                             inRenderProperties.shadowMapManager,
1845                             receivesShadows);
1846         setMaterialProperties(inProgram,
1847                               theMaterial,
1848                               inCameraVec,
1849                               inModelViewProjection,
1850                               inNormalMatrix,
1851                               inGlobalTransform,
1852                               inFirstImage,
1853                               inOpacity,
1854                               inRenderProperties.depthTexture,
1855                               inRenderProperties.ssaoTexture,
1856                               inRenderProperties.lightProbe,
1857                               inRenderProperties.lightProbe2,
1858                               inRenderProperties.probeHorizon,
1859                               inRenderProperties.probeBright,
1860                               inRenderProperties.probe2Window,
1861                               inRenderProperties.probe2Pos,
1862                               inRenderProperties.probe2Fade,
1863                               inRenderProperties.probeFOV);
1864     }
1865 
getLightConstantProperties__anon06cffe670111::QSSGShaderGenerator1866     QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *getLightConstantProperties(
1867             const QSSGRef<QSSGShaderGeneratorGeneratedShader> &shader)
1868     {
1869         if (!shader->m_lightConstantProperties
1870             || int(shader->m_lights.size()) > shader->m_lightConstantProperties->m_constants.size()) {
1871             if (shader->m_lightConstantProperties)
1872                 delete shader->m_lightConstantProperties;
1873             shader->m_lightConstantProperties = new QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader>(shader.data(), m_lightsAsSeparateUniforms);
1874         }
1875         return shader->m_lightConstantProperties;
1876     }
1877 };
1878 }
1879 
createDefaultMaterialShaderGenerator(QSSGRenderContextInterface * inRc)1880 QSSGRef<QSSGDefaultMaterialShaderGeneratorInterface> QSSGDefaultMaterialShaderGeneratorInterface::createDefaultMaterialShaderGenerator(
1881         QSSGRenderContextInterface *inRc)
1882 {
1883     return QSSGRef<QSSGDefaultMaterialShaderGeneratorInterface>(new QSSGShaderGenerator(inRc));
1884 }
1885 
1886 QSSGDefaultMaterialVertexPipelineInterface::~QSSGDefaultMaterialVertexPipelineInterface() = default;
1887 
1888 QT_END_NAMESPACE
1889