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