1 /*
2 * Stellarium Scenery3d Plug-in
3 *
4 * Copyright (C) 2014 Simon Parzer, Peter Neubauer, Georg Zotti, Andrei Borza, Florian Schaukowitsch
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19 */
20
21 #ifndef SHADERMANAGER_HPP
22 #define SHADERMANAGER_HPP
23
24 #include "S3DScene.hpp"
25 #include "StelTexture.hpp"
26 #include "S3DEnum.hpp"
27
28 #include <QMap>
29
30 class QOpenGLShaderProgram;
31
32 Q_DECLARE_LOGGING_CATEGORY(shaderMgr)
33
34 //! A structure for global shader parameters
35 struct GlobalShaderParameters
36 {
37 bool openglES; //true if we work with an OGL ES2 context, may have to adapt shaders
38 bool shadowTransform;
39 bool pixelLighting;
40 bool bump;
41 bool shadows;
42 S3DEnum::ShadowFilterQuality shadowFilterQuality;
43 bool pcss;
44 bool geometryShader;
45 bool torchLight;
46 //for now, only 1 or 4 really supported
47 int frustumSplits;
48 bool hwShadowSamplers;
49 };
50
51 //! A simple shader cache class that gives us the correct shader depending on desired configuration.
52 //! It also contains a uniform cache to avoid unnecessary glUniformLocation calls
53 class ShaderMgr
54 {
55 public:
56 ShaderMgr();
57 ~ShaderMgr();
58
59 //! Enum for OpenGL shader uniform locations (faster than accessing by string each time)
60 enum UNIFORM
61 {
62 //! Defines the ModelView matrix
63 UNIFORM_MAT_MODELVIEW,
64 //! Defines the Projection matrix
65 UNIFORM_MAT_PROJECTION,
66 //! Defines the combined ModelViewProjection matrix, used if shader needs no separation of the 2 for a bit of a speedup
67 UNIFORM_MAT_MVP,
68 //! Defines the 3x3 Normal matrix (transpose of the inverse MV matrix)
69 UNIFORM_MAT_NORMAL,
70 //! The shadow matrices (4x 4x4 Matrices)
71 UNIFORM_MAT_SHADOW0,
72 UNIFORM_MAT_SHADOW1,
73 UNIFORM_MAT_SHADOW2,
74 UNIFORM_MAT_SHADOW3,
75 //! The first cube MVP (array mat4, total 6)
76 UNIFORM_MAT_CUBEMVP,
77
78 //! Defines the Diffuse texture slot
79 UNIFORM_TEX_DIFFUSE,
80 //! Defines the emissive texture slot
81 UNIFORM_TEX_EMISSIVE,
82 //! Defines the Bump texture slot
83 UNIFORM_TEX_BUMP,
84 //! Defines the Height texture slot
85 UNIFORM_TEX_HEIGHT,
86 //! Shadow maps, 4x
87 UNIFORM_TEX_SHADOW0,
88 UNIFORM_TEX_SHADOW1,
89 UNIFORM_TEX_SHADOW2,
90 UNIFORM_TEX_SHADOW3,
91
92 //! Material specular shininess (exponent)
93 UNIFORM_MTL_SHININESS,
94 //! Material global transparency
95 UNIFORM_MTL_ALPHA,
96
97 //! (Light ambient + const factor) * Material ambient or diffuse, depending on Illum model
98 UNIFORM_MIX_AMBIENT,
99 //! Light directional * Material diffuse
100 UNIFORM_MIX_DIFFUSE,
101 //! Light specular * Material specular color
102 UNIFORM_MIX_SPECULAR,
103 //! Torch color * mat diffuse
104 UNIFORM_MIX_TORCHDIFFUSE,
105 //! Material emissive color * light emissive
106 UNIFORM_MIX_EMISSIVE,
107
108 //! Light direction vector (view space)
109 UNIFORM_LIGHT_DIRECTION_VIEW,
110 //! Torchlight attenuation factor (1 float, like in the second model at http://framebunker.com/blog/lighting-2-attenuation/)
111 UNIFORM_TORCH_ATTENUATION,
112
113 //! simple color vec4
114 UNIFORM_VEC_COLOR,
115 //! Squared frustum splits (vec4)
116 UNIFORM_VEC_SPLITDATA,
117 //! Scaling of each frustum's light ortho projection (4xvec2)
118 UNIFORM_VEC_LIGHTORTHOSCALE,
119 //! Alpha test threshold
120 UNIFORM_FLOAT_ALPHA_THRESH,
121 };
122
123 //! Returns a shader that supports the specified operations. Must be called within a GL context.
124 inline QOpenGLShaderProgram* getShader(const GlobalShaderParameters &globals, const S3DScene::Material *mat = Q_NULLPTR);
125
126 //! Returns the Frustum/Boundingbox Debug shader
127 inline QOpenGLShaderProgram* getDebugShader();
128
129 //! Returns the cubemapping shader
130 inline QOpenGLShaderProgram* getCubeShader();
131
132 //! Returns the basic texturing shader
133 inline QOpenGLShaderProgram* getTextureShader();
134
135 //! Returns the location of this uniform for this shader, or -1 if this uniform does not exist.
136 //! This is cached to elimate the overhead of the glGet calls
137 inline GLint uniformLocation(const QOpenGLShaderProgram* shad,UNIFORM uni);
138
139 //! Clears the shaders that have been created by this manager. Must be called within a GL context.
140 void clearCache();
141
142 private:
143 typedef QMap<QString,UNIFORM> t_UniformStrings;
144 static t_UniformStrings uniformStrings;
145
146 //A Bitflag enum which contains features that shaders can implement, and which this manager can dynamically select
147 enum FeatureFlags
148 {
149 INVALID = 0,
150 //Transform-only shader (all flags off) (use for depth-only render), should be mutually exclusive with SHADING
151 TRANSFORM = (1<<0),
152 //The shader has some sort of light-dependent color output, should be mutually exclusive with TRANSFORM
153 SHADING = (1<<1),
154 //Per-pixel lighting
155 PIXEL_LIGHTING = (1<<2),
156 //Shader applies shadows from shadow maps
157 SHADOWS = (1<<3),
158 //Shader applies bump/normal maps
159 BUMP = (1<<4),
160 //Shader applies height maps (in addition to bump map)
161 HEIGHT = (1<<5),
162 //Shader applies alpha testing (w. fragment discard)
163 ALPHATEST = (1<<6),
164 //Shader filters shadows
165 SHADOW_FILTER = (1<<7),
166 //shader filters shadows (higher quality)
167 SHADOW_FILTER_HQ = (1<<8),
168 //uses specular material
169 MAT_SPECULAR = (1<<9),
170 //has diffuse texture. On its own (i.e. esp. without SHADING) it defines the basic texture shader.
171 MAT_DIFFUSETEX = (1<<10),
172 //has emissive texture
173 MAT_EMISSIVETEX = (1<<11),
174 //needs geometry shader cubemapping
175 GEOMETRY_SHADER = (1<<12),
176 //shader performs cubemap lookup
177 CUBEMAP = (1<<13),
178 //shader performs blending, otherwise it is expected to output alpha 1.0
179 //it is required for correct blending for our cubemapping
180 BLENDING = (1<<14),
181 //shader uses an additional point light positioned at the camera that performs additional diffuse illumination
182 TORCH = (1<<15),
183 //debug shader for AABBs/Frustums
184 DEBUG = (1<<16),
185 //PCSS shadow filtering
186 PCSS = (1<<17),
187 //only a single shadow frustum is used
188 SINGLE_SHADOW_FRUSTUM = (1<<18),
189 //set if opengl es2
190 OGL_ES2 = (1<<19),
191 //true if shadow samplers (shadow2d) should be used for shadow maps instead of normal samplers (texture2d)
192 HW_SHADOW_SAMPLERS = (1<<20)
193 };
194
195 typedef QMap<QString,FeatureFlags> t_FeatureFlagStrings;
196 static t_FeatureFlagStrings featureFlagsStrings;
197
198 static QString getVShaderName(uint flags);
199 static QString getGShaderName(uint flags);
200 static QString getFShaderName(uint flags);
201 QOpenGLShaderProgram* findOrLoadShader(uint flags);
202 //! A simple shader preprocessor that can replace #defines
203 static bool preprocessShader(const QString& fileName, const uint flags, QByteArray& processedSource);
204 //compiles and links the shader with this specified source code
205 bool loadShader(QOpenGLShaderProgram& program, const QByteArray& vShader, const QByteArray& gShader, const QByteArray& fShader);
206 void buildUniformCache(QOpenGLShaderProgram& program);
207
208 typedef QHash<uint,QOpenGLShaderProgram*> t_ShaderCache;
209 t_ShaderCache m_shaderCache;
210
211 typedef QHash<QByteArray,QOpenGLShaderProgram*> t_ShaderContentCache;
212 t_ShaderContentCache m_shaderContentCache;
213
214 typedef QHash<UNIFORM,GLuint> t_UniformCacheEntry;
215 typedef QHash<const QOpenGLShaderProgram*, t_UniformCacheEntry> t_UniformCache;
216 t_UniformCache m_uniformCache;
217 };
218
getShader(const GlobalShaderParameters & globals,const S3DScene::Material * mat)219 QOpenGLShaderProgram* ShaderMgr::getShader(const GlobalShaderParameters& globals,const S3DScene::Material* mat)
220 {
221 //Build bitflags from bools. Some stuff requires pixelLighting to be enabled, so check it too.
222
223 uint flags = INVALID;
224 if(globals.openglES) flags|=OGL_ES2;
225 if(!globals.shadowTransform)
226 {
227 flags |= SHADING;
228 if(globals.pixelLighting) flags|= PIXEL_LIGHTING;
229 if(globals.pixelLighting && globals.shadows) flags|= SHADOWS;
230 if(globals.pixelLighting && globals.shadows && globals.shadowFilterQuality>S3DEnum::SFQ_HARDWARE) flags|= SHADOW_FILTER;
231 if(globals.pixelLighting && globals.shadows && globals.shadowFilterQuality>S3DEnum::SFQ_LOW_HARDWARE) flags|= SHADOW_FILTER_HQ;
232 if(globals.pixelLighting && globals.shadows && !globals.hwShadowSamplers && (globals.shadowFilterQuality == S3DEnum::SFQ_LOW || globals.shadowFilterQuality == S3DEnum::SFQ_HIGH) && globals.pcss) flags|= PCSS;
233 if(globals.hwShadowSamplers) flags|=HW_SHADOW_SAMPLERS;
234 if(globals.geometryShader) flags|= GEOMETRY_SHADER;
235 if(globals.torchLight) flags|= TORCH;
236 if(globals.frustumSplits == 1) flags|= SINGLE_SHADOW_FRUSTUM;
237 }
238 else
239 {
240 flags |= TRANSFORM;
241 }
242
243 if(mat)
244 {
245 if(mat->bAlphatest && mat->tex_Kd && mat->tex_Kd->hasAlphaChannel()) //alpha test needs diffuse texture, otherwise it would not make sense
246 flags|= ALPHATEST;
247 if(mat->traits.hasSpecularity)
248 flags|= MAT_SPECULAR;
249 if(mat->traits.hasTransparency || mat->traits.isFading)
250 flags|= BLENDING;
251 if(mat->traits.hasDiffuseTexture)
252 flags|= MAT_DIFFUSETEX;
253 if(mat->traits.hasEmissiveTexture)
254 flags|= MAT_EMISSIVETEX;
255 if(mat->traits.hasBumpTexture && globals.bump && globals.pixelLighting)
256 flags|= BUMP;
257 if(mat->traits.hasHeightTexture && globals.bump && globals.pixelLighting)
258 flags|= HEIGHT;
259 }
260
261 return findOrLoadShader(flags);
262 }
263
getDebugShader()264 QOpenGLShaderProgram* ShaderMgr::getDebugShader()
265 {
266 return findOrLoadShader(DEBUG);
267 }
268
getCubeShader()269 QOpenGLShaderProgram* ShaderMgr::getCubeShader()
270 {
271 return findOrLoadShader(CUBEMAP);
272 }
273
getTextureShader()274 QOpenGLShaderProgram* ShaderMgr::getTextureShader()
275 {
276 return findOrLoadShader(MAT_DIFFUSETEX);
277 }
278
uniformLocation(const QOpenGLShaderProgram * shad,UNIFORM uni)279 GLint ShaderMgr::uniformLocation(const QOpenGLShaderProgram *shad, UNIFORM uni)
280 {
281 auto it = m_uniformCache.find(shad);
282 if(it!=m_uniformCache.end())
283 {
284 return it.value().value(uni,-1);
285 }
286 return -1;
287 }
288
289 #endif
290