]> git.localhorst.tv Git - blank.git/blob - src/graphics/shader.cpp
some code reorganization
[blank.git] / src / graphics / shader.cpp
1 #include "BlockLighting.hpp"
2 #include "DirectionalLighting.hpp"
3 #include "Program.hpp"
4 #include "Shader.hpp"
5
6 #include "../app/init.hpp"
7
8 #include <algorithm>
9 #include <iostream>
10 #include <memory>
11 #include <ostream>
12 #include <stdexcept>
13 #include <string>
14
15
16 namespace {
17
18 void gl_error(std::string msg) {
19         const GLubyte *errBegin = gluErrorString(glGetError());
20         if (errBegin && *errBegin != '\0') {
21                 const GLubyte *errEnd = errBegin;
22                 while (*errEnd != '\0') {
23                         ++errEnd;
24                 }
25                 msg += ": ";
26                 msg.append(errBegin, errEnd);
27         }
28         throw std::runtime_error(msg);
29 }
30
31 }
32
33 namespace blank {
34
35 Shader::Shader(GLenum type)
36 : handle(glCreateShader(type)) {
37         if (handle == 0) {
38                 gl_error("glCreateShader");
39         }
40 }
41
42 Shader::~Shader() {
43         if (handle != 0) {
44                 glDeleteShader(handle);
45         }
46 }
47
48 Shader::Shader(Shader &&other) noexcept
49 : handle(other.handle) {
50         other.handle = 0;
51 }
52
53 Shader &Shader::operator =(Shader &&other) noexcept {
54         std::swap(handle, other.handle);
55         return *this;
56 }
57
58
59 void Shader::Source(const GLchar *src) noexcept {
60         const GLchar* src_arr[] = { src };
61         glShaderSource(handle, 1, src_arr, nullptr);
62 }
63
64 void Shader::Compile() noexcept {
65         glCompileShader(handle);
66 }
67
68 bool Shader::Compiled() const noexcept {
69         GLint compiled = GL_FALSE;
70         glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled);
71         return compiled == GL_TRUE;
72 }
73
74 void Shader::Log(std::ostream &out) const {
75         int log_len = 0, max_len = 0;
76         glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &max_len);
77         std::unique_ptr<char[]> log(new char[max_len]);
78         glGetShaderInfoLog(handle, max_len, &log_len, log.get());
79         out.write(log.get(), log_len);
80 }
81
82
83 void Shader::AttachToProgram(GLuint id) const noexcept {
84         glAttachShader(id, handle);
85 }
86
87
88 Program::Program()
89 : handle(glCreateProgram()) {
90         if (handle == 0) {
91                 gl_error("glCreateProgram");
92         }
93 }
94
95 Program::~Program() {
96         if (handle != 0) {
97                 glDeleteProgram(handle);
98         }
99 }
100
101
102 const Shader &Program::LoadShader(GLenum type, const GLchar *src) {
103         shaders.emplace_back(type);
104         Shader &shader = shaders.back();
105         shader.Source(src);
106         shader.Compile();
107         if (!shader.Compiled()) {
108                 shader.Log(std::cerr);
109                 throw std::runtime_error("compile shader");
110         }
111         Attach(shader);
112         return shader;
113 }
114
115 void Program::Attach(Shader &shader) noexcept {
116         shader.AttachToProgram(handle);
117 }
118
119 void Program::Link() noexcept {
120         glLinkProgram(handle);
121 }
122
123 bool Program::Linked() const noexcept {
124         GLint linked = GL_FALSE;
125         glGetProgramiv(handle, GL_LINK_STATUS, &linked);
126         return linked == GL_TRUE;
127 }
128
129 void Program::Log(std::ostream &out) const {
130         int log_len = 0, max_len = 0;
131         glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &max_len);
132         std::unique_ptr<char[]> log(new char[max_len]);
133         glGetProgramInfoLog(handle, max_len, &log_len, log.get());
134         out.write(log.get(), log_len);
135 }
136
137
138 GLint Program::AttributeLocation(const GLchar *name) const noexcept {
139         return glGetAttribLocation(handle, name);
140 }
141
142 GLint Program::UniformLocation(const GLchar *name) const noexcept {
143         return glGetUniformLocation(handle, name);
144 }
145
146
147 DirectionalLighting::DirectionalLighting()
148 : program()
149 , light_direction(1.0f, 3.0f, 2.0f)
150 , light_color(0.9f, 0.9f, 0.9f)
151 , vp(1.0f)
152 , m_handle(0)
153 , mv_handle(0)
154 , mvp_handle(0)
155 , light_direction_handle(0)
156 , light_color_handle(0)
157 , fog_density_handle(0) {
158         program.LoadShader(
159                 GL_VERTEX_SHADER,
160                 "#version 330 core\n"
161                 "layout(location = 0) in vec3 vtx_position;\n"
162                 "layout(location = 1) in vec3 vtx_color;\n"
163                 "layout(location = 2) in vec3 vtx_normal;\n"
164                 "uniform mat4 M;\n"
165                 "uniform mat4 MV;\n"
166                 "uniform mat4 MVP;\n"
167                 "out vec3 frag_color;\n"
168                 "out vec3 vtx_viewspace;\n"
169                 "out vec3 normal;\n"
170                 "void main() {\n"
171                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
172                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
173                         "normal = (M * vec4(vtx_normal, 0)).xyz;\n"
174                         "frag_color = vtx_color;\n"
175                 "}\n"
176         );
177         program.LoadShader(
178                 GL_FRAGMENT_SHADER,
179                 "#version 330 core\n"
180                 "in vec3 frag_color;\n"
181                 "in vec3 vtx_viewspace;\n"
182                 "in vec3 normal;\n"
183                 "uniform vec3 light_direction;\n"
184                 "uniform vec3 light_color;\n"
185                 "uniform float fog_density;\n"
186                 "out vec3 color;\n"
187                 "void main() {\n"
188                         "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
189                         // this should be the same as the clear color, otherwise looks really weird
190                         "vec3 fog_color = vec3(0, 0, 0);\n"
191                         "float e = 2.718281828;\n"
192                         "vec3 n = normalize(normal);\n"
193                         "vec3 l = normalize(light_direction);\n"
194                         "float cos_theta = clamp(dot(n, l), 0, 1);\n"
195                         "vec3 reflect_color = ambient + frag_color * light_color * cos_theta;\n"
196                         "float value = pow(e, -pow(fog_density * length(vtx_viewspace), 5));"
197                         "color = mix(fog_color, reflect_color, value);\n"
198                 "}\n"
199         );
200         program.Link();
201         if (!program.Linked()) {
202                 program.Log(std::cerr);
203                 throw std::runtime_error("link program");
204         }
205
206         m_handle = program.UniformLocation("M");
207         mv_handle = program.UniformLocation("MV");
208         mvp_handle = program.UniformLocation("MVP");
209         light_direction_handle = program.UniformLocation("light_direction");
210         light_color_handle = program.UniformLocation("light_color");
211         fog_density_handle = program.UniformLocation("fog_density");
212 }
213
214
215 void DirectionalLighting::Activate() noexcept {
216         GLContext::EnableDepthTest();
217         GLContext::EnableBackfaceCulling();
218         program.Use();
219
220         glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
221         glUniform3f(light_color_handle, light_color.x, light_color.y, light_color.z);
222 }
223
224 void DirectionalLighting::SetM(const glm::mat4 &m) noexcept {
225         glm::mat4 mv(view * m);
226         glm::mat4 mvp(vp * m);
227         glUniformMatrix4fv(m_handle, 1, GL_FALSE, &m[0][0]);
228         glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
229         glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
230 }
231
232 void DirectionalLighting::SetLightDirection(const glm::vec3 &dir) noexcept {
233         light_direction = -dir;
234         glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
235 }
236
237 void DirectionalLighting::SetFogDensity(float f) noexcept {
238         fog_density = f;
239         glUniform1f(fog_density_handle, fog_density);
240 }
241
242 void DirectionalLighting::SetProjection(const glm::mat4 &p) noexcept {
243         projection = p;
244         vp = p * view;
245 }
246
247 void DirectionalLighting::SetView(const glm::mat4 &v) noexcept {
248         view = v;
249         vp = projection * v;
250 }
251
252 void DirectionalLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
253         projection = p;
254         view = v;
255         vp = p * v;
256 }
257
258 void DirectionalLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
259         SetVP(v, p);
260         SetM(m);
261 }
262
263
264 BlockLighting::BlockLighting()
265 : program()
266 , vp(1.0f)
267 , mv_handle(0)
268 , mvp_handle(0)
269 , fog_density_handle(0) {
270         program.LoadShader(
271                 GL_VERTEX_SHADER,
272                 "#version 330 core\n"
273                 "layout(location = 0) in vec3 vtx_position;\n"
274                 "layout(location = 1) in vec3 vtx_color;\n"
275                 "layout(location = 2) in float vtx_light;\n"
276                 "uniform mat4 MV;\n"
277                 "uniform mat4 MVP;\n"
278                 "out vec3 frag_color;\n"
279                 "out vec3 vtx_viewspace;\n"
280                 "out float frag_light;\n"
281                 "void main() {\n"
282                         "gl_Position = MVP * vec4(vtx_position, 1);\n"
283                         "frag_color = vtx_color;\n"
284                         "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
285                         "frag_light = vtx_light;\n"
286                 "}\n"
287         );
288         program.LoadShader(
289                 GL_FRAGMENT_SHADER,
290                 "#version 330 core\n"
291                 "in vec3 frag_color;\n"
292                 "in vec3 vtx_viewspace;\n"
293                 "in float frag_light;\n"
294                 "uniform float fog_density;\n"
295                 "out vec3 color;\n"
296                 "void main() {\n"
297                         "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
298                         "float light_power = clamp(pow(0.8, 15 - frag_light), 0, 1);\n"
299                         "vec3 fog_color = vec3(0, 0, 0);\n"
300                         "float e = 2.718281828;\n"
301                         //"vec3 reflect_color = ambient + frag_color * light_power;\n"
302                         "vec3 reflect_color = frag_color * light_power;\n"
303                         "float value = pow(e, -pow(fog_density * length(vtx_viewspace), 5));"
304                         "color = mix(fog_color, reflect_color, value);\n"
305                 "}\n"
306         );
307         program.Link();
308         if (!program.Linked()) {
309                 program.Log(std::cerr);
310                 throw std::runtime_error("link program");
311         }
312
313         mv_handle = program.UniformLocation("MV");
314         mvp_handle = program.UniformLocation("MVP");
315         fog_density_handle = program.UniformLocation("fog_density");
316 }
317
318
319 void BlockLighting::Activate() noexcept {
320         GLContext::EnableDepthTest();
321         GLContext::EnableBackfaceCulling();
322         program.Use();
323 }
324
325 void BlockLighting::SetM(const glm::mat4 &m) noexcept {
326         glm::mat4 mv(view * m);
327         glm::mat4 mvp(vp * m);
328         glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
329         glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
330 }
331
332 void BlockLighting::SetFogDensity(float f) noexcept {
333         fog_density = f;
334         glUniform1f(fog_density_handle, fog_density);
335 }
336
337 void BlockLighting::SetProjection(const glm::mat4 &p) noexcept {
338         projection = p;
339         vp = p * view;
340 }
341
342 void BlockLighting::SetView(const glm::mat4 &v) noexcept {
343         view = v;
344         vp = projection * v;
345 }
346
347 void BlockLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
348         projection = p;
349         view = v;
350         vp = p * v;
351 }
352
353 void BlockLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
354         SetVP(v, p);
355         SetM(m);
356 }
357
358 }