1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2019 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5#ifndef QSSG_VERTEX_PIPELINE_IMPL_H
6#define QSSG_VERTEX_PIPELINE_IMPL_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
20#include <QtQuick3DRuntimeRender/private/qssgrendershaderkeys_p.h>
21
22#include <QtCore/QSharedPointer>
23
24QT_BEGIN_NAMESPACE
25
26struct QSSGMaterialVertexPipeline
27{
28 enum class GenerationFlag
29 {
30 UVCoords = 1,
31 EnvMapReflection = 1 << 1,
32 ViewVector = 1 << 2,
33 WorldNormal = 1 << 3,
34 ObjectNormal = 1 << 4,
35 WorldPosition = 1 << 5,
36 TangentBinormal = 1 << 6,
37 UVCoords1 = 1 << 7,
38 VertexColor = 1 << 8,
39 PerspDivDepth = 1 << 9,
40 PerspDivWorldPos = 1 << 10
41 };
42
43 typedef QHash<QByteArray, QByteArray> TStrTableStrMap;
44 typedef TStrTableStrMap::const_iterator TParamIter;
45 typedef QFlags<GenerationFlag> GenerationFlags;
46
47 QSSGProgramGenerator *m_programGenerator = nullptr;
48 QString m_tempString;
49
50 GenerationFlags m_generationFlags;
51 bool m_hasSkinning;
52 bool m_needsSkinning;
53 bool m_hasMorphing;
54 QList<QByteArray> m_addedFunctions;
55 int m_viewCount = 1;
56
57 const QSSGShaderDefaultMaterialKeyProperties &defaultMaterialShaderKeyProperties;
58 QSSGShaderMaterialAdapter *materialAdapter;
59 bool useFloatJointIndices;
60 bool hasCustomShadedMain;
61 bool usesInstancing;
62 bool skipCustomFragmentSnippet;
63
64 QSSGMaterialVertexPipeline(QSSGProgramGenerator &inProgram,
65 const QSSGShaderDefaultMaterialKeyProperties &materialProperties,
66 QSSGShaderMaterialAdapter *materialAdapter);
67
68 ~QSSGMaterialVertexPipeline() = default;
69
70 // Trues true if the code was *not* set.
71 bool setCode(GenerationFlag inCode)
72 {
73 if (m_generationFlags & inCode)
74 return true;
75 m_generationFlags |= inCode;
76 return false;
77 }
78 bool hasCode(GenerationFlag inCode) { return (m_generationFlags & inCode); }
79 QSSGProgramGenerator *programGenerator() const { return m_programGenerator; }
80
81 QSSGStageGeneratorBase &vertex()
82 {
83 return *programGenerator()->getStage(inStage: QSSGShaderGeneratorStage::Vertex);
84 }
85 QSSGStageGeneratorBase &fragment()
86 {
87 return *programGenerator()->getStage(inStage: QSSGShaderGeneratorStage::Fragment);
88 }
89
90 /**
91 * @brief Generates UV coordinates in shader code
92 *
93 * @param[in] inUVSet index of UV data set
94 *
95 * @return no return
96 */
97 void generateUVCoords(quint32 inUVSet, const QSSGShaderDefaultMaterialKey &inKey)
98 {
99 if (inUVSet == 0 && setCode(GenerationFlag::UVCoords))
100 return;
101 if (inUVSet == 1 && setCode(GenerationFlag::UVCoords1))
102 return;
103
104 const bool meshHasUV0 = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::TexCoord0, inKey);
105 const bool meshHasUV1 = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::TexCoord1, inKey);
106
107 Q_ASSERT(inUVSet == 0 || inUVSet == 1);
108
109 if (inUVSet == 0) {
110 if (hasCustomShadedMain || meshHasUV0) {
111 addInterpolationParameter(inParamName: "qt_varTexCoord0", inParamType: "vec2");
112 if (m_hasMorphing)
113 vertex().append(data: " qt_vertUV0 = qt_getTargetTex0(qt_vertUV0);");
114 vertex() << " qt_varTexCoord0 = qt_vertUV0;\n";
115 fragment() <<" vec2 qt_texCoord0 = qt_varTexCoord0;\n";
116 } else {
117 vertex() << " vec2 qt_varTexCoord0 = vec2(0.0);\n";
118 fragment() << " vec2 qt_texCoord0 = vec2(0.0);\n";
119 }
120 } else if (inUVSet == 1) {
121 if (hasCustomShadedMain || meshHasUV1) {
122 addInterpolationParameter(inParamName: "qt_varTexCoord1", inParamType: "vec2");
123 if (m_hasMorphing)
124 vertex().append(data: " qt_vertUV1 = qt_getTargetTex0(qt_vertUV1);");
125 vertex() << " qt_varTexCoord1 = qt_vertUV1;\n";
126 fragment() <<" vec2 qt_texCoord1 = qt_varTexCoord1;\n";
127 } else {
128 vertex() << " vec2 qt_varTexCoord1 = vec2(0.0);\n";
129 fragment() << " vec2 qt_texCoord1 = vec2(0.0);\n";
130 }
131 }
132 }
133
134 void generateLightmapUVCoords(const QSSGShaderDefaultMaterialKey &inKey)
135 {
136 if (hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::TexCoordLightmap, inKey)) {
137 addInterpolationParameter(inParamName: "qt_varTexCoordLightmap", inParamType: "vec2");
138 vertex() << " qt_varTexCoordLightmap = qt_vertLightmapUV;\n";
139 fragment() <<" vec2 qt_texCoordLightmap = qt_varTexCoordLightmap;\n";
140 } else {
141 vertex() << " vec2 qt_varTexCoordLightmap = vec2(0.0);\n";
142 fragment() << " vec2 qt_texCoordLightmap = vec2(0.0);\n";
143 }
144 }
145
146 void generateEnvMapReflection(const QSSGShaderDefaultMaterialKey &inKey)
147 {
148 if (setCode(GenerationFlag::EnvMapReflection))
149 return;
150
151 generateWorldPosition(inKey);
152 generateWorldNormal(inKey);
153 QSSGStageGeneratorBase &activeGenerator(activeStage());
154 addInterpolationParameter(inParamName: "qt_var_object_to_camera", inParamType: "vec3");
155
156 if (m_viewCount < 2) {
157 activeGenerator.addUniform(name: "qt_cameraPosition", type: "vec3");
158 activeGenerator.append(data: " qt_var_object_to_camera = normalize( qt_local_model_world_position - qt_cameraPosition );");
159 } else {
160 activeGenerator.addUniformArray(name: "qt_cameraPosition", type: "vec3", size: m_viewCount);
161 activeGenerator.append(data: " qt_var_object_to_camera = normalize( qt_local_model_world_position - qt_cameraPosition[qt_viewIndex] );");
162 }
163
164 // World normal cannot be relied upon in the vertex shader because of bump maps.
165 fragment().append(data: " vec3 environment_map_reflection = reflect( "
166 "normalize(qt_var_object_to_camera), qt_world_normal.xyz );");
167 fragment().append(data: " environment_map_reflection *= vec3( 0.5, 0.5, 0 );");
168 fragment().append(data: " environment_map_reflection += vec3( 0.5, 0.5, 1.0 );");
169 }
170
171 void generateViewVector(const QSSGShaderDefaultMaterialKey &inKey)
172 {
173 if (setCode(GenerationFlag::ViewVector))
174 return;
175
176 generateWorldPosition(inKey);
177
178 if (m_viewCount < 2) {
179 activeStage().addUniform(name: "qt_cameraPosition", type: "vec3");
180 fragment() << " vec3 qt_view_vector = normalize(qt_cameraPosition - qt_varWorldPos);\n";
181 } else {
182 activeStage().addUniformArray(name: "qt_cameraPosition", type: "vec3", size: m_viewCount);
183 fragment() << " vec3 qt_view_vector = normalize(qt_cameraPosition[qt_viewIndex] - qt_varWorldPos);\n";
184 }
185 }
186
187 // fragment shader expects varying vertex normal
188 // lighting in vertex pipeline expects qt_world_normal
189
190 // qt_world_normal in both vert and frag shader
191 void generateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey)
192 {
193 if (setCode(GenerationFlag::WorldNormal))
194 return;
195
196 const bool meshHasNormal = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Normal, inKey);
197
198 if (hasCustomShadedMain || meshHasNormal) {
199 addInterpolationParameter(inParamName: "qt_varNormal", inParamType: "vec3");
200 doGenerateWorldNormal(inKey);
201 } else {
202 generateWorldPosition(inKey);
203 // qt_rhi_properties.x is used to correct for Y inversion for non OpenGL backends
204 fragment().append(data: " vec3 qt_varNormal = cross(dFdx(qt_varWorldPos), qt_rhi_properties.x * dFdy(qt_varWorldPos));");
205 }
206 fragment().append(data: " vec3 qt_world_normal = normalize(qt_varNormal);");
207 }
208
209 void generateObjectNormal()
210 {
211 if (setCode(GenerationFlag::ObjectNormal))
212 return;
213
214 addInterpolationParameter(inParamName: "qt_varObjectNormal", inParamType: "vec3");
215 vertex().append(data: " qt_varObjectNormal = qt_vertNormal;");
216 fragment().append(data: " vec3 object_normal = normalize(qt_varObjectNormal);");
217 }
218
219 void generateWorldPosition(const QSSGShaderDefaultMaterialKey &inKey)
220 {
221 if (setCode(GenerationFlag::WorldPosition))
222 return;
223
224 activeStage().addUniform(name: "qt_modelMatrix", type: "mat4");
225 addInterpolationParameter(inParamName: "qt_varWorldPos", inParamType: "vec3");
226 const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey);
227 if (!usesInstancing) {
228 if (m_hasSkinning)
229 vertex().append(data: " vec3 qt_local_model_world_position = qt_vertPosition.xyz;");
230 else
231 vertex().append(data: " vec3 qt_local_model_world_position = (qt_modelMatrix * qt_vertPosition).xyz;");
232 } else {
233 vertex().append(data: " vec3 qt_local_model_world_position = (qt_instancedModelMatrix * qt_vertPosition).xyz;");
234 }
235
236 assignOutput(inVarName: "qt_varWorldPos", inVarValueExpr: "qt_local_model_world_position");
237 }
238
239 void generateDepth()
240 {
241 if (setCode(GenerationFlag::PerspDivDepth))
242 return;
243
244 addInterpolationParameter(inParamName: "qt_varDepth", inParamType: "float");
245 vertex().append(data: " qt_varDepth = gl_Position.z / gl_Position.w;");
246 }
247
248 void generateShadowWorldPosition(const QSSGShaderDefaultMaterialKey &inKey)
249 {
250 if (setCode(GenerationFlag::PerspDivWorldPos))
251 return;
252
253 activeStage().addUniform(name: "qt_modelMatrix", type: "mat4");
254 addInterpolationParameter(inParamName: "qt_varShadowWorldPos", inParamType: "vec3");
255
256 const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey);
257 if (!usesInstancing) {
258 if (m_hasSkinning)
259 vertex().append(data: " vec4 qt_shadow_world_tmp = qt_vertPosition;");
260 else
261 vertex().append(data: " vec4 qt_shadow_world_tmp = qt_modelMatrix * qt_vertPosition;");
262 } else {
263 vertex().append(data: " vec4 qt_shadow_world_tmp = qt_instancedModelMatrix * qt_vertPosition;");
264 }
265 vertex().append(data: " qt_varShadowWorldPos = qt_shadow_world_tmp.xyz / qt_shadow_world_tmp.w;");
266 }
267
268 void generateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &inKey, bool &genTangent, bool &genBinormal)
269 {
270 if (setCode(GenerationFlag::TangentBinormal))
271 return;
272
273 const bool meshHasTangent = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Tangent, inKey);
274 const bool meshHasBinormal = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Binormal, inKey);
275
276 // I assume that there is no mesh having only binormal without tangent
277 // since it is an abnormal case
278 if (hasCustomShadedMain || meshHasTangent) {
279 addInterpolationParameter(inParamName: "qt_varTangent", inParamType: "vec3");
280 doGenerateVarTangent(inKey);
281 fragment() << " vec3 qt_tangent = normalize(qt_varTangent);\n";
282
283 if (hasCustomShadedMain || meshHasBinormal) {
284 addInterpolationParameter(inParamName: "qt_varBinormal", inParamType: "vec3");
285 doGenerateVarBinormal(inKey);
286 fragment() << " vec3 qt_binormal = normalize(qt_varBinormal);\n";
287 genBinormal = true;
288 } else {
289 fragment() << " vec3 qt_binormal = vec3(0.0);\n";
290 }
291 genTangent = true;
292 } else {
293 fragment() << " vec3 qt_tangent = vec3(0.0);\n"
294 << " vec3 qt_binormal = vec3(0.0);\n";
295 }
296 }
297 void generateVertexColor(const QSSGShaderDefaultMaterialKey &inKey)
298 {
299 if (setCode(GenerationFlag::VertexColor))
300 return;
301
302 const bool meshHasColor = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Color, inKey);
303
304 const bool vColorEnabled = defaultMaterialShaderKeyProperties.m_vertexColorsEnabled.getValue(inDataStore: inKey);
305 const bool vColorMaskEnabled = defaultMaterialShaderKeyProperties.m_vertexColorsMaskEnabled.getValue(inDataStore: inKey);
306 const bool usesVarColor = defaultMaterialShaderKeyProperties.m_usesVarColor.getValue(inDataStore: inKey);
307 const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey);
308 const bool usesBlendParticles = defaultMaterialShaderKeyProperties.m_blendParticles.getValue(inDataStore: inKey);
309
310 const bool vertexColorsEnabled = (vColorEnabled && meshHasColor) || usesInstancing || usesBlendParticles || usesVarColor;
311 const bool vertexColorsMaskEnabled = (vColorMaskEnabled && meshHasColor);
312
313 if (vertexColorsEnabled || vertexColorsMaskEnabled) {
314 addInterpolationParameter(inParamName: "qt_varColor", inParamType: "vec4");
315 if (m_hasMorphing)
316 vertex().append(data: " qt_vertColor = qt_getTargetColor(qt_vertColor);");
317 vertex().append(data: " qt_varColor = qt_vertColor;");
318
319 fragment().append(data: " vec4 qt_vertColorMask = qt_varColor;\n");
320 if (vertexColorsEnabled)
321 fragment().append(data: " vec4 qt_vertColor = qt_varColor;\n");
322 else
323 fragment().append(data: " vec4 qt_vertColor = vec4(1.0);\n");
324 }else {
325 fragment().append(data: " vec4 qt_vertColorMask = vec4(1.0);\n");
326 fragment().append(data: " vec4 qt_vertColor = vec4(1.0);\n"); // must be 1,1,1,1 to not alter when multiplying with it
327 }
328 }
329
330 void addIncoming(const QByteArray &name, const QByteArray &type) { activeStage().addIncoming(name, type); }
331
332 void addOutgoing(const QByteArray &name, const QByteArray &type) { addInterpolationParameter(inParamName: name, inParamType: type); }
333
334 void addUniform(const QByteArray &name, const QByteArray &type) { activeStage().addUniform(name, type); }
335
336 void addUniformArray(const QByteArray &name, const QByteArray &type, quint32 size) { activeStage().addUniformArray(name, type, size); }
337
338 void addInclude(const QByteArray &name) { activeStage().addInclude(name); }
339
340 void addFunction(const QByteArray &functionName)
341 {
342 if (!m_addedFunctions.contains(t: functionName)) {
343 m_addedFunctions.push_back(t: functionName);
344 QByteArray includeName = "func" + functionName + ".glsllib";
345 addInclude(name: includeName);
346 }
347 }
348
349 void addConstantBuffer(const QByteArray &name, const QByteArray &layout)
350 {
351 activeStage().addConstantBuffer(name, layout);
352 }
353
354 void addConstantBufferParam(const QByteArray &cbName, const QByteArray &paramName, const QByteArray &type)
355 {
356 activeStage().addConstantBufferParam(cbName, paramName, type);
357 }
358
359 void addDefinition(const QByteArray &name, const QByteArray &value = QByteArray())
360 {
361 activeStage().addDefinition(name, value);
362 }
363
364 QSSGStageGeneratorBase &operator<<(const QByteArray &data)
365 {
366 activeStage() << data;
367 return activeStage();
368 }
369
370 void append(const QByteArray &data) { activeStage().append(data); }
371
372 QSSGShaderGeneratorStage stage() const
373 {
374 return const_cast<QSSGMaterialVertexPipeline *>(this)->activeStage().stage();
375 }
376
377 // Responsible for beginning all vertex and fragment generation (void main() { etc).
378 void beginVertexGeneration(const QSSGShaderDefaultMaterialKey &inKey,
379 const QSSGShaderFeatures &inFeatureSet,
380 QSSGShaderLibraryManager &shaderLibraryManager);
381 // The fragment shader expects a floating point constant, qt_objectOpacity to be defined
382 // post this method.
383 void beginFragmentGeneration(QSSGShaderLibraryManager &shaderLibraryManager);
384 // Output variables may be mangled in some circumstances so the shader generation system
385 // needs an abstraction
386 // mechanism around this.
387 void assignOutput(const QByteArray &inVarName, const QByteArray &inVarValueExpr);
388
389 // responsible for closing all vertex and fragment generation
390 void endVertexGeneration();
391 void endFragmentGeneration();
392
393 QSSGStageGeneratorBase &activeStage();
394 void addInterpolationParameter(const QByteArray &inParamName, const QByteArray &inParamType);
395 void addFlatParameter(const QByteArray &inParamName, const QByteArray &inParamType);
396
397 void doGenerateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey);
398 void doGenerateVarTangent(const QSSGShaderDefaultMaterialKey &inKey);
399 void doGenerateVarBinormal(const QSSGShaderDefaultMaterialKey &inKey);
400 bool hasAttributeInKey(QSSGShaderKeyVertexAttribute::VertexAttributeBits inAttr, const QSSGShaderDefaultMaterialKey &inKey);
401};
402
403QT_END_NAMESPACE
404
405#endif
406

source code of qtquick3d/src/runtimerender/rendererimpl/qssgvertexpipelineimpl_p.h