#include #include #include #define GLM_ENABLE_EXPERIMENTAL #include #include #include #include #include #include #include #include #include #include #include struct model { std::vector vertices; std::vector indices; void draw() { if (VAO == 0) initVAO(); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); glBindVertexArray(0); } private: void initVAO() { GLuint VBO; glGenBuffers(1, &VBO); GLuint EBO; glGenBuffers(1, &EBO); glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertices.size(), vertices.data(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), indices.data(), GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(0)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, (void*)(sizeof(float) * 3)); glBindVertexArray(0); } GLuint VAO = 0; }; struct freecam { glm::vec3 pos = glm::vec3(0, 0, -1); glm::vec2 rot = glm::vec2(0, 0); void update(sf::Window &window) { int mouseDeltaX = sf::Mouse::getPosition(window).x - window.getSize().x / 2; int mouseDeltaY = sf::Mouse::getPosition(window).y - window.getSize().y / 2; rot.x += mouseDeltaX; rot.y += mouseDeltaY; forward = glm::rotate(glm::vec3(0, 0, 1), rot.y / angleFactor, glm::vec3(1, 0, 0)); forward = glm::rotate(forward, -rot.x / angleFactor, glm::vec3(0, 1, 0)); glm::vec3 left = glm::rotate(glm::vec3(0, 0, 1), -rot.x / angleFactor + glm::radians(90.0f), glm::vec3(0, 1, 0)); if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::LShift)) moveFactor = 200; else moveFactor = 20; if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) pos += forward / moveFactor; if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) pos -= forward / moveFactor; if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) pos += left / moveFactor; if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) pos -= left / moveFactor; limit(); } void limit() { rot.x = fmod(rot.x, glm::radians(360.0f) * angleFactor); rot.y = fmod(rot.y, glm::radians(360.0f) * angleFactor); } glm::mat4 getViewMatrix() { forward = glm::rotate(glm::vec3(0, 0, 1), rot.y / angleFactor, glm::vec3(1, 0, 0)); forward = glm::rotate(forward, -rot.x / angleFactor, glm::vec3(0, 1, 0)); glm::mat4 result = glm::lookAt(pos, pos + forward, up); return result; } private: glm::vec3 forward = glm::vec3(0, 0, 1); glm::vec3 up = glm::vec3(0, 1, 0); const float angleFactor = 200; float moveFactor = 20; }; struct arccam { glm::vec2 rot = glm::vec2(0, 0); float radius = 1; void update(sf::Window &window) { int mouseDeltaX = sf::Mouse::getPosition(window).x - window.getSize().x / 2; int mouseDeltaY = sf::Mouse::getPosition(window).y - window.getSize().y / 2; rot.x += mouseDeltaX; rot.y += mouseDeltaY; limit(-89, 89); } void limit(float minY, float maxY) { float angleX = rot.x / angleFactor; float angleY = rot.y / angleFactor; rot.x = fmod(rot.x, glm::radians(360.0f) * angleFactor); if (angleY > glm::radians(maxY)) rot.y = glm::radians(maxY) * angleFactor; if (angleY < glm::radians(minY)) rot.y = glm::radians(minY) * angleFactor; } glm::vec3 getPos() { float angle = rot.y / angleFactor; float camY = sin(angle) * exp(radius); float camZ = cos(angle) * exp(radius); glm::vec3 result(0.0, camY, camZ); return glm::rotate(result, -rot.x / angleFactor, glm::vec3(0, 1, 0)); } glm::mat4 getViewMatrix() { float angle = rot.y / angleFactor; float camY = sin(angle) * exp(radius); float camZ = cos(angle) * exp(radius); glm::mat4 result = glm::lookAt(glm::vec3(0.0, camY, camZ), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)); result = glm::rotate(result, rot.x / angleFactor, glm::vec3(0, 1, 0)); return result; } private: const float angleFactor = 200; }; std::string readFile(std::string filename) { std::ifstream ifs(filename, std::ios::binary); std::string result, line; while (std::getline(ifs, line)) result += line + "\n"; return result; } model loadModel(const std::string &filename) { Assimp::Importer importer; const aiScene *scene = importer.ReadFile( filename, aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_SortByPType | aiProcess_GenSmoothNormals); model result; for (int i = 0; i < scene->mMeshes[0]->mNumVertices; i++) { aiVector3D v = scene->mMeshes[0]->mVertices[i]; aiVector3D n = scene->mMeshes[0]->mNormals[i]; result.vertices.push_back(v.x); result.vertices.push_back(v.y); result.vertices.push_back(v.z); result.vertices.push_back(n.x); result.vertices.push_back(n.y); result.vertices.push_back(n.z); } for (int i = 0; i < scene->mMeshes[0]->mNumFaces; i++) { aiFace f = scene->mMeshes[0]->mFaces[i]; for (int j = 0; j < f.mNumIndices; j++) { result.indices.push_back(f.mIndices[j]); } } return result; } GLuint compileShaders(const char *vertFilename, const char *fragFilename) { GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); std::string vertSource = readFile(vertFilename); const char *vertAddr = vertSource.c_str(); glShaderSource(vertShader, 1, &vertAddr, NULL); glCompileShader(vertShader); int success; char infoLog[512]; glGetShaderiv(vertShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(vertShader, 512, NULL, infoLog); printf("Error compiling vertex shader(%s): %s\n", vertFilename, infoLog); } GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); std::string fragSource = readFile(fragFilename); const char *fragAddr = fragSource.c_str(); glShaderSource(fragShader, 1, &fragAddr, NULL); glCompileShader(fragShader); glGetShaderiv(fragShader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragShader, 512, NULL, infoLog); printf("Error compiling fragment shader(%s): %s\n", fragFilename, infoLog); } // Link Shader Program GLuint shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertShader); glAttachShader(shaderProgram, fragShader); glLinkProgram(shaderProgram); glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog); printf("Error linking shader program: %s\n", infoLog); } glDeleteShader(vertShader); glDeleteShader(fragShader); return shaderProgram; } int main() { // Window Setup const int width = 1600, height = 900; sf::ContextSettings settings; settings.depthBits = 24; settings.antialiasingLevel = 0; settings.majorVersion = 4; settings.minorVersion = 6; sf::RenderWindow window(sf::VideoMode(1600, 900), "Subsurface Scattering", sf::Style::Default, settings); window.setVerticalSyncEnabled(true); ImGui::SFML::Init(window); // Initialize GLEW if (glewInit() != GLEW_OK) { } GLuint shaderProgram = compileShaders("shaders/vert.glsl", "shaders/frag.glsl"); model m = loadModel("models/Isotrop-upperjaw.ply"); arccam arcCam; freecam freeCam; glm::vec3 lightPos(1.2f, 5.0f, 2.0f); // MVP glm::mat4 model = glm::scale(glm::mat4(1.0f), glm::vec3(0.01f, 0.01f, 0.01f)); glm::mat4 view; glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)window.getSize().x / window.getSize().y, 0.001f, 1000.0f); // Framebuffer GLuint fbo; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); GLuint renderTexture; glGenTextures(1, &renderTexture); glBindTexture(GL_TEXTURE_2D, renderTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //glBindTexture(GL_TEXTURE_2D, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTexture, 0); GLuint rbo; glGenRenderbuffers(1, &rbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); //glBindRenderbuffer(GL_RENDERBUFFER, 0); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { printf("Successfully created framebuffer\n"); } glBindFramebuffer(GL_FRAMEBUFFER, 0); GLuint screenShaderProgram = compileShaders("shaders/fbo_vert.glsl", "shaders/fbo_frag.glsl"); glUseProgram(screenShaderProgram); glUniform1i(glGetUniformLocation(screenShaderProgram, "screenTexture"), 0); // Screen VAO GLuint screenVBO; glGenBuffers(1, &screenVBO); GLuint screenVAO; glGenVertexArrays(1, &screenVAO); glBindVertexArray(screenVAO); float screenVerts[] = { -1.0f, +1.0f, +0.0f, +1.0f, -1.0f, -1.0f, +0.0f, +0.0f, +1.0f, -1.0f, +1.0f, +0.0f, -1.0f, +1.0f, +0.0f, +1.0f, +1.0f, -1.0f, +1.0f, +0.0f, +1.0f, +1.0f, +1.0f, +1.0f, }; glBindBuffer(GL_ARRAY_BUFFER, screenVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, screenVerts, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(0)); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2)); glBindVertexArray(0); // Config struct { bool wireframe = false; bool freecam = false; } options; sf::Clock deltaClock; bool prevMouse = false; bool running = true; while (running) { // Events sf::Event event; while (window.pollEvent(event)) { ImGui::SFML::ProcessEvent(event); if (event.type == sf::Event::EventType::Closed) { running = false; } else if (event.type == sf::Event::EventType::Resized) { glViewport(0, 0, event.size.width, event.size.height); } else if (event.type == sf::Event::EventType::KeyReleased) { using keys = sf::Keyboard; switch (event.key.code) { case keys::Escape: running = false; break; } } else if (event.type == sf::Event::EventType::MouseWheelScrolled) { if (! options.freecam) { arcCam.radius -= event.mouseWheelScroll.delta / 5.0f; } } } // Update if (sf::Mouse::isButtonPressed(sf::Mouse::Right)) { window.setMouseCursorVisible(false); if (prevMouse) { if (options.freecam) freeCam.update(window); else arcCam.update(window); } sf::Mouse::setPosition(sf::Vector2i( window.getSize().x / 2, window.getSize().y / 2 ), window); } else { window.setMouseCursorVisible(true); } prevMouse = sf::Mouse::isButtonPressed(sf::Mouse::Right); // Render to fbo glBindFramebuffer(GL_FRAMEBUFFER, fbo); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); if (options.wireframe) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); else glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glUseProgram(shaderProgram); if (options.freecam) view = freeCam.getViewMatrix(); else view = arcCam.getViewMatrix(); glUniformMatrix4fv( glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, glm::value_ptr(model)); glUniformMatrix4fv( glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv( glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, glm::value_ptr(proj)); glUniform3f( glGetUniformLocation(shaderProgram, "objectColor"), 1.0f, 0.5f, 0.31f); glUniform3f( glGetUniformLocation(shaderProgram, "lightColor"), 1.0f, 1.0f, 1.0f); glUniform3fv( glGetUniformLocation(shaderProgram, "lightPos"), 1, glm::value_ptr(lightPos)); glUniform3fv( glGetUniformLocation(shaderProgram, "viewPos"), 1, glm::value_ptr(options.freecam ? freeCam.pos : arcCam.getPos())); m.draw(); // Render fbo to screen glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDisable(GL_DEPTH_TEST); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(screenShaderProgram); glBindVertexArray(screenVAO); glBindTexture(GL_TEXTURE_2D, renderTexture); glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); ImGui::SFML::Update(window, deltaClock.restart()); ImGui::Begin("Options"); ImGui::Checkbox("Wireframe", &options.wireframe); ImGui::Checkbox("Free Cam", &options.freecam); if (options.freecam) { ImGui::LabelText("Position", "%f %f %f", freeCam.pos.x, freeCam.pos.y, freeCam.pos.z); ImGui::LabelText("Rotation", "%f %f", freeCam.rot.x, freeCam.rot.y); if (ImGui::Button("Reset")) { freeCam.pos = glm::vec3(0, 0, -1); freeCam.rot = glm::vec2(0); } } else { ImGui::LabelText("Rotation", "%f %f", arcCam.rot.x, arcCam.rot.y); ImGui::DragFloat("Radius", &arcCam.radius, 0.01f, -1.0f, 1.0f); if (ImGui::Button("Reset")) { arcCam.rot = glm::vec2(0); arcCam.radius = 1; } } ImGui::End(); ImGui::SFML::Render(window); window.display(); } glDeleteFramebuffers(1, &fbo); return 0; }