]> git.localhorst.tv Git - blank.git/blob - src/graphics/shader.cpp
implemented font redering
[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         GLContext::EnableDepthTest();
219         GLContext::EnableBackfaceCulling();
220         program.Use();
221
222         glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
223         glUniform3f(light_color_handle, light_color.x, light_color.y, light_color.z);
224 }
225
226 void DirectionalLighting::SetM(const glm::mat4 &m) noexcept {
227         glm::mat4 mv(view * m);
228         glm::mat4 mvp(vp * m);
229         glUniformMatrix4fv(m_handle, 1, GL_FALSE, &m[0][0]);
230         glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
231         glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
232 }
233
234 void DirectionalLighting::SetLightDirection(const glm::vec3 &dir) noexcept {
235         light_direction = -dir;
236         glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
237 }
238
239 void DirectionalLighting::SetFogDensity(float f) noexcept {
240         fog_density = f;
241         glUniform1f(fog_density_handle, fog_density);
242 }
243
244 void DirectionalLighting::SetProjection(const glm::mat4 &p) noexcept {
245         projection = p;
246         vp = p * view;
247 }
248
249 void DirectionalLighting::SetView(const glm::mat4 &v) noexcept {
250         view = v;
251         vp = projection * v;
252 }
253
254 void DirectionalLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
255         projection = p;
256         view = v;
257         vp = p * v;
258 }
259
260 void DirectionalLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
261         SetVP(v, p);
262         SetM(m);
263 }
264
265
266 BlockLighting::BlockLighting()
267 : program()
268 , vp(1.0f)
269 , mv_handle(0)
270 , mvp_handle(0)
271 , fog_density_handle(0) {
272         program.LoadShader(
273                 GL_VERTEX_SHADER,
274                 "#version 330 core\n"
275                 "layout(location = 0) in vec3 vtx_position;\n"
276                 "layout(location = 1) in vec3 vtx_color;\n"
277                 "layout(location = 2) in float vtx_light;\n"
278                 "uniform mat4 MV;\n"
279                 "uniform mat4 MVP;\n"
280                 "out vec3 frag_color;\n"
281                 "out vec3 vtx_viewspace;\n"
282                 "out float frag_light;\n"
283                 "void main() {\n"
284                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
285                         "frag_color = vtx_color;\n"
286                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
287                         "frag_light = vtx_light;\n"
288                 "}\n"
289         );
290         program.LoadShader(
291                 GL_FRAGMENT_SHADER,
292                 "#version 330 core\n"
293                 "in vec3 frag_color;\n"
294                 "in vec3 vtx_viewspace;\n"
295                 "in float frag_light;\n"
296                 "uniform float fog_density;\n"
297                 "out vec3 color;\n"
298                 "void main() {\n"
299                         "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
300                         "float light_power = clamp(pow(0.8, 15 - frag_light), 0, 1);\n"
301                         "vec3 fog_color = vec3(0, 0, 0);\n"
302                         "float e = 2.718281828;\n"
303                         //"vec3 reflect_color = ambient + frag_color * light_power;\n"
304                         "vec3 reflect_color = frag_color * light_power;\n"
305                         "float value = pow(e, -pow(fog_density * length(vtx_viewspace), 5));"
306                         "color = mix(fog_color, reflect_color, value);\n"
307                 "}\n"
308         );
309         program.Link();
310         if (!program.Linked()) {
311                 program.Log(std::cerr);
312                 throw std::runtime_error("link program");
313         }
314
315         mv_handle = program.UniformLocation("MV");
316         mvp_handle = program.UniformLocation("MVP");
317         fog_density_handle = program.UniformLocation("fog_density");
318 }
319
320
321 void BlockLighting::Activate() noexcept {
322         GLContext::EnableDepthTest();
323         GLContext::EnableBackfaceCulling();
324         GLContext::DisableAlphaBlending();
325         program.Use();
326 }
327
328 void BlockLighting::SetM(const glm::mat4 &m) noexcept {
329         glm::mat4 mv(view * m);
330         glm::mat4 mvp(vp * m);
331         glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
332         glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
333 }
334
335 void BlockLighting::SetFogDensity(float f) noexcept {
336         fog_density = f;
337         glUniform1f(fog_density_handle, fog_density);
338 }
339
340 void BlockLighting::SetProjection(const glm::mat4 &p) noexcept {
341         projection = p;
342         vp = p * view;
343 }
344
345 void BlockLighting::SetView(const glm::mat4 &v) noexcept {
346         view = v;
347         vp = projection * v;
348 }
349
350 void BlockLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
351         projection = p;
352         view = v;
353         vp = p * v;
354 }
355
356 void BlockLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
357         SetVP(v, p);
358         SetM(m);
359 }
360
361
362 BlendedSprite::BlendedSprite()
363 : program()
364 , vp(1.0f)
365 , mvp_handle(0)
366 , sampler_handle(0) {
367         program.LoadShader(
368                 GL_VERTEX_SHADER,
369                 "#version 330 core\n"
370                 "layout(location = 0) in vec3 vtx_position;\n"
371                 "layout(location = 1) in vec2 vtx_tex_uv;\n"
372                 "uniform mat4 MVP;\n"
373                 "out vec2 frag_tex_uv;\n"
374                 "void main() {\n"
375                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
376                         "frag_tex_uv = vtx_tex_uv;\n"
377                 "}\n"
378         );
379         program.LoadShader(
380                 GL_FRAGMENT_SHADER,
381                 "#version 330 core\n"
382                 "in vec2 frag_tex_uv;\n"
383                 "uniform sampler2D tex_sampler;\n"
384                 "out vec4 color;\n"
385                 "void main() {\n"
386                         "color = texture(tex_sampler, frag_tex_uv);\n"
387                 "}\n"
388         );
389         program.Link();
390         if (!program.Linked()) {
391                 program.Log(std::cerr);
392                 throw std::runtime_error("link program");
393         }
394
395         mvp_handle = program.UniformLocation("MVP");
396         sampler_handle = program.UniformLocation("tex_sampler");
397 }
398
399
400 void BlendedSprite::Activate() noexcept {
401         GLContext::EnableAlphaBlending();
402         program.Use();
403 }
404
405 void BlendedSprite::SetM(const glm::mat4 &m) noexcept {
406         glm::mat4 mvp(vp * m);
407         glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
408 }
409
410 void BlendedSprite::SetProjection(const glm::mat4 &p) noexcept {
411         projection = p;
412         vp = p * view;
413 }
414
415 void BlendedSprite::SetView(const glm::mat4 &v) noexcept {
416         view = v;
417         vp = projection * v;
418 }
419
420 void BlendedSprite::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
421         projection = p;
422         view = v;
423         vp = p * v;
424 }
425
426 void BlendedSprite::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
427         SetVP(v, p);
428         SetM(m);
429 }
430
431 void BlendedSprite::SetTexture(Texture &tex) noexcept {
432         glActiveTexture(GL_TEXTURE0);
433         tex.Bind();
434         glUniform1i(sampler_handle, 0);
435 }
436
437 }