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