]> gitweb.ps.run Git - subsurface_scattering/blob - src/main.cpp
render to framebufer
[subsurface_scattering] / src / main.cpp
1 #include <stdio.h>\r
2 #include <fstream>\r
3 \r
4 #include <GL/glew.h>\r
5 \r
6 #define GLM_ENABLE_EXPERIMENTAL\r
7 \r
8 #include <glm/glm.hpp>\r
9 #include <glm/gtx/rotate_vector.hpp>\r
10 #include <glm/gtc/matrix_transform.hpp>\r
11 #include <glm/gtc/type_ptr.hpp>\r
12 \r
13 #include <SFML/OpenGL.hpp>\r
14 #include <SFML/Graphics.hpp>\r
15 \r
16 #include <imgui.h>\r
17 #include <imgui-SFML.h>\r
18 \r
19 #include <assimp/Importer.hpp>\r
20 #include <assimp/postprocess.h>\r
21 #include <assimp/scene.h>\r
22 \r
23 struct model {\r
24   std::vector<float> vertices;\r
25   std::vector<GLuint> indices;\r
26 \r
27   void draw() {\r
28     if (VAO == 0) initVAO();\r
29     glBindVertexArray(VAO);\r
30     glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);\r
31     glBindVertexArray(0);\r
32   }\r
33 \r
34 private:\r
35   void initVAO() {\r
36     GLuint VBO;\r
37     glGenBuffers(1, &VBO);\r
38 \r
39     GLuint EBO;\r
40     glGenBuffers(1, &EBO);\r
41 \r
42     glGenVertexArrays(1, &VAO);\r
43 \r
44     glBindVertexArray(VAO);\r
45 \r
46     glBindBuffer(GL_ARRAY_BUFFER, VBO);\r
47     glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(),\r
48                 vertices.data(), GL_STATIC_DRAW);\r
49 \r
50     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);\r
51     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(),\r
52                 indices.data(), GL_STATIC_DRAW);\r
53 \r
54     glEnableVertexAttribArray(0);\r
55     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(0));\r
56     glEnableVertexAttribArray(1);\r
57     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(sizeof(float) * 3));\r
58 \r
59     glBindVertexArray(0);\r
60   }\r
61   GLuint VAO = 0;\r
62 };\r
63 \r
64 \r
65 struct freecam {\r
66   glm::vec3 pos = glm::vec3(0, 0, -1);\r
67   glm::vec2 rot = glm::vec2(0, 0);\r
68 \r
69   void update(sf::Window &window) {\r
70     int mouseDeltaX = sf::Mouse::getPosition(window).x - window.getSize().x / 2;\r
71     int mouseDeltaY = sf::Mouse::getPosition(window).y - window.getSize().y / 2;\r
72 \r
73     rot.x += mouseDeltaX;\r
74     rot.y += mouseDeltaY;\r
75 \r
76     forward = glm::rotate(glm::vec3(0, 0, 1), rot.y / angleFactor, glm::vec3(1, 0, 0));\r
77     forward = glm::rotate(forward, -rot.x / angleFactor, glm::vec3(0, 1, 0));\r
78 \r
79     glm::vec3 left = glm::rotate(glm::vec3(0, 0, 1), -rot.x / angleFactor + glm::radians(90.0f), glm::vec3(0, 1, 0));\r
80 \r
81     if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LShift))\r
82       moveFactor = 200;\r
83     else\r
84       moveFactor = 20;\r
85 \r
86     if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))\r
87       pos += forward / moveFactor;\r
88     if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))\r
89       pos -= forward / moveFactor;\r
90     if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))\r
91       pos += left / moveFactor;\r
92     if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))\r
93       pos -= left / moveFactor;\r
94 \r
95     limit();\r
96   }\r
97 \r
98   void limit() {\r
99     rot.x = fmod(rot.x, glm::radians(360.0f) * angleFactor);\r
100     rot.y = fmod(rot.y, glm::radians(360.0f) * angleFactor);\r
101   }\r
102 \r
103   glm::mat4 getViewMatrix() {\r
104     forward = glm::rotate(glm::vec3(0, 0, 1), rot.y / angleFactor, glm::vec3(1, 0, 0));\r
105     forward = glm::rotate(forward, -rot.x / angleFactor, glm::vec3(0, 1, 0));\r
106     glm::mat4 result = glm::lookAt(pos, pos + forward, up);\r
107     return result;\r
108   }\r
109 \r
110 private:\r
111   glm::vec3 forward = glm::vec3(0, 0, 1);\r
112   glm::vec3 up = glm::vec3(0, 1, 0);\r
113 \r
114   const float angleFactor = 200;\r
115   float moveFactor = 20;\r
116 };\r
117 \r
118 \r
119 struct arccam {\r
120   glm::vec2 rot = glm::vec2(0, 0);\r
121   float radius = 1;\r
122 \r
123   void update(sf::Window &window) {\r
124     int mouseDeltaX = sf::Mouse::getPosition(window).x - window.getSize().x / 2;\r
125     int mouseDeltaY = sf::Mouse::getPosition(window).y - window.getSize().y / 2;\r
126 \r
127     rot.x += mouseDeltaX;\r
128     rot.y += mouseDeltaY;\r
129 \r
130     limit(-89, 89);\r
131   }\r
132 \r
133   void limit(float minY, float maxY) {\r
134     float angleX = rot.x / angleFactor;\r
135     float angleY = rot.y / angleFactor;\r
136 \r
137     rot.x = fmod(rot.x, glm::radians(360.0f) * angleFactor);\r
138 \r
139     if (angleY > glm::radians(maxY))\r
140       rot.y = glm::radians(maxY) * angleFactor;\r
141     if (angleY < glm::radians(minY))\r
142       rot.y = glm::radians(minY) * angleFactor;\r
143   }\r
144 \r
145   glm::vec3 getPos() {\r
146     float angle = rot.y / angleFactor;\r
147   \r
148     float camY = sin(angle) * exp(radius);\r
149     float camZ = cos(angle) * exp(radius);\r
150 \r
151     glm::vec3 result(0.0, camY, camZ);\r
152     return glm::rotate(result, -rot.x / angleFactor, glm::vec3(0, 1, 0));\r
153   }\r
154 \r
155   glm::mat4 getViewMatrix() {\r
156     float angle = rot.y / angleFactor;\r
157   \r
158     float camY = sin(angle) * exp(radius);\r
159     float camZ = cos(angle) * exp(radius);\r
160     glm::mat4 result = glm::lookAt(glm::vec3(0.0, camY, camZ), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));\r
161     result = glm::rotate(result, rot.x / angleFactor, glm::vec3(0, 1, 0));\r
162 \r
163     return result;\r
164   }\r
165 \r
166 private:\r
167   const float angleFactor = 200;\r
168 };\r
169 \r
170 \r
171 std::string readFile(std::string filename) {\r
172   std::ifstream ifs(filename, std::ios::binary);\r
173   std::string result, line;\r
174   while (std::getline(ifs, line))\r
175     result += line + "\n";\r
176 \r
177   return result;\r
178 }\r
179 \r
180 model loadModel(const std::string &filename) {\r
181   Assimp::Importer importer;\r
182 \r
183   const aiScene *scene = importer.ReadFile(\r
184       filename, aiProcess_CalcTangentSpace | aiProcess_Triangulate |\r
185                     aiProcess_SortByPType | aiProcess_GenSmoothNormals);\r
186 \r
187   model result;\r
188 \r
189   for (int i = 0; i < scene->mMeshes[0]->mNumVertices; i++) {\r
190     aiVector3D v = scene->mMeshes[0]->mVertices[i];\r
191     aiVector3D n = scene->mMeshes[0]->mNormals[i];\r
192     result.vertices.push_back(v.x);\r
193     result.vertices.push_back(v.y);\r
194     result.vertices.push_back(v.z);\r
195     result.vertices.push_back(n.x);\r
196     result.vertices.push_back(n.y);\r
197     result.vertices.push_back(n.z);\r
198   }\r
199 \r
200   for (int i = 0; i < scene->mMeshes[0]->mNumFaces; i++) {\r
201     aiFace f = scene->mMeshes[0]->mFaces[i];\r
202     for (int j = 0; j < f.mNumIndices; j++) {\r
203       result.indices.push_back(f.mIndices[j]);\r
204     }\r
205   }\r
206 \r
207   return result;\r
208 }\r
209 \r
210 GLuint compileShaders(const char *vertFilename, const char *fragFilename) {\r
211   GLuint vertShader = glCreateShader(GL_VERTEX_SHADER);\r
212   std::string vertSource = readFile(vertFilename);\r
213   const char *vertAddr = vertSource.c_str();\r
214   glShaderSource(vertShader, 1, &vertAddr, NULL);\r
215   glCompileShader(vertShader);\r
216 \r
217   int success;\r
218   char infoLog[512];\r
219   glGetShaderiv(vertShader, GL_COMPILE_STATUS, &success);\r
220   if (!success) {\r
221     glGetShaderInfoLog(vertShader, 512, NULL, infoLog);\r
222     printf("Error compiling vertex shader(%s): %s\n", vertFilename, infoLog);\r
223   }\r
224 \r
225   GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);\r
226   std::string fragSource = readFile(fragFilename);\r
227   const char *fragAddr = fragSource.c_str();\r
228   glShaderSource(fragShader, 1, &fragAddr, NULL);\r
229   glCompileShader(fragShader);\r
230 \r
231   glGetShaderiv(fragShader, GL_COMPILE_STATUS, &success);\r
232   if (!success) {\r
233     glGetShaderInfoLog(fragShader, 512, NULL, infoLog);\r
234     printf("Error compiling fragment shader(%s): %s\n", fragFilename, infoLog);\r
235   }\r
236 \r
237   // Link Shader Program\r
238 \r
239   GLuint shaderProgram = glCreateProgram();\r
240   glAttachShader(shaderProgram, vertShader);\r
241   glAttachShader(shaderProgram, fragShader);\r
242   glLinkProgram(shaderProgram);\r
243 \r
244   glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);\r
245   if (!success) {\r
246     glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);\r
247     printf("Error linking shader program: %s\n", infoLog);\r
248   }\r
249 \r
250   glDeleteShader(vertShader);\r
251   glDeleteShader(fragShader);\r
252 \r
253   return shaderProgram;\r
254 }\r
255 \r
256 \r
257 int main() {\r
258   // Window Setup\r
259 \r
260   const int width = 1600, height = 900;\r
261 \r
262   sf::ContextSettings settings;\r
263   settings.depthBits = 24;\r
264   settings.antialiasingLevel = 0;\r
265   settings.majorVersion = 4;\r
266   settings.minorVersion = 6;\r
267 \r
268   sf::RenderWindow window(sf::VideoMode(1600, 900), "Subsurface Scattering",\r
269                     sf::Style::Default, settings);\r
270   window.setVerticalSyncEnabled(true);\r
271 \r
272   ImGui::SFML::Init(window);\r
273 \r
274   // Initialize GLEW\r
275 \r
276   if (glewInit() != GLEW_OK) {\r
277   }\r
278 \r
279   GLuint shaderProgram = compileShaders("shaders/vert.glsl", "shaders/frag.glsl");\r
280 \r
281   model m = loadModel("models/Isotrop-upperjaw.ply");\r
282 \r
283   arccam arcCam;\r
284   freecam freeCam;\r
285       \r
286   glm::vec3 lightPos(1.2f, 5.0f, 2.0f);\r
287 \r
288   // MVP\r
289 \r
290   glm::mat4 model = glm::scale(glm::mat4(1.0f), glm::vec3(0.01f, 0.01f, 0.01f));\r
291 \r
292   glm::mat4 view;\r
293 \r
294   glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)window.getSize().x / window.getSize().y, 0.001f, 1000.0f);\r
295 \r
296   // Framebuffer\r
297   GLuint fbo;\r
298   glGenFramebuffers(1, &fbo);\r
299   glBindFramebuffer(GL_FRAMEBUFFER, fbo);\r
300   \r
301   GLuint renderTexture;\r
302   glGenTextures(1, &renderTexture);\r
303   glBindTexture(GL_TEXTURE_2D, renderTexture);\r
304   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);\r
305   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\r
306   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r
307   //glBindTexture(GL_TEXTURE_2D, 0);\r
308   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTexture, 0);\r
309 \r
310   GLuint rbo;\r
311   glGenRenderbuffers(1, &rbo);\r
312   glBindRenderbuffer(GL_RENDERBUFFER, rbo);\r
313   glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);\r
314   //glBindRenderbuffer(GL_RENDERBUFFER, 0);\r
315   glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);\r
316 \r
317   if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) {\r
318     printf("Successfully created framebuffer\n");\r
319   }\r
320   glBindFramebuffer(GL_FRAMEBUFFER, 0);\r
321   \r
322   GLuint screenShaderProgram = compileShaders("shaders/fbo_vert.glsl", "shaders/fbo_frag.glsl");\r
323   glUseProgram(screenShaderProgram);\r
324   glUniform1i(glGetUniformLocation(screenShaderProgram, "screenTexture"), 0);\r
325 \r
326   // Screen VAO\r
327   \r
328   GLuint screenVBO;\r
329   glGenBuffers(1, &screenVBO);\r
330 \r
331   GLuint screenVAO;\r
332   glGenVertexArrays(1, &screenVAO);\r
333 \r
334   glBindVertexArray(screenVAO);\r
335 \r
336   float screenVerts[] = {\r
337     -1.0f, +1.0f, +0.0f, +1.0f,\r
338     -1.0f, -1.0f, +0.0f, +0.0f,\r
339     +1.0f, -1.0f, +1.0f, +0.0f,\r
340  \r
341     -1.0f, +1.0f, +0.0f, +1.0f,\r
342     +1.0f, -1.0f, +1.0f, +0.0f,\r
343     +1.0f, +1.0f, +1.0f, +1.0f,\r
344   };\r
345 \r
346   glBindBuffer(GL_ARRAY_BUFFER, screenVBO);\r
347   glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4,\r
348               screenVerts, GL_STATIC_DRAW);\r
349 \r
350   glEnableVertexAttribArray(0);\r
351   glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(0));\r
352   glEnableVertexAttribArray(1);\r
353   glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2));\r
354 \r
355   glBindVertexArray(0);\r
356 \r
357   // Config\r
358 \r
359   struct {\r
360     bool wireframe = false;\r
361     bool freecam = false;\r
362   } options;\r
363 \r
364   sf::Clock deltaClock;\r
365 \r
366   bool prevMouse = false;\r
367 \r
368   bool running = true;\r
369   while (running) {\r
370     // Events\r
371 \r
372     sf::Event event;\r
373     while (window.pollEvent(event)) {\r
374       ImGui::SFML::ProcessEvent(event);\r
375 \r
376       if (event.type == sf::Event::EventType::Closed) {\r
377         running = false;\r
378       } else if (event.type == sf::Event::EventType::Resized) {\r
379         glViewport(0, 0, event.size.width, event.size.height);\r
380       } else if (event.type == sf::Event::EventType::KeyReleased) {\r
381         using keys = sf::Keyboard;\r
382         switch (event.key.code) {\r
383         case keys::Escape:\r
384           running = false;\r
385           break;\r
386         }\r
387       } else if (event.type == sf::Event::EventType::MouseWheelScrolled) {\r
388         if (! options.freecam) {\r
389           arcCam.radius -= event.mouseWheelScroll.delta / 5.0f;\r
390         }\r
391       }\r
392     }\r
393 \r
394     // Update\r
395 \r
396     if (sf::Mouse::isButtonPressed(sf::Mouse::Right)) {\r
397       window.setMouseCursorVisible(false);\r
398 \r
399       if (prevMouse) {\r
400         if (options.freecam)\r
401           freeCam.update(window);\r
402         else\r
403           arcCam.update(window);\r
404       }\r
405 \r
406 \r
407       sf::Mouse::setPosition(sf::Vector2i(\r
408           window.getSize().x / 2,\r
409           window.getSize().y / 2\r
410         ), window);  \r
411     } else {\r
412       window.setMouseCursorVisible(true);\r
413     }\r
414 \r
415     prevMouse = sf::Mouse::isButtonPressed(sf::Mouse::Right);\r
416 \r
417     // Render to fbo\r
418 \r
419     glBindFramebuffer(GL_FRAMEBUFFER, fbo);\r
420     glClearColor(0.1f, 0.1f, 0.1f, 1.0f);\r
421     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);\r
422     glEnable(GL_DEPTH_TEST);\r
423 \r
424     if (options.wireframe)\r
425       glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);\r
426     else\r
427       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\r
428 \r
429     glUseProgram(shaderProgram);\r
430     \r
431     if (options.freecam)\r
432       view = freeCam.getViewMatrix();\r
433     else\r
434       view = arcCam.getViewMatrix();\r
435     \r
436     glUniformMatrix4fv(\r
437       glGetUniformLocation(shaderProgram, "model"),\r
438       1, GL_FALSE, glm::value_ptr(model));\r
439     glUniformMatrix4fv(\r
440       glGetUniformLocation(shaderProgram, "view"),\r
441       1, GL_FALSE, glm::value_ptr(view));\r
442     glUniformMatrix4fv(\r
443       glGetUniformLocation(shaderProgram, "projection"),\r
444       1, GL_FALSE, glm::value_ptr(proj));\r
445 \r
446     glUniform3f(\r
447       glGetUniformLocation(shaderProgram, "objectColor"),\r
448       1.0f, 0.5f, 0.31f);\r
449     glUniform3f(\r
450       glGetUniformLocation(shaderProgram, "lightColor"),\r
451       1.0f, 1.0f, 1.0f);\r
452     glUniform3fv(\r
453       glGetUniformLocation(shaderProgram, "lightPos"),\r
454       1, glm::value_ptr(lightPos));\r
455     glUniform3fv(\r
456       glGetUniformLocation(shaderProgram, "viewPos"),\r
457       1, glm::value_ptr(options.freecam ? freeCam.pos : arcCam.getPos()));\r
458 \r
459     m.draw();\r
460 \r
461     // Render fbo to screen\r
462     \r
463     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);\r
464 \r
465     glBindFramebuffer(GL_FRAMEBUFFER, 0);\r
466     glDisable(GL_DEPTH_TEST);\r
467     glClearColor(1.0f, 1.0f, 1.0f, 1.0f);\r
468     glClear(GL_COLOR_BUFFER_BIT);\r
469     glUseProgram(screenShaderProgram);\r
470 \r
471     glBindVertexArray(screenVAO);\r
472     glBindTexture(GL_TEXTURE_2D, renderTexture);\r
473     glDrawArrays(GL_TRIANGLES, 0, 6);\r
474     glBindVertexArray(0);\r
475 \r
476     ImGui::SFML::Update(window, deltaClock.restart());\r
477 \r
478     ImGui::Begin("Options");\r
479     ImGui::Checkbox("Wireframe", &options.wireframe);\r
480     ImGui::Checkbox("Free Cam", &options.freecam);\r
481     if (options.freecam) {\r
482       ImGui::LabelText("Position", "%f %f %f", freeCam.pos.x, freeCam.pos.y, freeCam.pos.z);\r
483       ImGui::LabelText("Rotation", "%f %f", freeCam.rot.x, freeCam.rot.y);\r
484       if (ImGui::Button("Reset")) {\r
485         freeCam.pos = glm::vec3(0, 0, -1);\r
486         freeCam.rot = glm::vec2(0);\r
487       }\r
488     } else {\r
489       ImGui::LabelText("Rotation", "%f %f", arcCam.rot.x, arcCam.rot.y);\r
490       ImGui::DragFloat("Radius", &arcCam.radius, 0.01f, -1.0f, 1.0f);\r
491       if (ImGui::Button("Reset")) {\r
492         arcCam.rot = glm::vec2(0);\r
493         arcCam.radius = 1;\r
494       }\r
495     }\r
496     ImGui::End();\r
497 \r
498     ImGui::SFML::Render(window);\r
499 \r
500     window.display();\r
501   }\r
502 \r
503   glDeleteFramebuffers(1, &fbo);\r
504 \r
505   return 0;\r
506 }