]> git.localhorst.tv Git - blobs.git/blob - src/graphics/shader.cpp
multiple light sources
[blobs.git] / src / graphics / shader.cpp
1 #include "PlanetSurface.hpp"
2 #include "Program.hpp"
3 #include "Shader.hpp"
4 #include "SunSurface.hpp"
5
6 #include "ArrayTexture.hpp"
7 #include "../app/init.hpp"
8
9 #include <algorithm>
10 #include <iostream>
11 #include <memory>
12 #include <ostream>
13 #include <stdexcept>
14 #include <string>
15 #include <glm/gtc/type_ptr.hpp>
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 blobs {
36 namespace graphics {
37
38 Shader::Shader(GLenum type)
39 : handle(glCreateShader(type)) {
40         if (handle == 0) {
41                 gl_error("glCreateShader");
42         }
43 }
44
45 Shader::~Shader() {
46         if (handle != 0) {
47                 glDeleteShader(handle);
48         }
49 }
50
51 Shader::Shader(Shader &&other) noexcept
52 : handle(other.handle) {
53         other.handle = 0;
54 }
55
56 Shader &Shader::operator =(Shader &&other) noexcept {
57         std::swap(handle, other.handle);
58         return *this;
59 }
60
61
62 void Shader::Source(const GLchar *src) noexcept {
63         const GLchar* src_arr[] = { src };
64         glShaderSource(handle, 1, src_arr, nullptr);
65 }
66
67 void Shader::Compile() noexcept {
68         glCompileShader(handle);
69 }
70
71 bool Shader::Compiled() const noexcept {
72         GLint compiled = GL_FALSE;
73         glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled);
74         return compiled == GL_TRUE;
75 }
76
77 void Shader::Log(std::ostream &out) const {
78         int log_len = 0, max_len = 0;
79         glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &max_len);
80         std::unique_ptr<char[]> log(new char[max_len]);
81         glGetShaderInfoLog(handle, max_len, &log_len, log.get());
82         out.write(log.get(), log_len);
83 }
84
85
86 void Shader::AttachToProgram(GLuint id) const noexcept {
87         glAttachShader(id, handle);
88 }
89
90
91 Program::Program()
92 : handle(glCreateProgram()) {
93         if (handle == 0) {
94                 gl_error("glCreateProgram");
95         }
96 }
97
98 Program::~Program() {
99         if (handle != 0) {
100                 glDeleteProgram(handle);
101         }
102 }
103
104
105 const Shader &Program::LoadShader(GLenum type, const GLchar *src) {
106         shaders.emplace_back(type);
107         Shader &shader = shaders.back();
108         shader.Source(src);
109         shader.Compile();
110         if (!shader.Compiled()) {
111                 shader.Log(std::cerr);
112                 throw std::runtime_error("compile shader");
113         }
114         Attach(shader);
115         return shader;
116 }
117
118 void Program::Attach(Shader &shader) noexcept {
119         shader.AttachToProgram(handle);
120 }
121
122 void Program::Link() noexcept {
123         glLinkProgram(handle);
124 }
125
126 bool Program::Linked() const noexcept {
127         GLint linked = GL_FALSE;
128         glGetProgramiv(handle, GL_LINK_STATUS, &linked);
129         return linked == GL_TRUE;
130 }
131
132 void Program::Log(std::ostream &out) const {
133         int log_len = 0, max_len = 0;
134         glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &max_len);
135         std::unique_ptr<char[]> log(new char[max_len]);
136         glGetProgramInfoLog(handle, max_len, &log_len, log.get());
137         out.write(log.get(), log_len);
138 }
139
140
141 GLint Program::AttributeLocation(const GLchar *name) const noexcept {
142         return glGetAttribLocation(handle, name);
143 }
144
145 GLint Program::AttributeLocation(const std::string &name) const noexcept {
146         return AttributeLocation(name.c_str());
147 }
148
149 GLint Program::UniformLocation(const GLchar *name) const noexcept {
150         return glGetUniformLocation(handle, name);
151 }
152
153 GLint Program::UniformLocation(const std::string &name) const noexcept {
154         return UniformLocation(name.c_str());
155 }
156
157
158 void Program::Uniform(GLint loc, GLint val) noexcept {
159         glUniform1i(loc, val);
160 }
161
162 void Program::Uniform(GLint loc, float val) noexcept {
163         glUniform1f(loc, val);
164 }
165
166 void Program::Uniform(GLint loc, const glm::vec3 &val) noexcept {
167         glUniform3fv(loc, 1, glm::value_ptr(val));
168 }
169
170 void Program::Uniform(GLint loc, const glm::vec4 &val) noexcept {
171         glUniform4fv(loc, 1, glm::value_ptr(val));
172 }
173
174 void Program::Uniform(GLint loc, const glm::mat4 &val) noexcept {
175         glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(val));
176 }
177
178
179 constexpr int PlanetSurface::MAX_LIGHTS;
180
181 PlanetSurface::PlanetSurface()
182 : prog() {
183         prog.LoadShader(
184                 GL_VERTEX_SHADER,
185                 "#version 330 core\n"
186
187                 "layout(location = 0) in vec3 vtx_position;\n"
188                 "layout(location = 1) in vec3 vtx_tex_uv;\n"
189
190                 "uniform mat4 M;\n"
191                 "uniform mat4 MV;\n"
192                 "uniform mat4 MVP;\n"
193
194                 "out vec3 frag_tex_uv;\n"
195                 "out vec3 vtx_viewspace;\n"
196
197                 "void main() {\n"
198                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
199                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
200                         "frag_tex_uv = vtx_tex_uv;\n"
201                 "}\n"
202         );
203         prog.LoadShader(
204                 GL_FRAGMENT_SHADER,
205                 "#version 330 core\n"
206
207                 "struct LightSource {\n"
208                         "vec3 position;\n"
209                         "vec3 color;\n"
210                         "float strength;\n"
211                 "};\n"
212
213                 "in vec3 vtx_viewspace;\n"
214                 "in vec3 frag_tex_uv;\n"
215
216                 "uniform sampler2DArray tex_sampler;\n"
217                 "uniform vec3 normal;\n"
218                 "uniform int num_lights;\n"
219                 "uniform LightSource light[8];\n"
220
221                 "out vec3 color;\n"
222
223                 "void main() {\n"
224                         "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
225                         "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
226                         "for (int i = 0; i < num_lights; ++i) {\n"
227                                 "vec3 to_light = light[i].position - vtx_viewspace;\n"
228                                 "float distance = length(to_light) + length(vtx_viewspace);\n"
229                                 "vec3 light_dir = normalize(to_light);\n"
230                                 "float attenuation = light[i].strength / (distance * distance);\n"
231                                 "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light[i].color * tex_color;\n"
232                                 "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
233                                 "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
234                                 "if (dot(normal, light_dir) >= 0.0) {\n"
235                                         "attenuation * light[i].color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
236                                 "}\n"
237                                 "total_light = total_light + diffuse + specular;\n"
238                         "}\n"
239                         "color = total_light;\n"
240                 "}\n"
241         );
242         prog.Link();
243         if (!prog.Linked()) {
244                 prog.Log(std::cerr);
245                 throw std::runtime_error("link program");
246         }
247         m_handle = prog.UniformLocation("M");
248         mv_handle = prog.UniformLocation("MV");
249         mvp_handle = prog.UniformLocation("MVP");
250         sampler_handle = prog.UniformLocation("tex_sampler");
251         normal_handle = prog.UniformLocation("normal");
252         num_lights_handle = prog.UniformLocation("num_lights");
253         for (int i = 0; i < MAX_LIGHTS; ++i) {
254                 light_handle[3 * i + 0]  = prog.UniformLocation("light[" + std::to_string(i) + "].position");
255                 light_handle[3 * i + 1]  = prog.UniformLocation("light[" + std::to_string(i) + "].color");
256                 light_handle[3 * i + 2]  = prog.UniformLocation("light[" + std::to_string(i) + "].strength");
257         }
258 }
259
260 PlanetSurface::~PlanetSurface() {
261 }
262
263 void PlanetSurface::Activate() noexcept {
264         prog.Use();
265         glEnable(GL_DEPTH_TEST);
266         glDepthFunc(GL_LESS);
267         glEnable(GL_CULL_FACE);
268         glDisable(GL_BLEND);
269 }
270
271 void PlanetSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
272         m = mm;
273         v = vv;
274         p = pp;
275         mv = v * m;
276         mvp = p * mv;
277         prog.Uniform(m_handle, m);
278         prog.Uniform(mv_handle, mv);
279         prog.Uniform(mvp_handle, mvp);
280 }
281
282 void PlanetSurface::SetNormal(const glm::vec3 &n) noexcept {
283         prog.Uniform(normal_handle, n);
284 }
285
286 void PlanetSurface::SetTexture(ArrayTexture &tex) noexcept {
287         glActiveTexture(GL_TEXTURE0);
288         tex.Bind();
289         prog.Uniform(sampler_handle, GLint(0));
290 }
291
292 void PlanetSurface::SetLight(int n, const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
293         prog.Uniform(light_handle[3 * n + 0], pos);
294         prog.Uniform(light_handle[3 * n + 1], color);
295         prog.Uniform(light_handle[3 * n + 2], strength);
296 }
297
298 void PlanetSurface::SetNumLights(int n) noexcept {
299         prog.Uniform(num_lights_handle, std::min(MAX_LIGHTS, n));
300 }
301
302
303 SunSurface::SunSurface()
304 : prog() {
305         prog.LoadShader(
306                 GL_VERTEX_SHADER,
307                 "#version 330 core\n"
308
309                 "layout(location = 0) in vec3 vtx_position;\n"
310
311                 "uniform mat4 M;\n"
312                 "uniform mat4 MV;\n"
313                 "uniform mat4 MVP;\n"
314
315                 "out vec3 vtx_viewspace;\n"
316
317                 "void main() {\n"
318                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
319                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
320                 "}\n"
321         );
322         prog.LoadShader(
323                 GL_FRAGMENT_SHADER,
324                 "#version 330 core\n"
325
326                 "in vec3 vtx_viewspace;\n"
327
328                 "uniform vec3 light_color;\n"
329                 "uniform float light_strength;\n"
330
331                 "out vec3 color;\n"
332
333                 "void main() {\n"
334                         "vec3 to_light = -vtx_viewspace;\n"
335                         "float distance = length(to_light);\n"
336                         //"vec3 light_dir = normalize(to_light);\n"
337                         "float attenuation = light_strength / (distance * distance);\n"
338                         "color = attenuation * light_color;\n"
339                 "}\n"
340         );
341         prog.Link();
342         if (!prog.Linked()) {
343                 prog.Log(std::cerr);
344                 throw std::runtime_error("link program");
345         }
346         m_handle = prog.UniformLocation("M");
347         mv_handle = prog.UniformLocation("MV");
348         mvp_handle = prog.UniformLocation("MVP");
349         light_color_handle = prog.UniformLocation("light_color");
350         light_strength_handle = prog.UniformLocation("light_strength");
351
352         vao.Bind();
353         vao.BindAttributes();
354         vao.EnableAttribute(0);
355         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
356         vao.ReserveAttributes(8, GL_STATIC_DRAW);
357         {
358                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
359                 attrib[0].position = glm::vec3(-1.0f, -1.0f, -1.0f);
360                 attrib[1].position = glm::vec3(-1.0f, -1.0f,  1.0f);
361                 attrib[2].position = glm::vec3(-1.0f,  1.0f, -1.0f);
362                 attrib[3].position = glm::vec3(-1.0f,  1.0f,  1.0f);
363                 attrib[4].position = glm::vec3( 1.0f, -1.0f, -1.0f);
364                 attrib[5].position = glm::vec3( 1.0f, -1.0f,  1.0f);
365                 attrib[6].position = glm::vec3( 1.0f,  1.0f, -1.0f);
366                 attrib[7].position = glm::vec3( 1.0f,  1.0f,  1.0f);
367         }
368         vao.BindElements();
369         vao.ReserveElements(36, GL_STATIC_DRAW);
370         {
371                 auto element = vao.MapElements(GL_WRITE_ONLY);
372                 // -X
373                 element[ 0] = 0;
374                 element[ 1] = 1;
375                 element[ 2] = 2;
376                 element[ 3] = 2;
377                 element[ 4] = 1;
378                 element[ 5] = 3;
379                 // -Y
380                 element[ 6] = 0;
381                 element[ 7] = 4;
382                 element[ 8] = 1;
383                 element[ 9] = 1;
384                 element[10] = 4;
385                 element[11] = 5;
386                 // -Z
387                 element[12] = 0;
388                 element[13] = 2;
389                 element[14] = 4;
390                 element[15] = 4;
391                 element[16] = 2;
392                 element[17] = 6;
393                 // +Z
394                 element[18] = 1;
395                 element[19] = 5;
396                 element[20] = 3;
397                 element[21] = 3;
398                 element[22] = 5;
399                 element[23] = 7;
400                 // +Y
401                 element[24] = 3;
402                 element[25] = 7;
403                 element[26] = 2;
404                 element[27] = 2;
405                 element[28] = 7;
406                 element[29] = 6;
407                 // +X
408                 element[30] = 5;
409                 element[31] = 4;
410                 element[32] = 7;
411                 element[33] = 7;
412                 element[34] = 4;
413                 element[35] = 6;
414         }
415         vao.Unbind();
416 }
417
418 SunSurface::~SunSurface() {
419 }
420
421 void SunSurface::Activate() noexcept {
422         prog.Use();
423         glEnable(GL_DEPTH_TEST);
424         glDepthFunc(GL_LESS);
425         glEnable(GL_CULL_FACE);
426         glDisable(GL_BLEND);
427 }
428
429 void SunSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
430         m = mm;
431         v = vv;
432         p = pp;
433         mv = v * m;
434         mvp = p * mv;
435         prog.Uniform(m_handle, m);
436         prog.Uniform(mv_handle, mv);
437         prog.Uniform(mvp_handle, mvp);
438 }
439
440 void SunSurface::SetLight(const glm::vec3 &color, float strength) noexcept {
441         prog.Uniform(light_color_handle, color);
442         prog.Uniform(light_strength_handle, strength);
443 }
444
445 void SunSurface::Draw() const noexcept {
446         vao.Bind();
447         vao.DrawTriangles(36);
448 }
449
450 }
451 }