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_hasMorphing;
53 TStrTableStrMap m_interpolationParameters;
54 QList<QByteArray> m_addedFunctions;
55
56 const QSSGShaderDefaultMaterialKeyProperties &defaultMaterialShaderKeyProperties;
57 QSSGShaderMaterialAdapter *materialAdapter;
58 bool useFloatJointIndices;
59 bool hasCustomShadedMain;
60 bool usesInstancing;
61 bool skipCustomFragmentSnippet;
62
63 QSSGMaterialVertexPipeline(QSSGProgramGenerator &inProgram,
64 const QSSGShaderDefaultMaterialKeyProperties &materialProperties,
65 QSSGShaderMaterialAdapter *materialAdapter);
66
67 ~QSSGMaterialVertexPipeline() = default;
68
69 // Trues true if the code was *not* set.
70 bool setCode(GenerationFlag inCode)
71 {
72 if (m_generationFlags & inCode)
73 return true;
74 m_generationFlags |= inCode;
75 return false;
76 }
77 bool hasCode(GenerationFlag inCode) { return (m_generationFlags & inCode); }
78 QSSGProgramGenerator *programGenerator() const { return m_programGenerator; }
79
80 QSSGStageGeneratorBase &vertex()
81 {
82 return *programGenerator()->getStage(inStage: QSSGShaderGeneratorStage::Vertex);
83 }
84 QSSGStageGeneratorBase &fragment()
85 {
86 return *programGenerator()->getStage(inStage: QSSGShaderGeneratorStage::Fragment);
87 }
88
89 /**
90 * @brief Generates UV coordinates in shader code
91 *
92 * @param[in] inUVSet index of UV data set
93 *
94 * @return no return
95 */
96 void generateUVCoords(quint32 inUVSet, const QSSGShaderDefaultMaterialKey &inKey)
97 {
98 if (inUVSet == 0 && setCode(GenerationFlag::UVCoords))
99 return;
100 if (inUVSet == 1 && setCode(GenerationFlag::UVCoords1))
101 return;
102
103 const bool meshHasUV0 = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::TexCoord0, inKey);
104 const bool meshHasUV1 = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::TexCoord1, inKey);
105
106 Q_ASSERT(inUVSet == 0 || inUVSet == 1);
107
108 if (inUVSet == 0) {
109 if (hasCustomShadedMain || meshHasUV0) {
110 addInterpolationParameter(inParamName: "qt_varTexCoord0", inParamType: "vec2");
111 if (m_hasMorphing)
112 vertex().append(data: " qt_vertUV0 = qt_getTargetTex0(qt_vertUV0);");
113 vertex() << " qt_varTexCoord0 = qt_vertUV0;\n";
114 fragment() <<" vec2 qt_texCoord0 = qt_varTexCoord0;\n";
115 } else {
116 vertex() << " vec2 qt_varTexCoord0 = vec2(0.0);\n";
117 fragment() << " vec2 qt_texCoord0 = vec2(0.0);\n";
118 }
119 } else if (inUVSet == 1) {
120 if (hasCustomShadedMain || meshHasUV1) {
121 addInterpolationParameter(inParamName: "qt_varTexCoord1", inParamType: "vec2");
122 if (m_hasMorphing)
123 vertex().append(data: " qt_vertUV1 = qt_getTargetTex0(qt_vertUV1);");
124 vertex() << " qt_varTexCoord1 = qt_vertUV1;\n";
125 fragment() <<" vec2 qt_texCoord1 = qt_varTexCoord1;\n";
126 } else {
127 vertex() << " vec2 qt_varTexCoord1 = vec2(0.0);\n";
128 fragment() << " vec2 qt_texCoord1 = vec2(0.0);\n";
129 }
130 }
131 }
132
133 void generateLightmapUVCoords(const QSSGShaderDefaultMaterialKey &inKey)
134 {
135 if (hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::TexCoordLightmap, inKey)) {
136 addInterpolationParameter(inParamName: "qt_varTexCoordLightmap", inParamType: "vec2");
137 vertex() << " qt_varTexCoordLightmap = qt_vertLightmapUV;\n";
138 fragment() <<" vec2 qt_texCoordLightmap = qt_varTexCoordLightmap;\n";
139 } else {
140 vertex() << " vec2 qt_varTexCoordLightmap = vec2(0.0);\n";
141 fragment() << " vec2 qt_texCoordLightmap = vec2(0.0);\n";
142 }
143 }
144
145 void generateEnvMapReflection(const QSSGShaderDefaultMaterialKey &inKey)
146 {
147 if (setCode(GenerationFlag::EnvMapReflection))
148 return;
149
150 generateWorldPosition(inKey);
151 generateWorldNormal(inKey);
152 QSSGStageGeneratorBase &activeGenerator(activeStage());
153 activeGenerator.addInclude(name: "viewProperties.glsllib");
154 addInterpolationParameter(inParamName: "qt_var_object_to_camera", inParamType: "vec3");
155
156 activeGenerator.append(data: " qt_var_object_to_camera = normalize( qt_local_model_world_position - qt_cameraPosition );");
157
158 // World normal cannot be relied upon in the vertex shader because of bump maps.
159 fragment().append(data: " vec3 environment_map_reflection = reflect( "
160 "normalize(qt_var_object_to_camera), qt_world_normal.xyz );");
161 fragment().append(data: " environment_map_reflection *= vec3( 0.5, 0.5, 0 );");
162 fragment().append(data: " environment_map_reflection += vec3( 0.5, 0.5, 1.0 );");
163 }
164 void generateViewVector(const QSSGShaderDefaultMaterialKey &inKey)
165 {
166 if (setCode(GenerationFlag::ViewVector))
167 return;
168
169 generateWorldPosition(inKey);
170 activeStage().addUniform(name: "qt_cameraPosition", type: "vec3");
171
172
173 fragment() << " vec3 qt_view_vector = normalize(qt_cameraPosition - qt_varWorldPos);\n";
174 }
175
176 // fragment shader expects varying vertex normal
177 // lighting in vertex pipeline expects qt_world_normal
178
179 // qt_world_normal in both vert and frag shader
180 void generateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey)
181 {
182 if (setCode(GenerationFlag::WorldNormal))
183 return;
184
185 const bool meshHasNormal = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Normal, inKey);
186
187 if (hasCustomShadedMain || meshHasNormal) {
188 addInterpolationParameter(inParamName: "qt_varNormal", inParamType: "vec3");
189 doGenerateWorldNormal(inKey);
190 } else {
191 generateWorldPosition(inKey);
192 fragment().append(data: " vec3 qt_varNormal = cross(dFdx(qt_varWorldPos), dFdy(qt_varWorldPos));");
193 }
194 fragment().append(data: " vec3 qt_world_normal = normalize(qt_varNormal);");
195 }
196
197 void generateObjectNormal()
198 {
199 if (setCode(GenerationFlag::ObjectNormal))
200 return;
201
202 addInterpolationParameter(inParamName: "qt_varObjectNormal", inParamType: "vec3");
203 vertex().append(data: " qt_varObjectNormal = qt_vertNormal;");
204 fragment().append(data: " vec3 object_normal = normalize(qt_varObjectNormal);");
205 }
206
207 void generateWorldPosition(const QSSGShaderDefaultMaterialKey &inKey)
208 {
209 if (setCode(GenerationFlag::WorldPosition))
210 return;
211
212 activeStage().addUniform(name: "qt_modelMatrix", type: "mat4");
213 addInterpolationParameter(inParamName: "qt_varWorldPos", inParamType: "vec3");
214 const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey);
215 if (!usesInstancing) {
216 if (m_hasSkinning)
217 vertex().append(data: " vec3 qt_local_model_world_position = qt_vertPosition.xyz;");
218 else
219 vertex().append(data: " vec3 qt_local_model_world_position = (qt_modelMatrix * qt_vertPosition).xyz;");
220 } else {
221 vertex().append(data: " vec3 qt_local_model_world_position = (qt_instancedModelMatrix * qt_vertPosition).xyz;");
222 }
223
224 assignOutput(inVarName: "qt_varWorldPos", inVarValueExpr: "qt_local_model_world_position");
225 }
226
227 void generateDepth()
228 {
229 if (setCode(GenerationFlag::PerspDivDepth))
230 return;
231
232 addInterpolationParameter(inParamName: "qt_varDepth", inParamType: "float");
233 vertex().append(data: " qt_varDepth = gl_Position.z / gl_Position.w;");
234 }
235
236 void generateShadowWorldPosition(const QSSGShaderDefaultMaterialKey &inKey)
237 {
238 if (setCode(GenerationFlag::PerspDivWorldPos))
239 return;
240
241 activeStage().addUniform(name: "qt_modelMatrix", type: "mat4");
242 addInterpolationParameter(inParamName: "qt_varShadowWorldPos", inParamType: "vec3");
243
244 const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey);
245 if (!usesInstancing) {
246 if (m_hasSkinning)
247 vertex().append(data: " vec4 qt_shadow_world_tmp = qt_vertPosition;");
248 else
249 vertex().append(data: " vec4 qt_shadow_world_tmp = qt_modelMatrix * qt_vertPosition;");
250 } else {
251 vertex().append(data: " vec4 qt_shadow_world_tmp = qt_instancedModelMatrix * qt_vertPosition;");
252 }
253 vertex().append(data: " qt_varShadowWorldPos = qt_shadow_world_tmp.xyz / qt_shadow_world_tmp.w;");
254 }
255
256 void generateVarTangentAndBinormal(const QSSGShaderDefaultMaterialKey &inKey, bool &genTangent, bool &genBinormal)
257 {
258 if (setCode(GenerationFlag::TangentBinormal))
259 return;
260
261 const bool meshHasTangent = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Tangent, inKey);
262 const bool meshHasBinormal = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Binormal, inKey);
263
264 // I assume that there is no mesh having only binormal without tangent
265 // since it is an abnormal case
266 if (hasCustomShadedMain || meshHasTangent) {
267 addInterpolationParameter(inParamName: "qt_varTangent", inParamType: "vec3");
268 doGenerateVarTangent(inKey);
269 fragment() << " vec3 qt_tangent = normalize(qt_varTangent);\n";
270
271 if (hasCustomShadedMain || meshHasBinormal) {
272 addInterpolationParameter(inParamName: "qt_varBinormal", inParamType: "vec3");
273 doGenerateVarBinormal(inKey);
274 fragment() << " vec3 qt_binormal = normalize(qt_varBinormal);\n";
275 genBinormal = true;
276 } else {
277 fragment() << " vec3 qt_binormal = vec3(0.0);\n";
278 }
279 genTangent = true;
280 } else {
281 fragment() << " vec3 qt_tangent = vec3(0.0);\n"
282 << " vec3 qt_binormal = vec3(0.0);\n";
283 }
284 }
285 void generateVertexColor(const QSSGShaderDefaultMaterialKey &inKey)
286 {
287 if (setCode(GenerationFlag::VertexColor))
288 return;
289
290 const bool meshHasColor = hasAttributeInKey(inAttr: QSSGShaderKeyVertexAttribute::Color, inKey);
291
292 const bool vColorEnabled = defaultMaterialShaderKeyProperties.m_vertexColorsEnabled.getValue(inDataStore: inKey);
293 const bool usesVarColor = defaultMaterialShaderKeyProperties.m_usesVarColor.getValue(inDataStore: inKey);
294 const bool usesInstancing = defaultMaterialShaderKeyProperties.m_usesInstancing.getValue(inDataStore: inKey);
295 const bool usesBlendParticles = defaultMaterialShaderKeyProperties.m_blendParticles.getValue(inDataStore: inKey);
296 if ((vColorEnabled && meshHasColor) || usesInstancing || usesBlendParticles || usesVarColor) {
297 addInterpolationParameter(inParamName: "qt_varColor", inParamType: "vec4");
298 if (m_hasMorphing)
299 vertex().append(data: " qt_vertColor = qt_getTargetColor(qt_vertColor);");
300 vertex().append(data: " qt_varColor = qt_vertColor;");
301 fragment().append(data: " vec4 qt_vertColor = qt_varColor;\n");
302 } else {
303 fragment().append(data: " vec4 qt_vertColor = vec4(1.0);\n"); // must be 1,1,1,1 to not alter when multiplying with it
304 }
305 }
306
307 void addIncoming(const QByteArray &name, const QByteArray &type) { activeStage().addIncoming(name, type); }
308
309 void addOutgoing(const QByteArray &name, const QByteArray &type) { addInterpolationParameter(inParamName: name, inParamType: type); }
310
311 void addUniform(const QByteArray &name, const QByteArray &type) { activeStage().addUniform(name, type); }
312
313 void addUniformArray(const QByteArray &name, const QByteArray &type, quint32 size) { activeStage().addUniformArray(name, type, size); }
314
315 void addInclude(const QByteArray &name) { activeStage().addInclude(name); }
316
317 void addFunction(const QByteArray &functionName)
318 {
319 if (!m_addedFunctions.contains(t: functionName)) {
320 m_addedFunctions.push_back(t: functionName);
321 QByteArray includeName = "func" + functionName + ".glsllib";
322 addInclude(name: includeName);
323 }
324 }
325
326 void addConstantBuffer(const QByteArray &name, const QByteArray &layout)
327 {
328 activeStage().addConstantBuffer(name, layout);
329 }
330
331 void addConstantBufferParam(const QByteArray &cbName, const QByteArray &paramName, const QByteArray &type)
332 {
333 activeStage().addConstantBufferParam(cbName, paramName, type);
334 }
335
336 void addDefinition(const QByteArray &name, const QByteArray &value = QByteArray())
337 {
338 activeStage().addDefinition(name, value);
339 }
340
341 QSSGStageGeneratorBase &operator<<(const QByteArray &data)
342 {
343 activeStage() << data;
344 return activeStage();
345 }
346
347 void append(const QByteArray &data) { activeStage().append(data); }
348
349 QSSGShaderGeneratorStage stage() const
350 {
351 return const_cast<QSSGMaterialVertexPipeline *>(this)->activeStage().stage();
352 }
353
354 // Responsible for beginning all vertex and fragment generation (void main() { etc).
355 void beginVertexGeneration(const QSSGShaderDefaultMaterialKey &inKey,
356 const QSSGShaderFeatures &inFeatureSet,
357 QSSGShaderLibraryManager &shaderLibraryManager);
358 // The fragment shader expects a floating point constant, qt_objectOpacity to be defined
359 // post this method.
360 void beginFragmentGeneration(QSSGShaderLibraryManager &shaderLibraryManager);
361 // Output variables may be mangled in some circumstances so the shader generation system
362 // needs an abstraction
363 // mechanism around this.
364 void assignOutput(const QByteArray &inVarName, const QByteArray &inVarValueExpr);
365
366 // responsible for closing all vertex and fragment generation
367 void endVertexGeneration();
368 void endFragmentGeneration();
369
370 QSSGStageGeneratorBase &activeStage();
371 void addInterpolationParameter(const QByteArray &inParamName, const QByteArray &inParamType);
372
373 void doGenerateWorldNormal(const QSSGShaderDefaultMaterialKey &inKey);
374 void doGenerateVarTangent(const QSSGShaderDefaultMaterialKey &inKey);
375 void doGenerateVarBinormal(const QSSGShaderDefaultMaterialKey &inKey);
376 bool hasAttributeInKey(QSSGShaderKeyVertexAttribute::VertexAttributeBits inAttr, const QSSGShaderDefaultMaterialKey &inKey);
377};
378
379QT_END_NAMESPACE
380
381#endif
382

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