#include "glad/glad.h" #include #include #include #include #include #include #include #include static const char* vertex_shader_text = "#version 420 core\n" "uniform mat4 MVP;\n" "uniform float time;\n" "uniform vec3 light;\n" "attribute vec3 pos;\n" "attribute vec3 normal;\n" "out vec3 vertColor;\n" "vec3 getHue() {\n" " float a = (1 + sin(time)) / 2;\n" " float b = (1 + cos(time / 1.618034)) / 2;\n" " float c = (1 + sin(time / (3.141593 / 3))) / 2;\n" " return vec3(a, b, c);\n" "}\n" "float getLuma() {\n" " vec3 n = normalize(normal);\n" " vec3 l = normalize(light);\n" " return clamp(dot(n, l), 0, 1);\n" "}\n" "void main() {\n" " gl_Position = MVP * vec4(pos, 1.0);\n" " vec3 hue = getHue() + vec3(0.2, 0.2, 0.2);\n" " vertColor = hue * 0.8f * getLuma();\n" "}\n"; static const char* fragment_shader_text = "#version 420 core\n" "in vec3 vertColor;\n" "out vec3 color;\n" "void main() {\n" " color = vertColor;\n" "}\n"; static const GLfloat cube_mesh[] = { -1.0f,-1.0f,-1.0f, /**/ -1.0f,-1.0f, 1.0f, /**/ -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, /**/ -1.0f,-1.0f,-1.0f, /**/ -1.0f, 1.0f,-1.0f, 1.0f,-1.0f, 1.0f, /**/ -1.0f,-1.0f,-1.0f, /**/ 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, /**/ 1.0f,-1.0f,-1.0f, /**/ -1.0f,-1.0f,-1.0f, -1.0f,-1.0f,-1.0f, /**/ -1.0f, 1.0f, 1.0f, /**/ -1.0f, 1.0f,-1.0f, 1.0f,-1.0f, 1.0f, /**/ -1.0f,-1.0f, 1.0f, /**/ -1.0f,-1.0f,-1.0f, -1.0f, 1.0f, 1.0f, /**/ -1.0f,-1.0f, 1.0f, /**/ 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, /**/ 1.0f,-1.0f,-1.0f, /**/ 1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, /**/ 1.0f, 1.0f, 1.0f, /**/ 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, /**/ 1.0f, 1.0f,-1.0f, /**/ -1.0f, 1.0f,-1.0f, 1.0f, 1.0f, 1.0f, /**/ -1.0f, 1.0f,-1.0f, /**/ -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, /**/ -1.0f, 1.0f, 1.0f, /**/ 1.0f,-1.0f, 1.0f }; static GLfloat cube_normals[108] = {0}; typedef struct { float max_speed; float max_acc; float disp = 0; float vel = 0; float acc = 0; } Control; Control lng, lat, zoom; void keys_handler(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GLFW_TRUE); if (action == GLFW_PRESS) { switch (key) { case GLFW_KEY_APOSTROPHE: zoom.acc = -zoom.max_acc; break; case GLFW_KEY_SLASH: zoom.acc = zoom.max_acc; break; case GLFW_KEY_UP: lat.acc = -lat.max_acc; break; case GLFW_KEY_DOWN: lat.acc = lat.max_acc; break; case GLFW_KEY_LEFT: lng.acc = -lng.max_acc; break; case GLFW_KEY_RIGHT: lng.acc = lng.max_acc; break; } } else if (action == GLFW_RELEASE) { switch (key) { case GLFW_KEY_APOSTROPHE: case GLFW_KEY_SLASH: zoom.acc = 0.0f; break; case GLFW_KEY_UP: case GLFW_KEY_DOWN: lat.acc = 0.0f; break; case GLFW_KEY_LEFT: case GLFW_KEY_RIGHT: lng.acc = 0.0f; break; } } } void tick_control(Control& c) { float vel = c.vel + c.acc; if (vel >= -c.max_speed && vel <= c.max_speed) c.vel = vel; if (c.acc > -0.0001f && c.acc < 0.0001f) { float friction = c.max_acc * 0.2f; if (c.vel > 0.001f) c.vel -= friction; else if (c.vel < -0.001f) c.vel += friction; else c.vel = 0; } c.disp += vel; } void tick_camera(glm::mat4& view) { tick_control(lng); tick_control(lat); tick_control(zoom); float *v = glm::value_ptr(view); v[15] = 1.0f; v[14] = -zoom.disp; glm::vec3 right(v[0], v[1], v[2]); glm::vec3 up(v[4], v[5], v[6]); glm::vec3 fw(v[8], v[9], v[10]); fw = glm::normalize(fw + lat.vel * up); up = glm::cross(fw, right); fw = glm::cross(up, right); fw = glm::normalize(fw + lng.vel * right); right = glm::cross(fw, up); fw = glm::cross(up, -right); v[0] = right.x; v[1] = right.y; v[2] = right.z; v[4] = up.x; v[5] = up.y; v[6] = up.z; v[8] = fw.x; v[9] = fw.y; v[10] = fw.z; view = glm::make_mat4x4(&v[0]); } int main() { lng.max_speed = 0.1f; lng.max_acc = 0.01f; lat.max_speed = 0.1f; lat.max_acc = 0.01f; zoom.max_speed = 0.2f; zoom.max_acc = 0.02f; zoom.disp = 7.0f; for (int i = 0; i < 12; i++) { GLfloat *m = (GLfloat*)&cube_mesh[i * 9]; glm::vec3 v1(m[0], m[1], m[2]); glm::vec3 a = glm::vec3(m[3], m[4], m[5]) - v1; glm::vec3 b = glm::vec3(m[6], m[7], m[8]) - v1; glm::vec3 normal = glm::normalize(glm::cross(a, b)); GLfloat *norms = &cube_normals[i * 9]; memcpy(norms, &normal[0], 3 * sizeof(float)); memcpy(norms + 3, &normal[0], 3 * sizeof(float)); memcpy(norms + 6, &normal[0], 3 * sizeof(float)); } GLFWwindow* window; if (!glfwInit()) return 1; glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL); if (!window) { glfwTerminate(); return 2; } glfwSetKeyCallback(window, keys_handler); glfwMakeContextCurrent(window); if (!gladLoadGL()) { glfwDestroyWindow(window); glfwTerminate(); return 3; } // Enable v-sync by specifying that a buffer swap happens after 1 screen update glfwSwapInterval(1); glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); glClearColor(0.15f, 0.0f, 0.3f, 0.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); GLuint VertexArrayID; glGenVertexArrays(2, &VertexArrayID); glBindVertexArray(VertexArrayID); GLuint cube_mesh_id; glGenBuffers(1, &cube_mesh_id); glBindBuffer(GL_ARRAY_BUFFER, cube_mesh_id); glBufferData(GL_ARRAY_BUFFER, sizeof(cube_mesh), &cube_mesh[0], GL_STATIC_DRAW); GLuint cube_normals_id; glGenBuffers(1, &cube_normals_id); glBindBuffer(GL_ARRAY_BUFFER, cube_normals_id); glBufferData(GL_ARRAY_BUFFER, sizeof(cube_normals), &cube_normals[0], GL_STATIC_DRAW); GLuint frag_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(frag_shader, 1, &fragment_shader_text, NULL); glCompileShader(frag_shader); GLuint vert_shader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vert_shader, 1, &vertex_shader_text, NULL); glCompileShader(vert_shader); GLuint program = glCreateProgram(); glAttachShader(program, frag_shader); glAttachShader(program, vert_shader); glLinkProgram(program); GLint mvp_loc = glGetUniformLocation(program, "MVP"); GLint time_loc = glGetUniformLocation(program, "time"); GLint light_loc = glGetUniformLocation(program, "light"); GLint pos_loc = glGetAttribLocation(program, "pos"); GLint normal_loc = glGetAttribLocation(program, "normal"); glm::mat4 view(1.0f); while (!glfwWindowShouldClose(window)) { int width, height; glfwGetFramebufferSize(window, &width, &height); float ratio = (float)width / (float)height; glViewport(0, 0, width, height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glm::mat4 projection = glm::perspective(glm::radians(45.0f), ratio, 0.1f, 100.0f); tick_camera(view); glm::mat4 mvp = projection * view; glUseProgram(program); glUniformMatrix4fv(mvp_loc, 1, GL_FALSE, (const GLfloat*)&mvp[0]); glUniform1f(time_loc, glfwGetTime()); GLfloat *v = (GLfloat*)glm::value_ptr(view); glUniform3f(light_loc, v[2], v[6], v[10]); glEnableVertexAttribArray(pos_loc); glBindBuffer(GL_ARRAY_BUFFER, cube_mesh_id); glVertexAttribPointer(pos_loc, 3, GL_FLOAT, 0, 0, NULL); glEnableVertexAttribArray(normal_loc); glBindBuffer(GL_ARRAY_BUFFER, cube_normals_id); glVertexAttribPointer(normal_loc, 3, GL_FLOAT, 0, 0, NULL); glDrawArrays(GL_TRIANGLES, 0, 12*3); glDisableVertexAttribArray(pos_loc); /* Swap front and back buffers */ glfwSwapBuffers(window); /* Poll for and process events */ glfwPollEvents(); } glDeleteBuffers(1, &cube_mesh_id); glDeleteBuffers(1, &cube_normals_id); glDeleteProgram(program); glDeleteVertexArrays(1, &VertexArrayID); glfwDestroyWindow(window); glfwTerminate(); return 0; }