| ... | ... |
@@ -4,7 +4,10 @@ A C++11 helper for [OpenGL][] >=2.0 [shaders][Shader]. |
| 4 | 4 |
|
| 5 | 5 |
`glshader` can load an arbitrary number of [shaders][Shader] with the type |
| 6 | 6 |
autodetected by the file extension (using the same rules as the [OpenGL / |
| 7 |
-OpenGL ES Reference Compiler][]). For example: |
|
| 7 |
+OpenGL ES Reference Compiler][]). [Uniforms][Uniform] are set with a unified |
|
| 8 |
+interface (using template specialization). Data is provided by [OpenGL |
|
| 9 |
+Mathematics (GLM)][] objects (which uses [OpenGL Shading Language (GLSL)][] |
|
| 10 |
+naming conventions). For example: |
|
| 8 | 11 |
|
| 9 | 12 |
```cpp |
| 10 | 13 |
Shader shader({
|
| ... | ... |
@@ -12,6 +15,9 @@ Shader shader({
|
| 12 | 15 |
"test.frag", |
| 13 | 16 |
}); |
| 14 | 17 |
shader.use(); |
| 18 |
+ |
|
| 19 |
+glm::vec4 color(1, 0, 0, 1); |
|
| 20 |
+shader.uniform("color", color);
|
|
| 15 | 21 |
``` |
| 16 | 22 |
|
| 17 | 23 |
Errors are handled by throwing [`std::runtime_error`][] with a [`what()`][] |
| ... | ... |
@@ -21,7 +27,10 @@ messages. |
| 21 | 27 |
|
| 22 | 28 |
[OpenGL]: https://www.opengl.org |
| 23 | 29 |
[Shader]: https://www.khronos.org/opengl/wiki/Shader |
| 30 |
+[Uniform]: https://www.khronos.org/opengl/wiki/Uniform |
|
| 24 | 31 |
[OpenGL / OpenGL ES Reference Compiler]: https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/ |
| 32 |
+[OpenGL Mathematics (GLM)]: https://glm.g-truc.net |
|
| 33 |
+[OpenGL Shading Language (GLSL)]: https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language |
|
| 25 | 34 |
[`std::runtime_error`]: https://en.cppreference.com/w/cpp/error/runtime_error |
| 26 | 35 |
[`what()`]: https://en.cppreference.com/w/cpp/error/exception/what |
| 27 | 36 |
|
| ... | ... |
@@ -45,7 +54,8 @@ manager][] can be done with: |
| 45 | 54 |
// Library dependencies. |
| 46 | 55 |
sudo apt-get install \ |
| 47 | 56 |
libgl1-mesa-dev \ |
| 48 |
- libglew-dev |
|
| 57 |
+ libglew-dev \ |
|
| 58 |
+ libglm-dev |
|
| 49 | 59 |
|
| 50 | 60 |
// Test dependencies. |
| 51 | 61 |
sudo apt-get install \ |
| ... | ... |
@@ -72,7 +72,8 @@ Shader::Shader(std::vector<std::string> paths, std::string name) |
| 72 | 72 |
name_{!name.empty()
|
| 73 | 73 |
? std::move(name) |
| 74 | 74 |
: STR_JOIN(", ", "'" << it << "'", paths_)
|
| 75 |
- } |
|
| 75 |
+ }, |
|
| 76 |
+ uniform_location_cache_{}
|
|
| 76 | 77 |
{
|
| 77 | 78 |
new_(); |
| 78 | 79 |
} |
| ... | ... |
@@ -80,9 +81,10 @@ Shader::Shader(std::vector<std::string> paths, std::string name) |
| 80 | 81 |
|
| 81 | 82 |
Shader::Shader(Shader const & other) |
| 82 | 83 |
: |
| 83 |
- program_{0},
|
|
| 84 |
- paths_ {other.paths_},
|
|
| 85 |
- name_ {other.name_}
|
|
| 84 |
+ program_ {0},
|
|
| 85 |
+ paths_ {other.paths_},
|
|
| 86 |
+ name_ {other.name_},
|
|
| 87 |
+ uniform_location_cache_{other.uniform_location_cache_}
|
|
| 86 | 88 |
{
|
| 87 | 89 |
new_(); |
| 88 | 90 |
} |
| ... | ... |
@@ -91,9 +93,10 @@ Shader::Shader(Shader const & other) |
| 91 | 93 |
Shader & Shader::operator=(Shader const & other) |
| 92 | 94 |
{
|
| 93 | 95 |
delete_(); |
| 94 |
- program_ = 0; |
|
| 95 |
- paths_ = other.paths_; |
|
| 96 |
- name_ = other.name_; |
|
| 96 |
+ program_ = 0; |
|
| 97 |
+ paths_ = other.paths_; |
|
| 98 |
+ name_ = other.name_; |
|
| 99 |
+ uniform_location_cache_ = other.uniform_location_cache_; |
|
| 97 | 100 |
new_(); |
| 98 | 101 |
return *this; |
| 99 | 102 |
} |
| ... | ... |
@@ -101,9 +104,10 @@ Shader & Shader::operator=(Shader const & other) |
| 101 | 104 |
|
| 102 | 105 |
Shader::Shader(Shader && other) |
| 103 | 106 |
: |
| 104 |
- program_{std::move(other.program_)},
|
|
| 105 |
- paths_ {std::move(other.paths_)},
|
|
| 106 |
- name_ {std::move(other.name_)}
|
|
| 107 |
+ program_ {std::move(other.program_)},
|
|
| 108 |
+ paths_ {std::move(other.paths_)},
|
|
| 109 |
+ name_ {std::move(other.name_)},
|
|
| 110 |
+ uniform_location_cache_{std::move(other.uniform_location_cache_)}
|
|
| 107 | 111 |
{
|
| 108 | 112 |
other.program_ = 0; |
| 109 | 113 |
} |
| ... | ... |
@@ -112,9 +116,10 @@ Shader::Shader(Shader && other) |
| 112 | 116 |
Shader & Shader::operator=(Shader && other) |
| 113 | 117 |
{
|
| 114 | 118 |
delete_(); |
| 115 |
- program_ = std::move(other.program_); |
|
| 116 |
- paths_ = std::move(other.paths_); |
|
| 117 |
- name_ = std::move(other.name_); |
|
| 119 |
+ program_ = std::move(other.program_); |
|
| 120 |
+ paths_ = std::move(other.paths_); |
|
| 121 |
+ name_ = std::move(other.name_); |
|
| 122 |
+ uniform_location_cache_ = std::move(other.uniform_location_cache_); |
|
| 118 | 123 |
other.program_ = 0; |
| 119 | 124 |
return *this; |
| 120 | 125 |
} |
| ... | ... |
@@ -259,3 +264,24 @@ void Shader::ensure_current_( |
| 259 | 264 |
)}; |
| 260 | 265 |
} |
| 261 | 266 |
} |
| 267 |
+ |
|
| 268 |
+ |
|
| 269 |
+GLint Shader::uniform_location_(std::string const & name) |
|
| 270 |
+{
|
|
| 271 |
+ // Try cache. |
|
| 272 |
+ auto cache_entry = uniform_location_cache_.find(name); |
|
| 273 |
+ if (cache_entry != uniform_location_cache_.end()) |
|
| 274 |
+ return cache_entry->second; |
|
| 275 |
+ |
|
| 276 |
+ // Query OpenGL. |
|
| 277 |
+ auto location = glGetUniformLocation(program_, name.c_str()); |
|
| 278 |
+ if (location == -1) |
|
| 279 |
+ throw std::runtime_error{STR(
|
|
| 280 |
+ "Failed to get location of uniform '" << name << |
|
| 281 |
+ "' of shader program " << name_ << "." |
|
| 282 |
+ )}; |
|
| 283 |
+ |
|
| 284 |
+ // Save in cache and return. |
|
| 285 |
+ uniform_location_cache_.emplace(name, location); |
|
| 286 |
+ return location; |
|
| 287 |
+} |
| ... | ... |
@@ -4,8 +4,11 @@ |
| 4 | 4 |
|
| 5 | 5 |
#include <vector> |
| 6 | 6 |
#include <string> |
| 7 |
+#include <unordered_map> |
|
| 7 | 8 |
|
| 8 | 9 |
#include <GL/glew.h> |
| 10 |
+#include <glm/glm.hpp> |
|
| 11 |
+#include <glm/gtc/type_ptr.hpp> |
|
| 9 | 12 |
|
| 10 | 13 |
|
| 11 | 14 |
class Shader {
|
| ... | ... |
@@ -20,19 +23,26 @@ public: |
| 20 | 23 |
|
| 21 | 24 |
Shader & use(); |
| 22 | 25 |
Shader & validate(); |
| 26 |
+ template<typename Value> |
|
| 27 |
+ Shader & uniform(std::string const & name, Value const & value) = delete; |
|
| 23 | 28 |
|
| 24 | 29 |
private: |
| 25 | 30 |
|
| 31 |
+ template<typename Value> |
|
| 32 |
+ using StringCache = std::unordered_map<std::string, Value>; |
|
| 33 |
+ |
|
| 26 | 34 |
void new_(); |
| 27 | 35 |
void delete_(); |
| 28 | 36 |
void ensure_current_( |
| 29 | 37 |
std::string const & operation, |
| 30 | 38 |
std::string const & name = "" |
| 31 | 39 |
); |
| 40 |
+ GLint uniform_location_(std::string const & name); |
|
| 32 | 41 |
|
| 33 | 42 |
GLuint program_; |
| 34 | 43 |
std::vector<std::string> paths_; |
| 35 | 44 |
std::string name_; |
| 45 |
+ StringCache<GLint> uniform_location_cache_; |
|
| 36 | 46 |
}; |
| 37 | 47 |
|
| 38 | 48 |
|
| ... | ... |
@@ -44,4 +54,88 @@ private: |
| 44 | 54 |
#endif |
| 45 | 55 |
|
| 46 | 56 |
|
| 57 |
+#define GLSHADER_UNIFORM(VALUE_TYPE, CODE) \ |
|
| 58 |
+ template<> \ |
|
| 59 |
+ inline Shader & Shader::uniform( \ |
|
| 60 |
+ std::string const & name, VALUE_TYPE const & value \ |
|
| 61 |
+ ) { \
|
|
| 62 |
+ GLSHADER_ENSURE_CURRENT("set uniform", name); \
|
|
| 63 |
+ CODE; \ |
|
| 64 |
+ return *this; \ |
|
| 65 |
+ } |
|
| 66 |
+ |
|
| 67 |
+#define GLSHADER_UNIFORM_SCALAR(VALUE_TYPE, GL_TYPE) \ |
|
| 68 |
+ GLSHADER_UNIFORM( \ |
|
| 69 |
+ VALUE_TYPE, \ |
|
| 70 |
+ glUniform1##GL_TYPE( \ |
|
| 71 |
+ uniform_location_(name), value \ |
|
| 72 |
+ ) \ |
|
| 73 |
+ ) |
|
| 74 |
+ |
|
| 75 |
+#define GLSHADER_UNIFORM_N(N, GLM_VALUE_TYPE, GL_TYPE) \ |
|
| 76 |
+ GLSHADER_UNIFORM( \ |
|
| 77 |
+ glm::GLM_VALUE_TYPE##N, \ |
|
| 78 |
+ glUniform##N##GL_TYPE##v( \ |
|
| 79 |
+ uniform_location_(name), 1, glm::value_ptr(value) \ |
|
| 80 |
+ ) \ |
|
| 81 |
+ ) |
|
| 82 |
+ |
|
| 83 |
+#define GLSHADER_UNIFORM_N_BOOL(N) \ |
|
| 84 |
+ GLSHADER_UNIFORM( \ |
|
| 85 |
+ glm::bvec##N, \ |
|
| 86 |
+ GLint int_value[N]; \ |
|
| 87 |
+ for (auto i = 0; i < N; ++i) \ |
|
| 88 |
+ int_value[i] = value[i]; \ |
|
| 89 |
+ glUniform##N##iv( \ |
|
| 90 |
+ uniform_location_(name), 1, &int_value[0] \ |
|
| 91 |
+ ) \ |
|
| 92 |
+ ) |
|
| 93 |
+ |
|
| 94 |
+#define GLSHADER_UNIFORM_MATRIX_N(N) \ |
|
| 95 |
+ GLSHADER_UNIFORM( \ |
|
| 96 |
+ glm::mat##N, \ |
|
| 97 |
+ glUniformMatrix##N##fv( \ |
|
| 98 |
+ uniform_location_(name), 1, GL_FALSE, glm::value_ptr(value) \ |
|
| 99 |
+ ) \ |
|
| 100 |
+ ) |
|
| 101 |
+ |
|
| 102 |
+#define GLSHADER_UNIFORM_MATRIX_N_M(N, M) \ |
|
| 103 |
+ GLSHADER_UNIFORM( \ |
|
| 104 |
+ glm::mat##N##x##M, \ |
|
| 105 |
+ glUniformMatrix##N##x##M##fv( \ |
|
| 106 |
+ uniform_location_(name), 1, GL_FALSE, glm::value_ptr(value) \ |
|
| 107 |
+ ) \ |
|
| 108 |
+ ) |
|
| 109 |
+ |
|
| 110 |
+GLSHADER_UNIFORM_SCALAR(bool, i) |
|
| 111 |
+GLSHADER_UNIFORM_SCALAR(int, i) |
|
| 112 |
+GLSHADER_UNIFORM_SCALAR(glm::uint, ui) |
|
| 113 |
+GLSHADER_UNIFORM_SCALAR(float, f) |
|
| 114 |
+GLSHADER_UNIFORM_SCALAR(double, f) |
|
| 115 |
+ |
|
| 116 |
+GLSHADER_UNIFORM_N_BOOL(2) |
|
| 117 |
+GLSHADER_UNIFORM_N_BOOL(3) |
|
| 118 |
+GLSHADER_UNIFORM_N_BOOL(4) |
|
| 119 |
+GLSHADER_UNIFORM_N(2, ivec, i) |
|
| 120 |
+GLSHADER_UNIFORM_N(3, ivec, i) |
|
| 121 |
+GLSHADER_UNIFORM_N(4, ivec, i) |
|
| 122 |
+GLSHADER_UNIFORM_N(2, uvec, ui) |
|
| 123 |
+GLSHADER_UNIFORM_N(3, uvec, ui) |
|
| 124 |
+GLSHADER_UNIFORM_N(4, uvec, ui) |
|
| 125 |
+GLSHADER_UNIFORM_N(2, vec, f) |
|
| 126 |
+GLSHADER_UNIFORM_N(3, vec, f) |
|
| 127 |
+GLSHADER_UNIFORM_N(4, vec, f) |
|
| 128 |
+ |
|
| 129 |
+GLSHADER_UNIFORM_MATRIX_N(2) |
|
| 130 |
+GLSHADER_UNIFORM_MATRIX_N(3) |
|
| 131 |
+GLSHADER_UNIFORM_MATRIX_N(4) |
|
| 132 |
+ |
|
| 133 |
+GLSHADER_UNIFORM_MATRIX_N_M(2, 3) |
|
| 134 |
+GLSHADER_UNIFORM_MATRIX_N_M(2, 4) |
|
| 135 |
+GLSHADER_UNIFORM_MATRIX_N_M(3, 2) |
|
| 136 |
+GLSHADER_UNIFORM_MATRIX_N_M(3, 4) |
|
| 137 |
+GLSHADER_UNIFORM_MATRIX_N_M(4, 2) |
|
| 138 |
+GLSHADER_UNIFORM_MATRIX_N_M(4, 3) |
|
| 139 |
+ |
|
| 140 |
+ |
|
| 47 | 141 |
#endif // GLSHADER_SHADER_HPP |
| ... | ... |
@@ -82,6 +82,17 @@ void test() {
|
| 82 | 82 |
}); |
| 83 | 83 |
shader.use(); |
| 84 | 84 |
shader.validate(); |
| 85 |
+ |
|
| 86 |
+ glm::vec4 color(1, 0, 0, 1); |
|
| 87 |
+ EXPECT_EXCEPTION( |
|
| 88 |
+ Shader({ "good.vert", "good.frag" }).uniform("noncurrent", color),
|
|
| 89 |
+ "Failed to set uniform 'noncurrent' of shader program 'good.vert', 'good.frag'; program is not current." |
|
| 90 |
+ ) |
|
| 91 |
+ EXPECT_EXCEPTION( |
|
| 92 |
+ shader.uniform("nonexistent", color),
|
|
| 93 |
+ "Failed to get location of uniform 'nonexistent' of shader program 'good.vert', 'good.frag'." |
|
| 94 |
+ ) |
|
| 95 |
+ shader.uniform("color", color);
|
|
| 85 | 96 |
} |
| 86 | 97 |
|
| 87 | 98 |
|