]> git.localhorst.tv Git - blobs.git/blob - src/graphics/shader.cpp
more orbits and stuff
[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::UniformLocation(const GLchar *name) const noexcept {
146         return glGetUniformLocation(handle, name);
147 }
148
149
150 void Program::Uniform(GLint loc, GLint val) noexcept {
151         glUniform1i(loc, val);
152 }
153
154 void Program::Uniform(GLint loc, float val) noexcept {
155         glUniform1f(loc, val);
156 }
157
158 void Program::Uniform(GLint loc, const glm::vec3 &val) noexcept {
159         glUniform3fv(loc, 1, glm::value_ptr(val));
160 }
161
162 void Program::Uniform(GLint loc, const glm::vec4 &val) noexcept {
163         glUniform4fv(loc, 1, glm::value_ptr(val));
164 }
165
166 void Program::Uniform(GLint loc, const glm::mat4 &val) noexcept {
167         glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(val));
168 }
169
170
171 PlanetSurface::PlanetSurface()
172 : prog() {
173         prog.LoadShader(
174                 GL_VERTEX_SHADER,
175                 "#version 330 core\n"
176
177                 "layout(location = 0) in vec3 vtx_position;\n"
178                 "layout(location = 1) in vec3 vtx_tex_uv;\n"
179
180                 "uniform mat4 M;\n"
181                 "uniform mat4 MV;\n"
182                 "uniform mat4 MVP;\n"
183
184                 "out vec3 frag_tex_uv;\n"
185                 "out vec3 vtx_viewspace;\n"
186
187                 "void main() {\n"
188                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
189                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
190                         "frag_tex_uv = vtx_tex_uv;\n"
191                 "}\n"
192         );
193         prog.LoadShader(
194                 GL_FRAGMENT_SHADER,
195                 "#version 330 core\n"
196
197                 "struct LightSource {\n"
198                         "vec3 position;\n"
199                         "vec3 color;\n"
200                         "float strength;\n"
201                 "};\n"
202
203                 "in vec3 vtx_viewspace;\n"
204                 "in vec3 frag_tex_uv;\n"
205
206                 "uniform sampler2DArray tex_sampler;\n"
207                 "uniform vec3 normal;\n"
208                 "uniform LightSource light;\n"
209
210                 "out vec3 color;\n"
211
212                 "void main() {\n"
213                         "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
214                         "vec3 to_light = light.position - vtx_viewspace;\n"
215                         "float distance = length(to_light);\n"
216                         "vec3 light_dir = normalize(to_light);\n"
217                         "float attenuation = light.strength / (distance * distance);\n"
218                         "vec3 ambient = tex_color * vec3(0.01, 0.01, 0.01);\n"
219                         "vec3 diffuse = attenuation * max(0.0, dot(normal, light_dir)) * light.color * tex_color;\n"
220                         "vec3 view_dir = vec3(0.0, 0.0, 1.0);\n"
221                         "vec3 specular = vec3(0.0, 0.0, 0.0);\n"
222                         "if (dot(normal, light_dir) >= 0.0) {\n"
223                                 "attenuation * light.color * pow(max(0.0, dot(reflect(-light_dir, normal), view_dir)), 25.0);\n"
224                         "}\n"
225                         "color = ambient + diffuse + specular;\n"
226                 "}\n"
227         );
228         prog.Link();
229         if (!prog.Linked()) {
230                 prog.Log(std::cerr);
231                 throw std::runtime_error("link program");
232         }
233         m_handle = prog.UniformLocation("M");
234         mv_handle = prog.UniformLocation("MV");
235         mvp_handle = prog.UniformLocation("MVP");
236         sampler_handle = prog.UniformLocation("tex_sampler");
237         normal_handle = prog.UniformLocation("normal");
238         light_position_handle = prog.UniformLocation("light.position");
239         light_color_handle = prog.UniformLocation("light.color");
240         light_strength_handle = prog.UniformLocation("light.strength");
241 }
242
243 PlanetSurface::~PlanetSurface() {
244 }
245
246 void PlanetSurface::Activate() noexcept {
247         prog.Use();
248         glEnable(GL_DEPTH_TEST);
249         glDepthFunc(GL_LESS);
250         glEnable(GL_CULL_FACE);
251         glDisable(GL_BLEND);
252 }
253
254 void PlanetSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
255         m = mm;
256         v = vv;
257         p = pp;
258         mv = v * m;
259         mvp = p * mv;
260         prog.Uniform(m_handle, m);
261         prog.Uniform(mv_handle, mv);
262         prog.Uniform(mvp_handle, mvp);
263 }
264
265 void PlanetSurface::SetNormal(const glm::vec3 &n) noexcept {
266         prog.Uniform(normal_handle, n);
267 }
268
269 void PlanetSurface::SetTexture(ArrayTexture &tex) noexcept {
270         glActiveTexture(GL_TEXTURE0);
271         tex.Bind();
272         prog.Uniform(sampler_handle, GLint(0));
273 }
274
275 void PlanetSurface::SetLight(const glm::vec3 &pos, const glm::vec3 &color, float strength) noexcept {
276         prog.Uniform(light_position_handle, pos);
277         prog.Uniform(light_color_handle, color);
278         prog.Uniform(light_strength_handle, strength);
279 }
280
281
282 SunSurface::SunSurface()
283 : prog() {
284         prog.LoadShader(
285                 GL_VERTEX_SHADER,
286                 "#version 330 core\n"
287
288                 "layout(location = 0) in vec3 vtx_position;\n"
289
290                 "uniform mat4 M;\n"
291                 "uniform mat4 MV;\n"
292                 "uniform mat4 MVP;\n"
293
294                 "out vec3 vtx_viewspace;\n"
295
296                 "void main() {\n"
297                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
298                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
299                 "}\n"
300         );
301         prog.LoadShader(
302                 GL_FRAGMENT_SHADER,
303                 "#version 330 core\n"
304
305                 "in vec3 vtx_viewspace;\n"
306
307                 "uniform vec3 light_color;\n"
308                 "uniform float light_strength;\n"
309
310                 "out vec3 color;\n"
311
312                 "void main() {\n"
313                         "vec3 to_light = -vtx_viewspace;\n"
314                         "float distance = length(to_light);\n"
315                         //"vec3 light_dir = normalize(to_light);\n"
316                         "float attenuation = light_strength / (distance * distance);\n"
317                         "color = attenuation * light_color;\n"
318                 "}\n"
319         );
320         prog.Link();
321         if (!prog.Linked()) {
322                 prog.Log(std::cerr);
323                 throw std::runtime_error("link program");
324         }
325         m_handle = prog.UniformLocation("M");
326         mv_handle = prog.UniformLocation("MV");
327         mvp_handle = prog.UniformLocation("MVP");
328         light_color_handle = prog.UniformLocation("light_color");
329         light_strength_handle = prog.UniformLocation("light_strength");
330
331         vao.Bind();
332         vao.BindAttributes();
333         vao.EnableAttribute(0);
334         vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
335         vao.ReserveAttributes(8, GL_STATIC_DRAW);
336         {
337                 auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
338                 attrib[0].position = glm::vec3(-1.0f, -1.0f, -1.0f);
339                 attrib[1].position = glm::vec3(-1.0f, -1.0f,  1.0f);
340                 attrib[2].position = glm::vec3(-1.0f,  1.0f, -1.0f);
341                 attrib[3].position = glm::vec3(-1.0f,  1.0f,  1.0f);
342                 attrib[4].position = glm::vec3( 1.0f, -1.0f, -1.0f);
343                 attrib[5].position = glm::vec3( 1.0f, -1.0f,  1.0f);
344                 attrib[6].position = glm::vec3( 1.0f,  1.0f, -1.0f);
345                 attrib[7].position = glm::vec3( 1.0f,  1.0f,  1.0f);
346         }
347         vao.BindElements();
348         vao.ReserveElements(36, GL_STATIC_DRAW);
349         {
350                 auto element = vao.MapElements(GL_WRITE_ONLY);
351                 // -X
352                 element[ 0] = 0;
353                 element[ 1] = 1;
354                 element[ 2] = 2;
355                 element[ 3] = 2;
356                 element[ 4] = 1;
357                 element[ 5] = 3;
358                 // -Y
359                 element[ 6] = 0;
360                 element[ 7] = 4;
361                 element[ 8] = 1;
362                 element[ 9] = 1;
363                 element[10] = 4;
364                 element[11] = 5;
365                 // -Z
366                 element[12] = 0;
367                 element[13] = 2;
368                 element[14] = 4;
369                 element[15] = 4;
370                 element[16] = 2;
371                 element[17] = 6;
372                 // +Z
373                 element[18] = 1;
374                 element[19] = 5;
375                 element[20] = 3;
376                 element[21] = 3;
377                 element[22] = 5;
378                 element[23] = 7;
379                 // +Y
380                 element[24] = 3;
381                 element[25] = 7;
382                 element[26] = 2;
383                 element[27] = 2;
384                 element[28] = 7;
385                 element[29] = 6;
386                 // +X
387                 element[30] = 5;
388                 element[31] = 4;
389                 element[32] = 7;
390                 element[33] = 7;
391                 element[34] = 4;
392                 element[35] = 6;
393         }
394         vao.Unbind();
395 }
396
397 SunSurface::~SunSurface() {
398 }
399
400 void SunSurface::Activate() noexcept {
401         prog.Use();
402         glEnable(GL_DEPTH_TEST);
403         glDepthFunc(GL_LESS);
404         glEnable(GL_CULL_FACE);
405         glDisable(GL_BLEND);
406 }
407
408 void SunSurface::SetMVP(const glm::mat4 &mm, const glm::mat4 &vv, const glm::mat4 &pp) noexcept {
409         m = mm;
410         v = vv;
411         p = pp;
412         mv = v * m;
413         mvp = p * mv;
414         prog.Uniform(m_handle, m);
415         prog.Uniform(mv_handle, mv);
416         prog.Uniform(mvp_handle, mvp);
417 }
418
419 void SunSurface::SetLight(const glm::vec3 &color, float strength) noexcept {
420         prog.Uniform(light_color_handle, color);
421         prog.Uniform(light_strength_handle, strength);
422 }
423
424 void SunSurface::Draw() const noexcept {
425         vao.Bind();
426         vao.DrawTriangles(36);
427 }
428
429 }
430 }