]> git.localhorst.tv Git - blank.git/blob - src/graphics/shader.cpp
reorganize basic rendering functionality
[blank.git] / src / graphics / shader.cpp
1 #include "BlendedSprite.hpp"
2 #include "BlockLighting.hpp"
3 #include "DirectionalLighting.hpp"
4 #include "Program.hpp"
5 #include "Shader.hpp"
6
7 #include "Texture.hpp"
8 #include "../app/init.hpp"
9
10 #include <algorithm>
11 #include <iostream>
12 #include <memory>
13 #include <ostream>
14 #include <stdexcept>
15 #include <string>
16
17
18 namespace {
19
20 void gl_error(std::string msg) {
21         const GLubyte *errBegin = gluErrorString(glGetError());
22         if (errBegin && *errBegin != '\0') {
23                 const GLubyte *errEnd = errBegin;
24                 while (*errEnd != '\0') {
25                         ++errEnd;
26                 }
27                 msg += ": ";
28                 msg.append(errBegin, errEnd);
29         }
30         throw std::runtime_error(msg);
31 }
32
33 }
34
35 namespace blank {
36
37 Shader::Shader(GLenum type)
38 : handle(glCreateShader(type)) {
39         if (handle == 0) {
40                 gl_error("glCreateShader");
41         }
42 }
43
44 Shader::~Shader() {
45         if (handle != 0) {
46                 glDeleteShader(handle);
47         }
48 }
49
50 Shader::Shader(Shader &&other) noexcept
51 : handle(other.handle) {
52         other.handle = 0;
53 }
54
55 Shader &Shader::operator =(Shader &&other) noexcept {
56         std::swap(handle, other.handle);
57         return *this;
58 }
59
60
61 void Shader::Source(const GLchar *src) noexcept {
62         const GLchar* src_arr[] = { src };
63         glShaderSource(handle, 1, src_arr, nullptr);
64 }
65
66 void Shader::Compile() noexcept {
67         glCompileShader(handle);
68 }
69
70 bool Shader::Compiled() const noexcept {
71         GLint compiled = GL_FALSE;
72         glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled);
73         return compiled == GL_TRUE;
74 }
75
76 void Shader::Log(std::ostream &out) const {
77         int log_len = 0, max_len = 0;
78         glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &max_len);
79         std::unique_ptr<char[]> log(new char[max_len]);
80         glGetShaderInfoLog(handle, max_len, &log_len, log.get());
81         out.write(log.get(), log_len);
82 }
83
84
85 void Shader::AttachToProgram(GLuint id) const noexcept {
86         glAttachShader(id, handle);
87 }
88
89
90 Program::Program()
91 : handle(glCreateProgram()) {
92         if (handle == 0) {
93                 gl_error("glCreateProgram");
94         }
95 }
96
97 Program::~Program() {
98         if (handle != 0) {
99                 glDeleteProgram(handle);
100         }
101 }
102
103
104 const Shader &Program::LoadShader(GLenum type, const GLchar *src) {
105         shaders.emplace_back(type);
106         Shader &shader = shaders.back();
107         shader.Source(src);
108         shader.Compile();
109         if (!shader.Compiled()) {
110                 shader.Log(std::cerr);
111                 throw std::runtime_error("compile shader");
112         }
113         Attach(shader);
114         return shader;
115 }
116
117 void Program::Attach(Shader &shader) noexcept {
118         shader.AttachToProgram(handle);
119 }
120
121 void Program::Link() noexcept {
122         glLinkProgram(handle);
123 }
124
125 bool Program::Linked() const noexcept {
126         GLint linked = GL_FALSE;
127         glGetProgramiv(handle, GL_LINK_STATUS, &linked);
128         return linked == GL_TRUE;
129 }
130
131 void Program::Log(std::ostream &out) const {
132         int log_len = 0, max_len = 0;
133         glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &max_len);
134         std::unique_ptr<char[]> log(new char[max_len]);
135         glGetProgramInfoLog(handle, max_len, &log_len, log.get());
136         out.write(log.get(), log_len);
137 }
138
139
140 GLint Program::AttributeLocation(const GLchar *name) const noexcept {
141         return glGetAttribLocation(handle, name);
142 }
143
144 GLint Program::UniformLocation(const GLchar *name) const noexcept {
145         return glGetUniformLocation(handle, name);
146 }
147
148
149 DirectionalLighting::DirectionalLighting()
150 : program()
151 , light_direction(1.0f, 3.0f, 2.0f)
152 , light_color(0.9f, 0.9f, 0.9f)
153 , vp(1.0f)
154 , m_handle(0)
155 , mv_handle(0)
156 , mvp_handle(0)
157 , light_direction_handle(0)
158 , light_color_handle(0)
159 , fog_density_handle(0) {
160         program.LoadShader(
161                 GL_VERTEX_SHADER,
162                 "#version 330 core\n"
163                 "layout(location = 0) in vec3 vtx_position;\n"
164                 "layout(location = 1) in vec3 vtx_color;\n"
165                 "layout(location = 2) in vec3 vtx_normal;\n"
166                 "uniform mat4 M;\n"
167                 "uniform mat4 MV;\n"
168                 "uniform mat4 MVP;\n"
169                 "out vec3 frag_color;\n"
170                 "out vec3 vtx_viewspace;\n"
171                 "out vec3 normal;\n"
172                 "void main() {\n"
173                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
174                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
175                         "normal = (M * vec4(vtx_normal, 0)).xyz;\n"
176                         "frag_color = vtx_color;\n"
177                 "}\n"
178         );
179         program.LoadShader(
180                 GL_FRAGMENT_SHADER,
181                 "#version 330 core\n"
182                 "in vec3 frag_color;\n"
183                 "in vec3 vtx_viewspace;\n"
184                 "in vec3 normal;\n"
185                 "uniform vec3 light_direction;\n"
186                 "uniform vec3 light_color;\n"
187                 "uniform float fog_density;\n"
188                 "out vec3 color;\n"
189                 "void main() {\n"
190                         "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
191                         // this should be the same as the clear color, otherwise looks really weird
192                         "vec3 fog_color = vec3(0, 0, 0);\n"
193                         "float e = 2.718281828;\n"
194                         "vec3 n = normalize(normal);\n"
195                         "vec3 l = normalize(light_direction);\n"
196                         "float cos_theta = clamp(dot(n, l), 0, 1);\n"
197                         "vec3 reflect_color = ambient + frag_color * light_color * cos_theta;\n"
198                         "float value = pow(e, -pow(fog_density * length(vtx_viewspace), 5));"
199                         "color = mix(fog_color, reflect_color, value);\n"
200                 "}\n"
201         );
202         program.Link();
203         if (!program.Linked()) {
204                 program.Log(std::cerr);
205                 throw std::runtime_error("link program");
206         }
207
208         m_handle = program.UniformLocation("M");
209         mv_handle = program.UniformLocation("MV");
210         mvp_handle = program.UniformLocation("MVP");
211         light_direction_handle = program.UniformLocation("light_direction");
212         light_color_handle = program.UniformLocation("light_color");
213         fog_density_handle = program.UniformLocation("fog_density");
214 }
215
216
217 void DirectionalLighting::Activate() noexcept {
218         program.Use();
219
220         glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
221         glUniform3f(light_color_handle, light_color.x, light_color.y, light_color.z);
222 }
223
224 void DirectionalLighting::SetM(const glm::mat4 &m) noexcept {
225         glm::mat4 mv(view * m);
226         glm::mat4 mvp(vp * m);
227         glUniformMatrix4fv(m_handle, 1, GL_FALSE, &m[0][0]);
228         glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
229         glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
230 }
231
232 void DirectionalLighting::SetLightDirection(const glm::vec3 &dir) noexcept {
233         light_direction = -dir;
234         glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
235 }
236
237 void DirectionalLighting::SetFogDensity(float f) noexcept {
238         fog_density = f;
239         glUniform1f(fog_density_handle, fog_density);
240 }
241
242 void DirectionalLighting::SetProjection(const glm::mat4 &p) noexcept {
243         projection = p;
244         vp = p * view;
245 }
246
247 void DirectionalLighting::SetView(const glm::mat4 &v) noexcept {
248         view = v;
249         vp = projection * v;
250 }
251
252 void DirectionalLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
253         projection = p;
254         view = v;
255         vp = p * v;
256 }
257
258 void DirectionalLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
259         SetVP(v, p);
260         SetM(m);
261 }
262
263
264 BlockLighting::BlockLighting()
265 : program()
266 , vp(1.0f)
267 , mv_handle(0)
268 , mvp_handle(0)
269 , fog_density_handle(0) {
270         program.LoadShader(
271                 GL_VERTEX_SHADER,
272                 "#version 330 core\n"
273                 "layout(location = 0) in vec3 vtx_position;\n"
274                 "layout(location = 1) in vec3 vtx_color;\n"
275                 "layout(location = 2) in float vtx_light;\n"
276                 "uniform mat4 MV;\n"
277                 "uniform mat4 MVP;\n"
278                 "out vec3 frag_color;\n"
279                 "out vec3 vtx_viewspace;\n"
280                 "out float frag_light;\n"
281                 "void main() {\n"
282                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
283                         "frag_color = vtx_color;\n"
284                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
285                         "frag_light = vtx_light;\n"
286                 "}\n"
287         );
288         program.LoadShader(
289                 GL_FRAGMENT_SHADER,
290                 "#version 330 core\n"
291                 "in vec3 frag_color;\n"
292                 "in vec3 vtx_viewspace;\n"
293                 "in float frag_light;\n"
294                 "uniform float fog_density;\n"
295                 "out vec3 color;\n"
296                 "void main() {\n"
297                         "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
298                         "float light_power = clamp(pow(0.8, 15 - frag_light), 0, 1);\n"
299                         "vec3 fog_color = vec3(0, 0, 0);\n"
300                         "float e = 2.718281828;\n"
301                         //"vec3 reflect_color = ambient + frag_color * light_power;\n"
302                         "vec3 reflect_color = frag_color * light_power;\n"
303                         "float value = pow(e, -pow(fog_density * length(vtx_viewspace), 5));"
304                         "color = mix(fog_color, reflect_color, value);\n"
305                 "}\n"
306         );
307         program.Link();
308         if (!program.Linked()) {
309                 program.Log(std::cerr);
310                 throw std::runtime_error("link program");
311         }
312
313         mv_handle = program.UniformLocation("MV");
314         mvp_handle = program.UniformLocation("MVP");
315         fog_density_handle = program.UniformLocation("fog_density");
316 }
317
318
319 void BlockLighting::Activate() noexcept {
320         program.Use();
321 }
322
323 void BlockLighting::SetM(const glm::mat4 &m) noexcept {
324         glm::mat4 mv(view * m);
325         glm::mat4 mvp(vp * m);
326         glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
327         glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
328 }
329
330 void BlockLighting::SetFogDensity(float f) noexcept {
331         fog_density = f;
332         glUniform1f(fog_density_handle, fog_density);
333 }
334
335 void BlockLighting::SetProjection(const glm::mat4 &p) noexcept {
336         projection = p;
337         vp = p * view;
338 }
339
340 void BlockLighting::SetView(const glm::mat4 &v) noexcept {
341         view = v;
342         vp = projection * v;
343 }
344
345 void BlockLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
346         projection = p;
347         view = v;
348         vp = p * v;
349 }
350
351 void BlockLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
352         SetVP(v, p);
353         SetM(m);
354 }
355
356
357 BlendedSprite::BlendedSprite()
358 : program()
359 , vp(1.0f)
360 , mvp_handle(0)
361 , sampler_handle(0) {
362         program.LoadShader(
363                 GL_VERTEX_SHADER,
364                 "#version 330 core\n"
365                 "layout(location = 0) in vec3 vtx_position;\n"
366                 "layout(location = 1) in vec2 vtx_tex_uv;\n"
367                 "uniform mat4 MVP;\n"
368                 "out vec2 frag_tex_uv;\n"
369                 "void main() {\n"
370                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
371                         "frag_tex_uv = vtx_tex_uv;\n"
372                 "}\n"
373         );
374         program.LoadShader(
375                 GL_FRAGMENT_SHADER,
376                 "#version 330 core\n"
377                 "in vec2 frag_tex_uv;\n"
378                 "uniform sampler2D tex_sampler;\n"
379                 "out vec4 color;\n"
380                 "void main() {\n"
381                         "color = texture(tex_sampler, frag_tex_uv);\n"
382                 "}\n"
383         );
384         program.Link();
385         if (!program.Linked()) {
386                 program.Log(std::cerr);
387                 throw std::runtime_error("link program");
388         }
389
390         mvp_handle = program.UniformLocation("MVP");
391         sampler_handle = program.UniformLocation("tex_sampler");
392 }
393
394
395 void BlendedSprite::Activate() noexcept {
396         program.Use();
397 }
398
399 void BlendedSprite::SetM(const glm::mat4 &m) noexcept {
400         glm::mat4 mvp(vp * m);
401         glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
402 }
403
404 void BlendedSprite::SetProjection(const glm::mat4 &p) noexcept {
405         projection = p;
406         vp = p * view;
407 }
408
409 void BlendedSprite::SetView(const glm::mat4 &v) noexcept {
410         view = v;
411         vp = projection * v;
412 }
413
414 void BlendedSprite::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
415         projection = p;
416         view = v;
417         vp = p * v;
418 }
419
420 void BlendedSprite::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
421         SetVP(v, p);
422         SetM(m);
423 }
424
425 void BlendedSprite::SetTexture(Texture &tex) noexcept {
426         glActiveTexture(GL_TEXTURE0);
427         tex.Bind();
428         glUniform1i(sampler_handle, 0);
429 }
430
431 }