Browse code

Add uniform support

Robert Cranston authored on 16/05/2020 10:39:49
Showing 5 changed files

... ...
@@ -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
... ...
@@ -1,6 +1,9 @@
1 1
 #version 110
2 2
 
3 3
 
4
+uniform vec4 color;
5
+
6
+
4 7
 void main() {
5
-    gl_FragColor = vec4(1, 0, 0, 1);
8
+    gl_FragColor = color;
6 9
 }
... ...
@@ -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