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