... | ... |
@@ -39,6 +39,8 @@ int main() |
39 | 39 |
std::array<float, 4> value1; |
40 | 40 |
std::array<float, 4> value2; |
41 | 41 |
} values = {}; |
42 |
+ auto texture0 = GLuint{}; |
|
43 |
+ glGenTextures(1, &texture0); |
|
42 | 44 |
|
43 | 45 |
// Global settings. |
44 | 46 |
Shader::root("assets/shaders"); |
... | ... |
@@ -65,6 +67,7 @@ int main() |
65 | 67 |
.uniform("light_count", light_count) |
66 | 68 |
.uniform("color", color) |
67 | 69 |
.uniform("values", values) |
70 |
+ .texture("texture0", texture0, GL_TEXTURE_2D) |
|
68 | 71 |
.validate(); |
69 | 72 |
Shader::uniform_buffer("values", values); |
70 | 73 |
} |
... | ... |
@@ -319,6 +322,26 @@ Bare arrays are not supported, wrap them in uniform blocks. |
319 | 322 |
[memory layout]: https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout |
320 | 323 |
[avoid `vec3`/`mat3`]: https://stackoverflow.com/questions/38172696/should-i-ever-use-a-vec3-inside-of-a-uniform-buffer-or-shader-storage-buffer-object |
321 | 324 |
|
325 |
+#### Textures |
|
326 |
+ |
|
327 |
+As a special case, uniform texture samplers can be set by calling |
|
328 |
+`texture(std::string const & name, GLuint texture, GLenum target, bool required |
|
329 |
+= true)`. Unless a texture unit is already correctly set up as a result of a |
|
330 |
+previous call, one is automatically allocated and its `target` bound to |
|
331 |
+`texture`. If a texture unit needs to be allocated but no unused one is |
|
332 |
+available (the limit can be queried with |
|
333 |
+[`glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, ...)`][glGet] the least |
|
334 |
+recently used one is reused. [`uniform`](#uniforms) is then called with the |
|
335 |
+given `name` (and `required`) and the allocated texture unit as the `value`. |
|
336 |
+ |
|
337 |
+Subsequent calls with a given `texture` are very cheap provided that the number |
|
338 |
+of other textures set since the previous call stay below the limit. |
|
339 |
+ |
|
340 |
+If a subsequent call for a given `texture` uses a different `target` an error |
|
341 |
+is thrown. |
|
342 |
+ |
|
343 |
+[glGet]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGet.xhtml |
|
344 |
+ |
|
322 | 345 |
## Dependencies |
323 | 346 |
|
324 | 347 |
Public (interface): |
... | ... |
@@ -13,6 +13,8 @@ A [C++11][] [OpenGL][] \>=[2.0][] [shader][] library. |
13 | 13 |
Overview of usage: |
14 | 14 |
|
15 | 15 |
```cpp |
16 |
+#include <array> |
|
17 |
+ |
|
16 | 18 |
#include <GL/glew.h> |
17 | 19 |
#include <glm/glm.hpp> |
18 | 20 |
|
... | ... |
@@ -32,6 +34,11 @@ int main() |
32 | 34 |
// Local data. |
33 | 35 |
auto light_count = 3; |
34 | 36 |
auto color = glm::vec4{1, 0, 0, 1}; |
37 |
+ struct |
|
38 |
+ { |
|
39 |
+ std::array<float, 4> value1; |
|
40 |
+ std::array<float, 4> value2; |
|
41 |
+ } values = {}; |
|
35 | 42 |
|
36 | 43 |
// Global settings. |
37 | 44 |
Shader::root("assets/shaders"); |
... | ... |
@@ -57,7 +64,9 @@ int main() |
57 | 64 |
.use() |
58 | 65 |
.uniform("light_count", light_count) |
59 | 66 |
.uniform("color", color) |
67 |
+ .uniform("values", values) |
|
60 | 68 |
.validate(); |
69 |
+ Shader::uniform_buffer("values", values); |
|
61 | 70 |
} |
62 | 71 |
``` |
63 | 72 |
|
... | ... |
@@ -294,6 +303,22 @@ the supplied variables `TYPE const & value` and `GLint const & location`, |
294 | 303 |
probably as parameters to `glUniform*`. `GLSHADER_UNIFORM_DELETE(TYPE)` can be |
295 | 304 |
used to [`delete`][] the specialization for a given type. |
296 | 305 |
|
306 |
+#### Uniform blocks / buffers |
|
307 |
+ |
|
308 |
+If no specialization is provided, [uniform block][]s are assumed. This requires |
|
309 |
+OpenGL \>=3.1 or the [`ARB_uniform_buffer_object`][] extension, an error is |
|
310 |
+thrown if it is not available. Shared, by name, [Uniform Buffer Object |
|
311 |
+(UBO)][]s are automatically allocated. Beware of alignment, it is recommended |
|
312 |
+to use the `std140` [memory layout][] and to [avoid `vec3`/`mat3`][]. |
|
313 |
+ |
|
314 |
+Bare arrays are not supported, wrap them in uniform blocks. |
|
315 |
+ |
|
316 |
+[Uniform block]: https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Uniform_blocks |
|
317 |
+[`ARB_uniform_buffer_object`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt |
|
318 |
+[Uniform Buffer Object (UBO)]: https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object |
|
319 |
+[memory layout]: https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout |
|
320 |
+[avoid `vec3`/`mat3`]: https://stackoverflow.com/questions/38172696/should-i-ever-use-a-vec3-inside-of-a-uniform-buffer-or-shader-storage-buffer-object |
|
321 |
+ |
|
297 | 322 |
## Dependencies |
298 | 323 |
|
299 | 324 |
Public (interface): |
... | ... |
@@ -14,8 +14,10 @@ Overview of usage: |
14 | 14 |
|
15 | 15 |
```cpp |
16 | 16 |
#include <GL/glew.h> |
17 |
+#include <glm/glm.hpp> |
|
17 | 18 |
|
18 | 19 |
#include <glshader.hpp> |
20 |
+#include <glshader_glm.hpp> |
|
19 | 21 |
|
20 | 22 |
|
21 | 23 |
// Global data. |
... | ... |
@@ -29,6 +31,7 @@ int main() |
29 | 31 |
{ |
30 | 32 |
// Local data. |
31 | 33 |
auto light_count = 3; |
34 |
+ auto color = glm::vec4{1, 0, 0, 1}; |
|
32 | 35 |
|
33 | 36 |
// Global settings. |
34 | 37 |
Shader::root("assets/shaders"); |
... | ... |
@@ -53,6 +56,7 @@ int main() |
53 | 56 |
player |
54 | 57 |
.use() |
55 | 58 |
.uniform("light_count", light_count) |
59 |
+ .uniform("color", color) |
|
56 | 60 |
.validate(); |
57 | 61 |
} |
58 | 62 |
``` |
... | ... |
@@ -271,6 +275,15 @@ literals) or use an explicit cast to get the `GLfloat` specialization. |
271 | 275 |
|
272 | 276 |
[`delete`]: https://en.cppreference.com/w/cpp/language/function#Deleted_functions |
273 | 277 |
|
278 |
+#### OpenGL Mathematics (GLM) |
|
279 |
+ |
|
280 |
+[OpenGL Mathematics (GLM)][] specializations are provided in the include file |
|
281 |
+`glshader_glm.hpp`. OpenGL Mathematics (GLM) uses [OpenGL Shading Language |
|
282 |
+(GLSL)][] naming conventions. |
|
283 |
+ |
|
284 |
+[OpenGL Mathematics (GLM)]: https://glm.g-truc.net |
|
285 |
+[OpenGL Shading Language (GLSL)]: https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language |
|
286 |
+ |
|
274 | 287 |
#### User-defined uniforms |
275 | 288 |
|
276 | 289 |
Support for e.g. third party mathematics libraries can be added by providing |
... | ... |
@@ -287,6 +300,7 @@ Public (interface): |
287 | 300 |
|
288 | 301 |
- [OpenGL][], system (e.g. [`libgl1-mesa-dev`][]). |
289 | 302 |
- [OpenGL Extension Wrangler (GLEW)][], system (e.g. [`libglew-dev`][]). |
303 |
+- [OpenGL Mathematics (GLM)][], **optional**, system (e.g. [`libglm-dev`][]). |
|
290 | 304 |
|
291 | 305 |
Private (build): |
292 | 306 |
|
... | ... |
@@ -295,12 +309,15 @@ Private (build): |
295 | 309 |
Private (tests): |
296 | 310 |
|
297 | 311 |
- [GLFW][], system (e.g. [`libglfw3-dev`][]). |
312 |
+- [OpenGL Mathematics (GLM)][], system (e.g. [`libglm-dev`][]). |
|
298 | 313 |
- [`gltest`][], downloaded as part of the CMake configure step. |
299 | 314 |
|
300 | 315 |
[OpenGL Extension Wrangler (GLEW)]: http://glew.sourceforge.net |
316 |
+[OpenGL Mathematics (GLM)]: https://glm.g-truc.net |
|
301 | 317 |
[GLFW]: https://www.glfw.org |
302 | 318 |
[`libgl1-mesa-dev`]: https://packages.debian.org/search?keywords=libgl1-mesa-dev |
303 | 319 |
[`libglew-dev`]: https://packages.debian.org/search?keywords=libglew-dev |
320 |
+[`libglm-dev`]: https://packages.debian.org/search?keywords=libglm-dev |
|
304 | 321 |
[`libglfw3-dev`]: https://packages.debian.org/search?keywords=libglfw3-dev |
305 | 322 |
[`str`]: https://git.rcrnstn.net/rcrnstn/str |
306 | 323 |
[`gltest`]: https://git.rcrnstn.net/rcrnstn/gltest |
... | ... |
@@ -27,6 +27,9 @@ constexpr auto frag_color = 0; |
27 | 27 |
|
28 | 28 |
int main() |
29 | 29 |
{ |
30 |
+ // Local data. |
|
31 |
+ auto light_count = 3; |
|
32 |
+ |
|
30 | 33 |
// Global settings. |
31 | 34 |
Shader::root("assets/shaders"); |
32 | 35 |
Shader::defines({ |
... | ... |
@@ -49,6 +52,7 @@ int main() |
49 | 52 |
// Use. |
50 | 53 |
player |
51 | 54 |
.use() |
55 |
+ .uniform("light_count", light_count) |
|
52 | 56 |
.validate(); |
53 | 57 |
} |
54 | 58 |
``` |
... | ... |
@@ -257,6 +261,16 @@ value is of the wrong type. [`use`](#use) must be called before calling |
257 | 261 |
|
258 | 262 |
[uniform]: https://www.khronos.org/opengl/wiki/Uniform |
259 | 263 |
|
264 |
+#### Scalar uniforms |
|
265 |
+ |
|
266 |
+Template specializations are provided for all `GL*` types corresponding to a |
|
267 |
+`glUniform1*` variant. As special cases, `GLboolean` and `bool` map to the `i` |
|
268 |
+(`GLint`) variant. The `GLdouble` specialization is [`delete`][]d to avoid |
|
269 |
+accidental conversions, change the type (with an `f`/`F` suffix in case of |
|
270 |
+literals) or use an explicit cast to get the `GLfloat` specialization. |
|
271 |
+ |
|
272 |
+[`delete`]: https://en.cppreference.com/w/cpp/language/function#Deleted_functions |
|
273 |
+ |
|
260 | 274 |
#### User-defined uniforms |
261 | 275 |
|
262 | 276 |
Support for e.g. third party mathematics libraries can be added by providing |
... | ... |
@@ -267,8 +281,6 @@ the supplied variables `TYPE const & value` and `GLint const & location`, |
267 | 281 |
probably as parameters to `glUniform*`. `GLSHADER_UNIFORM_DELETE(TYPE)` can be |
268 | 282 |
used to [`delete`][] the specialization for a given type. |
269 | 283 |
|
270 |
-[`delete`]: https://en.cppreference.com/w/cpp/language/function#Deleted_functions |
|
271 |
- |
|
272 | 284 |
## Dependencies |
273 | 285 |
|
274 | 286 |
Public (interface): |
... | ... |
@@ -137,6 +137,9 @@ Additional issues that are not typically detected by [`glValidateProgram`][] |
137 | 137 |
are checked for: |
138 | 138 |
|
139 | 139 |
- Shader program not [current](#use). |
140 |
+- Uniforms not set with a call to [`uniform`](#uniforms), uniform blocks not |
|
141 |
+ set with a call to `uniform` or `uniform_buffer`. (OpenGL defaults them to |
|
142 |
+ zero unless initialized in the shader.) |
|
140 | 143 |
|
141 | 144 |
If the macro [`NDEBUG`][] is defined, the check is optimised out. Therefore, it |
142 | 145 |
is recommended to always call `validate()` before issuing draw calls. |
... | ... |
@@ -148,7 +151,7 @@ is recommended to always call `validate()` before issuing draw calls. |
148 | 151 |
### Use |
149 | 152 |
|
150 | 153 |
The shader needs to be [use][]d by calling `use()` before performing certain |
151 |
-other actions on it. |
|
154 |
+other actions on it, such as calling [`uniform`](#uniforms). |
|
152 | 155 |
|
153 | 156 |
If the macro [`NDEBUG`][] is not defined, an error is thrown if this is not the |
154 | 157 |
case. |
... | ... |
@@ -243,6 +246,29 @@ up once at application startup. |
243 | 246 |
[`glDrawBuffers`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawBuffers.xhtml |
244 | 247 |
[program interface query]: https://www.khronos.org/opengl/wiki/Program_Introspection#Interface_query |
245 | 248 |
|
249 |
+### Uniforms |
|
250 |
+ |
|
251 |
+[Uniform][]s are set with the template specialized `uniform(std::string const & |
|
252 |
+name, T const & value, bool required = true)`. If `required` is `true` an error |
|
253 |
+is thrown if the shader does not contain an active uniform with the given |
|
254 |
+`name`. If the macro [`NDEBUG`][] is not defined an error is thrown if the |
|
255 |
+value is of the wrong type. [`use`](#use) must be called before calling |
|
256 |
+`uniform`. |
|
257 |
+ |
|
258 |
+[uniform]: https://www.khronos.org/opengl/wiki/Uniform |
|
259 |
+ |
|
260 |
+#### User-defined uniforms |
|
261 |
+ |
|
262 |
+Support for e.g. third party mathematics libraries can be added by providing |
|
263 |
+the relevant template specializations of `uniform`. The macro |
|
264 |
+`GLSHADER_UNIFORM(TYPE, CODE)` may be used to ease this task. `TYPE` is the |
|
265 |
+type for which to provide the specialization and `CODE` is code that may use |
|
266 |
+the supplied variables `TYPE const & value` and `GLint const & location`, |
|
267 |
+probably as parameters to `glUniform*`. `GLSHADER_UNIFORM_DELETE(TYPE)` can be |
|
268 |
+used to [`delete`][] the specialization for a given type. |
|
269 |
+ |
|
270 |
+[`delete`]: https://en.cppreference.com/w/cpp/language/function#Deleted_functions |
|
271 |
+ |
|
246 | 272 |
## Dependencies |
247 | 273 |
|
248 | 274 |
Public (interface): |
... | ... |
@@ -20,6 +20,9 @@ Overview of usage: |
20 | 20 |
|
21 | 21 |
// Global data. |
22 | 22 |
constexpr auto light_count_max = 32; |
23 |
+constexpr auto vert_position = 0; |
|
24 |
+constexpr auto vert_tex_coord = 1; |
|
25 |
+constexpr auto frag_color = 0; |
|
23 | 26 |
|
24 | 27 |
|
25 | 28 |
int main() |
... | ... |
@@ -29,6 +32,13 @@ int main() |
29 | 32 |
Shader::defines({ |
30 | 33 |
{"light_count_max", std::to_string(light_count_max)}, |
31 | 34 |
}); |
35 |
+ Shader::verts({ |
|
36 |
+ {"vert_position", vert_position}, |
|
37 |
+ {"vert_tex_coord", vert_tex_coord}, |
|
38 |
+ }); |
|
39 |
+ Shader::frags({ |
|
40 |
+ {"frag_color", frag_color}, |
|
41 |
+ }); |
|
32 | 42 |
|
33 | 43 |
// Create. |
34 | 44 |
auto player = Shader({ |
... | ... |
@@ -202,6 +212,37 @@ been seen before. |
202 | 212 |
[`glNamedStringARB`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt |
203 | 213 |
[`glCompileShaderIncludeARB`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt |
204 | 214 |
|
215 |
+### User-defined vertex inputs (attributes) and fragment outputs (data) |
|
216 |
+ |
|
217 |
+Locations for user-defined [vertex inputs][] (attributes) and [fragment |
|
218 |
+outputs][] (data, requires OpenGL >= 3.0) can be specified by calling |
|
219 |
+`Shader::verts(Shader::Locations const & verts)` and |
|
220 |
+`Shader::frags(Shader::Locations const & frags)` respectively before |
|
221 |
+instantiating a `Shader` that uses them. `Shader::Locations` is an alias for |
|
222 |
+`std::map<std::string, GLuint>`. Any `location` [layout qualifier][] overrides |
|
223 |
+the values specified this way. Note that a `Shader` object retains the input |
|
224 |
+and output locations that were in effect when it was instantiated. See |
|
225 |
+[`glBindAttribLocation`][] and [`glBindFragDataLocation`][]. Note that fragment |
|
226 |
+outputs are subject to [`glDrawBuffer`][] / [`glDrawBuffers`][]. |
|
227 |
+ |
|
228 |
+An error is thrown if a shader uses a non-specified input. No error is thrown |
|
229 |
+for non-specified outputs (this is mostly a result of the inability to |
|
230 |
+enumerate shader outputs before the introduction of the [program interface |
|
231 |
+query][]). Shaders need not use all of the specified inputs/outputs. |
|
232 |
+ |
|
233 |
+The intent is for the application to define global unchanging input/output |
|
234 |
+locations that are used by all shaders, geometry and framebuffers and set them |
|
235 |
+up once at application startup. |
|
236 |
+ |
|
237 |
+[vertex inputs]: https://www.khronos.org/opengl/wiki/Vertex_Shader#Inputs |
|
238 |
+[fragment outputs]: https://www.khronos.org/opengl/wiki/Fragment_Shader#Outputs |
|
239 |
+[layout qualifier]: https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL) |
|
240 |
+[`glBindAttribLocation`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindAttribLocation.xhtml |
|
241 |
+[`glBindFragDataLocation`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindFragDataLocation.xhtml |
|
242 |
+[`glDrawBuffer`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawBuffer.xhtml |
|
243 |
+[`glDrawBuffers`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawBuffers.xhtml |
|
244 |
+[program interface query]: https://www.khronos.org/opengl/wiki/Program_Introspection#Interface_query |
|
245 |
+ |
|
205 | 246 |
## Dependencies |
206 | 247 |
|
207 | 248 |
Public (interface): |
... | ... |
@@ -169,6 +169,39 @@ defines)` are injected after the `#version`. `Shader::Defines` is an alias for |
169 | 169 |
|
170 | 170 |
[`#define`]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Preprocessor_directives |
171 | 171 |
|
172 |
+#### `#include`s |
|
173 |
+ |
|
174 |
+Support for [`#include`][]s is provided, with the same syntax as the |
|
175 |
+[`ARB_shading_language_include`][] extension. An `#include`d path must be |
|
176 |
+surrounded by `<` `>`, in which case it is treated as relative to |
|
177 |
+[`root`](#loading-files), or by `"` `"`, in which case it is treated as |
|
178 |
+relative to the including file. |
|
179 |
+ |
|
180 |
+Even though the extension is not used for including files, an `#extension |
|
181 |
+GL_ARB_shading_language_include : require` line is required for portability, |
|
182 |
+otherwise an error is thrown. The line is silently dropped before the source is |
|
183 |
+fed to OpenGL so as to not upset the shader compiler if the extension is not |
|
184 |
+available. |
|
185 |
+ |
|
186 |
+If the [`ARB_shading_language_include`][] extension is available, `#line` |
|
187 |
+statements that include the file path are injected before every line (after the |
|
188 |
+`#version`) to improve error messages from the shader compiler. |
|
189 |
+ |
|
190 |
+Note that the `#include` statements are expanded before the source is fed to |
|
191 |
+OpenGL, and therefore before the [GLSL preprocessor][] is run which means |
|
192 |
+things like [include guard][]s do not work. (For portability, it is recommended |
|
193 |
+that include guards be used anyway.) Circular includes are broken by skipping |
|
194 |
+`#include`s where the *including file, line number, included file* tuple has |
|
195 |
+been seen before. |
|
196 |
+ |
|
197 |
+[`#include`]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Preprocessor_directives |
|
198 |
+[`glCompileShader`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glCompileShader.xhtml |
|
199 |
+[GLSL preprocessor]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Preprocessor_directives |
|
200 |
+[include guard]: https://en.wikipedia.org/wiki/Include_guard |
|
201 |
+[`ARB_shading_language_include`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt |
|
202 |
+[`glNamedStringARB`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt |
|
203 |
+[`glCompileShaderIncludeARB`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt |
|
204 |
+ |
|
172 | 205 |
## Dependencies |
173 | 206 |
|
174 | 207 |
Public (interface): |
... | ... |
@@ -18,10 +18,17 @@ Overview of usage: |
18 | 18 |
#include <glshader.hpp> |
19 | 19 |
|
20 | 20 |
|
21 |
+// Global data. |
|
22 |
+constexpr auto light_count_max = 32; |
|
23 |
+ |
|
24 |
+ |
|
21 | 25 |
int main() |
22 | 26 |
{ |
23 | 27 |
// Global settings. |
24 | 28 |
Shader::root("assets/shaders"); |
29 |
+ Shader::defines({ |
|
30 |
+ {"light_count_max", std::to_string(light_count_max)}, |
|
31 |
+ }); |
|
25 | 32 |
|
26 | 33 |
// Create. |
27 | 34 |
auto player = Shader({ |
... | ... |
@@ -154,6 +161,14 @@ is to use `#version 110` if none is provided.) |
154 | 161 |
|
155 | 162 |
[`#version`]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Version |
156 | 163 |
|
164 |
+#### `#define`s |
|
165 |
+ |
|
166 |
+[`#define`][]s specified with `Shader::defines(Shader::Defines const & |
|
167 |
+defines)` are injected after the `#version`. `Shader::Defines` is an alias for |
|
168 |
+`std::map<std::string, std::string>`. |
|
169 |
+ |
|
170 |
+[`#define`]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Preprocessor_directives |
|
171 |
+ |
|
157 | 172 |
## Dependencies |
158 | 173 |
|
159 | 174 |
Public (interface): |
... | ... |
@@ -138,6 +138,22 @@ case. |
138 | 138 |
|
139 | 139 |
[use]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glUseProgram.xhtml |
140 | 140 |
|
141 |
+### Preprocessing |
|
142 |
+ |
|
143 |
+A simple preprocessor is run before shader sources are passed to OpenGL. |
|
144 |
+ |
|
145 |
+At present the preprocessor does not interpret line continuations or multi-line |
|
146 |
+comments correctly on lines it acts upon. Other lines are left alone so they |
|
147 |
+can be used there without issue. |
|
148 |
+ |
|
149 |
+#### `#version` |
|
150 |
+ |
|
151 |
+Each supplied shader file is checked for the existence of one, and only one, |
|
152 |
+[`#version`][]. An error is thrown if this is not the case. (The OpenGL default |
|
153 |
+is to use `#version 110` if none is provided.) |
|
154 |
+ |
|
155 |
+[`#version`]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Version |
|
156 |
+ |
|
141 | 157 |
## Dependencies |
142 | 158 |
|
143 | 159 |
Public (interface): |
... | ... |
@@ -31,6 +31,7 @@ int main() |
31 | 31 |
|
32 | 32 |
// Use. |
33 | 33 |
player |
34 |
+ .use() |
|
34 | 35 |
.validate(); |
35 | 36 |
} |
36 | 37 |
``` |
... | ... |
@@ -116,7 +117,9 @@ OpenGL state to make sure that a subsequent draw call would succeed. An error |
116 | 117 |
is thrown if a problem is detected. |
117 | 118 |
|
118 | 119 |
Additional issues that are not typically detected by [`glValidateProgram`][] |
119 |
-are checked for. |
|
120 |
+are checked for: |
|
121 |
+ |
|
122 |
+- Shader program not [current](#use). |
|
120 | 123 |
|
121 | 124 |
If the macro [`NDEBUG`][] is defined, the check is optimised out. Therefore, it |
122 | 125 |
is recommended to always call `validate()` before issuing draw calls. |
... | ... |
@@ -125,6 +128,16 @@ is recommended to always call `validate()` before issuing draw calls. |
125 | 128 |
[`glValidateProgram`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glValidateProgram.xhtml |
126 | 129 |
[`NDEBUG`]: https://en.cppreference.com/w/c/error/assert |
127 | 130 |
|
131 |
+### Use |
|
132 |
+ |
|
133 |
+The shader needs to be [use][]d by calling `use()` before performing certain |
|
134 |
+other actions on it. |
|
135 |
+ |
|
136 |
+If the macro [`NDEBUG`][] is not defined, an error is thrown if this is not the |
|
137 |
+case. |
|
138 |
+ |
|
139 |
+[use]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glUseProgram.xhtml |
|
140 |
+ |
|
128 | 141 |
## Dependencies |
129 | 142 |
|
130 | 143 |
Public (interface): |
... | ... |
@@ -28,9 +28,18 @@ int main() |
28 | 28 |
"player.vert", |
29 | 29 |
"player.frag", "lighting.frag", |
30 | 30 |
}); |
31 |
+ |
|
32 |
+ // Use. |
|
33 |
+ player |
|
34 |
+ .validate(); |
|
31 | 35 |
} |
32 | 36 |
``` |
33 | 37 |
|
38 |
+All public (non-static) member functions return a reference to the current |
|
39 |
+object, allowing [method chaining][]. |
|
40 |
+ |
|
41 |
+[method chaining]: https://en.wikipedia.org/wiki/Method_chaining |
|
42 |
+ |
|
34 | 43 |
### Errors |
35 | 44 |
|
36 | 45 |
Errors are handled by throwing [`std::runtime_error`][] with a [`what()`][] |
... | ... |
@@ -100,6 +109,22 @@ a separating `"/"` if non-empty, to all paths (defaults to `""`). |
100 | 109 |
[shader stage]: https://www.khronos.org/opengl/wiki/Shader#Stages |
101 | 110 |
[OpenGL / OpenGL ES Reference Compiler]: https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/ |
102 | 111 |
|
112 |
+### Validation |
|
113 |
+ |
|
114 |
+By calling `validate()` the shader can be [validate][]d against the current |
|
115 |
+OpenGL state to make sure that a subsequent draw call would succeed. An error |
|
116 |
+is thrown if a problem is detected. |
|
117 |
+ |
|
118 |
+Additional issues that are not typically detected by [`glValidateProgram`][] |
|
119 |
+are checked for. |
|
120 |
+ |
|
121 |
+If the macro [`NDEBUG`][] is defined, the check is optimised out. Therefore, it |
|
122 |
+is recommended to always call `validate()` before issuing draw calls. |
|
123 |
+ |
|
124 |
+[validate]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glValidateProgram.xhtml |
|
125 |
+[`glValidateProgram`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glValidateProgram.xhtml |
|
126 |
+[`NDEBUG`]: https://en.cppreference.com/w/c/error/assert |
|
127 |
+ |
|
103 | 128 |
## Dependencies |
104 | 129 |
|
105 | 130 |
Public (interface): |
... | ... |
@@ -8,6 +8,122 @@ A [C++11][] [OpenGL][] \>=[2.0][] [shader][] library. |
8 | 8 |
[2.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history |
9 | 9 |
[shader]: https://www.khronos.org/opengl/wiki/Shader |
10 | 10 |
|
11 |
+## Usage |
|
12 |
+ |
|
13 |
+Overview of usage: |
|
14 |
+ |
|
15 |
+```cpp |
|
16 |
+#include <GL/glew.h> |
|
17 |
+ |
|
18 |
+#include <glshader.hpp> |
|
19 |
+ |
|
20 |
+ |
|
21 |
+int main() |
|
22 |
+{ |
|
23 |
+ // Global settings. |
|
24 |
+ Shader::root("assets/shaders"); |
|
25 |
+ |
|
26 |
+ // Create. |
|
27 |
+ auto player = Shader({ |
|
28 |
+ "player.vert", |
|
29 |
+ "player.frag", "lighting.frag", |
|
30 |
+ }); |
|
31 |
+} |
|
32 |
+``` |
|
33 |
+ |
|
34 |
+### Errors |
|
35 |
+ |
|
36 |
+Errors are handled by throwing [`std::runtime_error`][] with a [`what()`][] |
|
37 |
+that returns helpful context, including operating system messages and the |
|
38 |
+OpenGL info log where appropriate. |
|
39 |
+ |
|
40 |
+If OpenGL \>=4.3 or the extension [`GL_KHR_debug`][] is available, |
|
41 |
+[`glObjectLabel`][] is used to improve debug messages provided to any callback |
|
42 |
+registered with [`glDebugMessageCallback`][]. |
|
43 |
+ |
|
44 |
+Se [`tests/glshader.cpp`][] for an overview of some of the handled errors. |
|
45 |
+(Note that the tests are written for compatibility and convenience and do not |
|
46 |
+necessarily reflect current best practices.) |
|
47 |
+ |
|
48 |
+[`std::runtime_error`]: https://en.cppreference.com/w/cpp/error/runtime_error |
|
49 |
+[`what()`]: https://en.cppreference.com/w/cpp/error/exception/what |
|
50 |
+[`GL_KHR_debug`]: https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_debug.txt |
|
51 |
+[`glObjectLabel`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glObjectLabel.xhtml |
|
52 |
+[`glDebugMessageCallback`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDebugMessageCallback.xhtml |
|
53 |
+[`tests/glshader.cpp`]: tests/glshader.cpp |
|
54 |
+ |
|
55 |
+### Lifetime |
|
56 |
+ |
|
57 |
+No default constructor is defined. This, together with a throwing constructor, |
|
58 |
+means that a (non-moved-from) `Shader` object always corresponds to a valid |
|
59 |
+OpenGL shader object. |
|
60 |
+ |
|
61 |
+At destruction, the underlying OpenGL shader program is deleted, but not |
|
62 |
+explicitly unbound. Note that the behavior of [deletion unbinding][] depends on |
|
63 |
+the OpenGL version. It is recommended to always consider the underlying OpenGL |
|
64 |
+shader program to be invalid after destruction. |
|
65 |
+ |
|
66 |
+Copying (creating a new identical underlying OpenGL shader object) or move |
|
67 |
+assigning (deleting the underlying OpenGL shader object of the moved-into |
|
68 |
+`Shader` object) would probably be a (performance) bug and is disabled. I.e. |
|
69 |
+`Shader` is a move(-constructible)-only type. |
|
70 |
+ |
|
71 |
+[deletion unbinding]: https://www.khronos.org/opengl/wiki/OpenGL_Object#Deletion_unbinding |
|
72 |
+ |
|
73 |
+### Loading files |
|
74 |
+ |
|
75 |
+An arbitrary number of files can be loaded by giving them as arguments to the |
|
76 |
+`Shader(Shader::Paths const & paths)` constructor. |
|
77 |
+ |
|
78 |
+`Shader::Paths` is an alias for `std::set<std::string>`. `std::set` is used |
|
79 |
+over e.g. `std::vector` to facilitate deduplicating shaders based on paths, |
|
80 |
+even when the paths are given in different orders. `std::set` is used over |
|
81 |
+`std::unordered_set` to retain the same ordering in error messages as in the |
|
82 |
+creation for easy identification. |
|
83 |
+ |
|
84 |
+The [shader stage][] is automatically detected by the file extension using the |
|
85 |
+same rules as the [OpenGL / OpenGL ES Reference Compiler][]: |
|
86 |
+ |
|
87 |
+- `.vert`: vertex |
|
88 |
+- `.tesc`: tessellation control |
|
89 |
+- `.tese`: tessellation evaluation |
|
90 |
+- `.geom`: geometry |
|
91 |
+- `.frag`: fragment |
|
92 |
+- `.comp`: compute |
|
93 |
+ |
|
94 |
+If multiple files are provided for the same stage they are compiled separately |
|
95 |
+and linked into the final shader program. |
|
96 |
+ |
|
97 |
+The value passed to `Shader::root(std::string const & root)` is prepended, with |
|
98 |
+a separating `"/"` if non-empty, to all paths (defaults to `""`). |
|
99 |
+ |
|
100 |
+[shader stage]: https://www.khronos.org/opengl/wiki/Shader#Stages |
|
101 |
+[OpenGL / OpenGL ES Reference Compiler]: https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/ |
|
102 |
+ |
|
103 |
+## Dependencies |
|
104 |
+ |
|
105 |
+Public (interface): |
|
106 |
+ |
|
107 |
+- [OpenGL][], system (e.g. [`libgl1-mesa-dev`][]). |
|
108 |
+- [OpenGL Extension Wrangler (GLEW)][], system (e.g. [`libglew-dev`][]). |
|
109 |
+ |
|
110 |
+Private (build): |
|
111 |
+ |
|
112 |
+- [`str`][], downloaded as part of the CMake configure step. |
|
113 |
+ |
|
114 |
+Private (tests): |
|
115 |
+ |
|
116 |
+- [GLFW][], system (e.g. [`libglfw3-dev`][]). |
|
117 |
+- [`gltest`][], downloaded as part of the CMake configure step. |
|
118 |
+ |
|
119 |
+[OpenGL Extension Wrangler (GLEW)]: http://glew.sourceforge.net |
|
120 |
+[GLFW]: https://www.glfw.org |
|
121 |
+[`libgl1-mesa-dev`]: https://packages.debian.org/search?keywords=libgl1-mesa-dev |
|
122 |
+[`libglew-dev`]: https://packages.debian.org/search?keywords=libglew-dev |
|
123 |
+[`libglfw3-dev`]: https://packages.debian.org/search?keywords=libglfw3-dev |
|
124 |
+[`str`]: https://git.rcrnstn.net/rcrnstn/str |
|
125 |
+[`gltest`]: https://git.rcrnstn.net/rcrnstn/gltest |
|
126 |
+ |
|
11 | 127 |
## Build system |
12 | 128 |
|
13 | 129 |
This project supports [CMake][] and uses [`cmake-common`][]. There are several |
... | ... |
@@ -8,6 +8,95 @@ A [C++11][] [OpenGL][] \>=[2.0][] [shader][] library. |
8 | 8 |
[2.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history |
9 | 9 |
[shader]: https://www.khronos.org/opengl/wiki/Shader |
10 | 10 |
|
11 |
+## Build system |
|
12 |
+ |
|
13 |
+This project supports [CMake][] and uses [`cmake-common`][]. There are several |
|
14 |
+ways to use it in other CMake-based projects: |
|
15 |
+ |
|
16 |
+- With [`find_package`][]: ([Package][] and) [install][] it on the system. |
|
17 |
+ |
|
18 |
+- With [`add_subdirectory`][]: Bundle it. |
|
19 |
+ |
|
20 |
+- With [`FetchContent`][]: Download it as part of the CMake configure step. |
|
21 |
+ |
|
22 |
+- With [`cmake-common`][]: Use any of the above methods through a simplified |
|
23 |
+ interface. |
|
24 |
+ |
|
25 |
+As usual, use [`add_dependencies`][] or [`target_link_libraries`][] (or |
|
26 |
+`cmake-common`'s `DEPENDENCIES_*`) to declare the dependency. |
|
27 |
+ |
|
28 |
+[CMake]: https://cmake.org |
|
29 |
+[`cmake-common`]: https://git.rcrnstn.net/rcrnstn/cmake-common |
|
30 |
+[`FetchContent`]: https://cmake.org/cmake/help/v3.14/module/FetchContent.html |
|
31 |
+[`add_subdirectory`]: https://cmake.org/cmake/help/v3.14/command/add_subdirectory.html |
|
32 |
+[`find_package`]: https://cmake.org/cmake/help/v3.14/command/find_package.html |
|
33 |
+[Package]: #Package |
|
34 |
+[Install]: #Install |
|
35 |
+[`add_dependencies`]: https://cmake.org/cmake/help/v3.14/command/add_dependencies.html |
|
36 |
+[`target_link_libraries`]: https://cmake.org/cmake/help/v3.14/command/target_link_libraries.html |
|
37 |
+ |
|
38 |
+### Configure and generate |
|
39 |
+ |
|
40 |
+To configure and generate a build tree, use `cmake`: |
|
41 |
+ |
|
42 |
+```sh |
|
43 |
+cmake -B _build |
|
44 |
+``` |
|
45 |
+ |
|
46 |
+To set the build type, pass e.g. `-D`[`CMAKE_BUILD_TYPE`][]`=Release`. |
|
47 |
+ |
|
48 |
+[`cmake`]: https://cmake.org/cmake/help/v3.14/manual/cmake.1.html#generate-a-project-buildsystem |
|
49 |
+[`CMAKE_BUILD_TYPE`]: https://cmake.org/cmake/help/v3.14/variable/CMAKE_BUILD_TYPE.html |
|
50 |
+ |
|
51 |
+### Build |
|
52 |
+ |
|
53 |
+To build, use [`cmake --build`][]: |
|
54 |
+ |
|
55 |
+```sh |
|
56 |
+cmake --build _build |
|
57 |
+``` |
|
58 |
+ |
|
59 |
+To disable building tests, pass `-D`[`BUILD_TESTING`][]`=OFF`. |
|
60 |
+ |
|
61 |
+[`cmake --build`]: https://cmake.org/cmake/help/v3.14/manual/cmake.1.html#build-a-project |
|
62 |
+[`BUILD_TESTING`]: https://cmake.org/cmake/help/v3.14/module/CTest.html |
|
63 |
+ |
|
64 |
+### Test |
|
65 |
+ |
|
66 |
+To run tests, use [`ctest`][]: |
|
67 |
+ |
|
68 |
+```sh |
|
69 |
+(cd _build && ctest) |
|
70 |
+``` |
|
71 |
+ |
|
72 |
+To show output from failing tests, pass `--output-on-failure`. To show output |
|
73 |
+from all tests, pass `--verbose`. |
|
74 |
+ |
|
75 |
+[`ctest`]: https://cmake.org/cmake/help/v3.14/manual/ctest.1.html |
|
76 |
+ |
|
77 |
+### Package |
|
78 |
+ |
|
79 |
+To package, use [`cpack`][]: |
|
80 |
+ |
|
81 |
+```sh |
|
82 |
+(cd _build && cpack) |
|
83 |
+``` |
|
84 |
+ |
|
85 |
+[`cpack`]: https://cmake.org/cmake/help/v3.14/manual/cpack.1.html |
|
86 |
+ |
|
87 |
+### Install |
|
88 |
+ |
|
89 |
+To install onto the current system, use [`cmake --install`][]: |
|
90 |
+ |
|
91 |
+```sh |
|
92 |
+cmake --install _build |
|
93 |
+``` |
|
94 |
+ |
|
95 |
+To set the prefix, pass e.g. `-D`[`CMAKE_INSTALL_PREFIX`][]`="$HOME/.local"`. |
|
96 |
+ |
|
97 |
+[`cmake --install`]: https://cmake.org/cmake/help/v3.14/manual/cmake.1.html#install-a-project |
|
98 |
+[`CMAKE_INSTALL_PREFIX`]: https://cmake.org/cmake/help/v3.14/variable/CMAKE_INSTALL_PREFIX.html |
|
99 |
+ |
|
11 | 100 |
## License |
12 | 101 |
|
13 | 102 |
Licensed under the [ISC License][] unless otherwise noted, see the |
... | ... |
@@ -7,3 +7,11 @@ A [C++11][] [OpenGL][] \>=[2.0][] [shader][] library. |
7 | 7 |
[OpenGL]: https://en.wikipedia.org/wiki/OpenGL |
8 | 8 |
[2.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history |
9 | 9 |
[shader]: https://www.khronos.org/opengl/wiki/Shader |
10 |
+ |
|
11 |
+## License |
|
12 |
+ |
|
13 |
+Licensed under the [ISC License][] unless otherwise noted, see the |
|
14 |
+[`LICENSE`][] file. |
|
15 |
+ |
|
16 |
+[ISC License]: https://choosealicense.com/licenses/isc |
|
17 |
+[`LICENSE`]: LICENSE |
1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,9 @@ |
1 |
+# [`glshader`][] |
|
2 |
+ |
|
3 |
+A [C++11][] [OpenGL][] \>=[2.0][] [shader][] library. |
|
4 |
+ |
|
5 |
+[`glshader`]: https://git.rcrnstn.net/rcrnstn/glshader |
|
6 |
+[C++11]: https://en.wikipedia.org/wiki/C++11 |
|
7 |
+[OpenGL]: https://en.wikipedia.org/wiki/OpenGL |
|
8 |
+[2.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history |
|
9 |
+[shader]: https://www.khronos.org/opengl/wiki/Shader |