| ... | ... |
@@ -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 |