]> git.localhorst.tv Git - blobs.git/blob - src/graphics/shader.cpp
basic creature model
[blobs.git] / src / graphics / shader.cpp
1 #include "CreatureSkin.hpp"
2 #include "PlanetSurface.hpp"
3 #include "Program.hpp"
4 #include "Shader.hpp"
5 #include "SunSurface.hpp"
6
7 #include "ArrayTexture.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 #include <glm/gtc/type_ptr.hpp>
17
18
19 namespace {
20
21 void gl_error(std::string msg) {
22         const GLubyte *errBegin = gluErrorString(glGetError());
23         if (errBegin && *errBegin != '\0') {
24                 const GLubyte *errEnd = errBegin;
25                 while (*errEnd != '\0') {
26                         ++errEnd;
27                 }
28                 msg += ": ";
29                 msg.append(errBegin, errEnd);
30         }
31         throw std::runtime_error(msg);
32 }
33
34 }
35
36 namespace blobs {
37 namespace graphics {
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::AttributeLocation(const std::string &name) const noexcept {
147         return AttributeLocation(name.c_str());
148 }
149
150 GLint Program::UniformLocation(const GLchar *name) const noexcept {
151         return glGetUniformLocation(handle, name);
152 }
153
154 GLint Program::UniformLocation(const std::string &name) const noexcept {
155         return UniformLocation(name.c_str());
156 }
157
158
159 void Program::Uniform(GLint loc, GLint val) noexcept {
160         glUniform1i(loc, val);
161 }
162
163 void Program::Uniform(GLint loc, float val) noexcept {
164         glUniform1f(loc, val);
165 }
166
167 void Program::Uniform(GLint loc, const glm::vec3 &val) noexcept {
168         glUniform3fv(loc, 1, glm::value_ptr(val));
169 }
170
171 void Program::Uniform(GLint loc, const glm::vec4 &val) noexcept {
172         glUniform4fv(loc, 1, glm::value_ptr(val));
173 }
174
175 void Program::Uniform(GLint loc, const glm::mat4 &val) noexcept {
176         glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(val));
177 }
178
179
180 constexpr int PlanetSurface::MAX_LIGHTS;
181
182 PlanetSurface::PlanetSurface()
183 : prog() {
184         prog.LoadShader(
185                 GL_VERTEX_SHADER,
186                 "#version 330 core\n"
187
188                 "layout(location = 0) in vec3 vtx_position;\n"
189                 "layout(location = 1) in vec3 vtx_tex_uv;\n"
190
191                 "uniform mat4 M;\n"
192                 "uniform mat4 MV;\n"
193                 "uniform mat4 MVP;\n"
194
195                 "out vec3 frag_tex_uv;\n"
196                 "out vec3 vtx_viewspace;\n"
197
198                 "void main() {\n"
199                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
200                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
201                         "frag_tex_uv = vtx_tex_uv;\n"
202                 "}\n"
203         );
204         prog.LoadShader(
205                 GL_FRAGMENT_SHADER,
206                 "#version 330 core\n"
207
208                 "struct LightSource {\n"
209                         "vec3 position;\n"
210                         "vec3 color;\n"
211                         "float strength;\n"
212                 "};\n"
213
214                 "in vec3 vtx_viewspace;\n"
215                 "in vec3 frag_tex_uv;\n"
216
217                 "uniform sampler2DArray tex_sampler;\n"
218                 "uniform vec3 normal;\n"
219                 "uniform int num_lights;\n"
220                 "uniform LightSource light[8];\n"
221
222                 "out vec3 color;\n"
223
224                 "void main() {\n"
225                         "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
226                         "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
227                         "for (int i = 0; i < num_lights; ++i) {\n"
228                                 "vec3 to_light = light[i].position - vtx_viewspace;\n"
229                                 "float distance = length(to_light) + length(vtx_viewspace);\n"
230                                 "vec3 light_dir = normalize(to_light);\n"
231                                 "float attenuation = light[i].strength / (distance * distance);\n"
232                                 "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * tex_color;\n"
233                                 "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
234                                 "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
235                                 "if (dot(normal, light_dir) >= 0.0) {\n"
236                                         "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
237                                 "}\n"
238                                 "total_light = total_light + diffuse + specular;\n"
239                         "}\n"
240                         "color = total_light;\n"
241                 "}\n"
242         );
243         prog.Link();
244         if (!prog.Linked()) {
245                 prog.Log(std::cerr);
246                 throw std::runtime_error("link program");
247         }
248         m_handle = prog.UniformLocation("M");
249         mv_handle = prog.UniformLocation("MV");
250         mvp_handle = prog.UniformLocation("MVP");
251         sampler_handle = prog.UniformLocation("tex_sampler");
252         normal_handle = prog.UniformLocation("normal");
253         num_lights_handle = prog.UniformLocation("num_lights");
254         for (int i = 0; i < MAX_LIGHTS; ++i) {
255                 light_handle[3 * i + 0]  = prog.UniformLocation("light[" + std::to_string(i) + "].position");
256                 light_handle[3 * i + 1]  = prog.UniformLocation("light[" + std::to_string(i) + "].color");
257                 light_handle[3 * i + 2]  = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
258         }
259 }
260
261 PlanetSurface::~PlanetSurface() {
262 }
263
264 void PlanetSurface::Activate() noexcept {
265         prog.Use();
266         glEnable(GL_DEPTH_TEST);
267         glDepthFunc(GL_LESS);
268         glEnable(GL_CULL_FACE);
269         glDisable(GL_BLEND);
270 }
271
272 void PlanetSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
273         m = mm;
274         v = vv;
275         p = pp;
276         mv = v * m;
277         mvp = p * mv;
278         prog.Uniform(m_handle, m);
279         prog.Uniform(mv_handle, mv);
280         prog.Uniform(mvp_handle, mvp);
281 }
282
283 void PlanetSurface::SetNormal(const glm::vec3 &n) noexcept {
284         prog.Uniform(normal_handle, n);
285 }
286
287 void PlanetSurface::SetTexture(ArrayTexture &tex) noexcept {
288         glActiveTexture(GL_TEXTURE0);
289         tex.Bind();
290         prog.Uniform(sampler_handle, GLint(0));
291 }
292
293 void PlanetSurface::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
294         prog.Uniform(light_handle[3 * n + 0], pos);
295         prog.Uniform(light_handle[3 * n + 1], color);
296         prog.Uniform(light_handle[3 * n + 2], strength);
297 }
298
299 void PlanetSurface::SetNumLights(int n) noexcept {
300         prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
301 }
302
303
304 SunSurface::SunSurface()
305 : prog() {
306         prog.LoadShader(
307                 GL_VERTEX_SHADER,
308                 "#version 330 core\n"
309
310                 "layout(location = 0) in vec3 vtx_position;\n"
311
312                 "uniform mat4 M;\n"
313                 "uniform mat4 MV;\n"
314                 "uniform mat4 MVP;\n"
315
316                 "out vec3 vtx_viewspace;\n"
317
318                 "void main() {\n"
319                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
320                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
321                 "}\n"
322         );
323         prog.LoadShader(
324                 GL_FRAGMENT_SHADER,
325                 "#version 330 core\n"
326
327                 "in vec3 vtx_viewspace;\n"
328
329                 "uniform vec3 light_color;\n"
330                 "uniform float light_strength;\n"
331
332                 "out vec3 color;\n"
333
334                 "void main() {\n"
335                         "vec3 to_light = -vtx_viewspace;\n"
336                         "float distance = length(to_light);\n"
337                         //"vec3 light_dir = normalize(to_light);\n"
338                         "float attenuation = light_strength / (distance * distance);\n"
339                         "color = attenuation * light_color;\n"
340                 "}\n"
341         );
342         prog.Link();
343         if (!prog.Linked()) {
344                 prog.Log(std::cerr);
345                 throw std::runtime_error("link program");
346         }
347         m_handle = prog.UniformLocation("M");
348         mv_handle = prog.UniformLocation("MV");
349         mvp_handle = prog.UniformLocation("MVP");
350         light_color_handle = prog.UniformLocation("light_color");
351         light_strength_handle = prog.UniformLocation("light_strength");
352
353         vao.Bind();
354         vao.BindAttributes();
355         vao.EnableAttribute(0);
356         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
357         vao.ReserveAttributes(8, GL_STATIC_DRAW);
358         {
359                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
360                 attrib[0].position = glm::vec3(-1.0f, -1.0f, -1.0f);
361                 attrib[1].position = glm::vec3(-1.0f, -1.0f,  1.0f);
362                 attrib[2].position = glm::vec3(-1.0f,  1.0f, -1.0f);
363                 attrib[3].position = glm::vec3(-1.0f,  1.0f,  1.0f);
364                 attrib[4].position = glm::vec3( 1.0f, -1.0f, -1.0f);
365                 attrib[5].position = glm::vec3( 1.0f, -1.0f,  1.0f);
366                 attrib[6].position = glm::vec3( 1.0f,  1.0f, -1.0f);
367                 attrib[7].position = glm::vec3( 1.0f,  1.0f,  1.0f);
368         }
369         vao.BindElements();
370         vao.ReserveElements(36, GL_STATIC_DRAW);
371         {
372                 auto element = vao.MapElements(GL_WRITE_ONLY);
373                 // -X
374                 element[ 0] = 0;
375                 element[ 1] = 1;
376                 element[ 2] = 2;
377                 element[ 3] = 2;
378                 element[ 4] = 1;
379                 element[ 5] = 3;
380                 // -Y
381                 element[ 6] = 0;
382                 element[ 7] = 4;
383                 element[ 8] = 1;
384                 element[ 9] = 1;
385                 element[10] = 4;
386                 element[11] = 5;
387                 // -Z
388                 element[12] = 0;
389                 element[13] = 2;
390                 element[14] = 4;
391                 element[15] = 4;
392                 element[16] = 2;
393                 element[17] = 6;
394                 // +Z
395                 element[18] = 1;
396                 element[19] = 5;
397                 element[20] = 3;
398                 element[21] = 3;
399                 element[22] = 5;
400                 element[23] = 7;
401                 // +Y
402                 element[24] = 3;
403                 element[25] = 7;
404                 element[26] = 2;
405                 element[27] = 2;
406                 element[28] = 7;
407                 element[29] = 6;
408                 // +X
409                 element[30] = 5;
410                 element[31] = 4;
411                 element[32] = 7;
412                 element[33] = 7;
413                 element[34] = 4;
414                 element[35] = 6;
415         }
416         vao.Unbind();
417 }
418
419 SunSurface::~SunSurface() {
420 }
421
422 void SunSurface::Activate() noexcept {
423         prog.Use();
424         glEnable(GL_DEPTH_TEST);
425         glDepthFunc(GL_LESS);
426         glEnable(GL_CULL_FACE);
427         glDisable(GL_BLEND);
428 }
429
430 void SunSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
431         m = mm;
432         v = vv;
433         p = pp;
434         mv = v * m;
435         mvp = p * mv;
436         prog.Uniform(m_handle, m);
437         prog.Uniform(mv_handle, mv);
438         prog.Uniform(mvp_handle, mvp);
439 }
440
441 void SunSurface::SetLight(const glm::vec3 &color, float strength) noexcept {
442         prog.Uniform(light_color_handle, color);
443         prog.Uniform(light_strength_handle, strength);
444 }
445
446 void SunSurface::Draw() const noexcept {
447         vao.Bind();
448         vao.DrawTriangles(36);
449 }
450
451
452 constexpr int CreatureSkin::MAX_LIGHTS;
453
454 CreatureSkin::CreatureSkin()
455 : prog() {
456         prog.LoadShader(
457                 GL_VERTEX_SHADER,
458                 "#version 330 core\n"
459
460                 "layout(location = 0) in vec3 vtx_position;\n"
461                 "layout(location = 1) in vec3 vtx_normal;\n"
462                 "layout(location = 2) in vec3 vtx_tex_uv;\n"
463
464                 "uniform mat4 M;\n"
465                 "uniform mat4 MV;\n"
466                 "uniform mat4 MVP;\n"
467
468                 "out vec3 vtx_viewspace;\n"
469                 "out vec3 frag_tex_uv;\n"
470                 "out vec3 normal;\n"
471
472                 "void main() {\n"
473                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
474                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
475                         "normal = normalize((MV * vec4(vtx_normal, 0)).xyz);\n"
476                         "frag_tex_uv = vtx_tex_uv;\n"
477                 "}\n"
478         );
479         prog.LoadShader(
480                 GL_FRAGMENT_SHADER,
481                 "#version 330 core\n"
482
483                 "struct LightSource {\n"
484                         "vec3 position;\n"
485                         "vec3 color;\n"
486                         "float strength;\n"
487                 "};\n"
488
489                 "in vec3 vtx_viewspace;\n"
490                 "in vec3 frag_tex_uv;\n"
491                 "in vec3 normal;\n"
492
493                 "uniform sampler2DArray tex_sampler;\n"
494                 "uniform int num_lights;\n"
495                 "uniform LightSource light[8];\n"
496
497                 "out vec3 color;\n"
498
499                 "void main() {\n"
500                         "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
501                         "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
502                         "for (int i = 0; i < num_lights; ++i) {\n"
503                                 "vec3 to_light = light[i].position - vtx_viewspace;\n"
504                                 "float distance = length(to_light) + length(vtx_viewspace);\n"
505                                 "vec3 light_dir = normalize(to_light);\n"
506                                 "float attenuation = light[i].strength / (distance * distance);\n"
507                                 "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * tex_color;\n"
508                                 "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
509                                 "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
510                                 "if (dot(normal, light_dir) >= 0.0) {\n"
511                                         "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
512                                 "}\n"
513                                 "total_light = total_light + diffuse + specular;\n"
514                         "}\n"
515                         "color = total_light;\n"
516                 "}\n"
517         );
518         prog.Link();
519         if (!prog.Linked()) {
520                 prog.Log(std::cerr);
521                 throw std::runtime_error("link program");
522         }
523         m_handle = prog.UniformLocation("M");
524         mv_handle = prog.UniformLocation("MV");
525         mvp_handle = prog.UniformLocation("MVP");
526         sampler_handle = prog.UniformLocation("tex_sampler");
527         num_lights_handle = prog.UniformLocation("num_lights");
528         for (int i = 0; i < MAX_LIGHTS; ++i) {
529                 light_handle[3 * i + 0]  = prog.UniformLocation("light[" + std::to_string(i) + "].position");
530                 light_handle[3 * i + 1]  = prog.UniformLocation("light[" + std::to_string(i) + "].color");
531                 light_handle[3 * i + 2]  = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
532         }
533 }
534
535 CreatureSkin::~CreatureSkin() {
536 }
537
538 void CreatureSkin::Activate() noexcept {
539         prog.Use();
540         glEnable(GL_DEPTH_TEST);
541         glDepthFunc(GL_LESS);
542         glEnable(GL_CULL_FACE);
543         glDisable(GL_BLEND);
544 }
545
546 void CreatureSkin::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
547         m = mm;
548         v = vv;
549         p = pp;
550         mv = v * m;
551         mvp = p * mv;
552         prog.Uniform(m_handle, m);
553         prog.Uniform(mv_handle, mv);
554         prog.Uniform(mvp_handle, mvp);
555 }
556
557 void CreatureSkin::SetTexture(ArrayTexture &tex) noexcept {
558         glActiveTexture(GL_TEXTURE0);
559         tex.Bind();
560         prog.Uniform(sampler_handle, GLint(0));
561 }
562
563 void CreatureSkin::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
564         prog.Uniform(light_handle[3 * n + 0], pos);
565         prog.Uniform(light_handle[3 * n + 1], color);
566         prog.Uniform(light_handle[3 * n + 2], strength);
567 }
568
569 void CreatureSkin::SetNumLights(int n) noexcept {
570         prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
571 }
572
573 }
574 }