From 8ce253c0dcee933109f0ecf227266827cb2b225c Mon Sep 17 00:00:00 2001 From: hippoz <10706925-hippoz@users.noreply.gitlab.com> Date: Sat, 2 Sep 2023 02:39:46 +0300 Subject: [PATCH] improve lighting and use UBOs --- meson.build | 5 ++- shaders/light_cube_vertex.glsl | 7 +++- shaders/lighting_fragment.glsl | 12 +++--- shaders/lighting_vertex.glsl | 7 +++- src/camera.cpp | 4 +- src/defer.hpp | 8 ++-- src/main.cpp | 63 ++++++++++++++++++++++------- src/mesh.cpp | 47 +++++++++++---------- src/mesh.hpp | 8 +++- src/texture.cpp | 74 ++++++++++++++++++++++++++++------ 10 files changed, 166 insertions(+), 69 deletions(-) diff --git a/meson.build b/meson.build index 7986d93..6914ef5 100644 --- a/meson.build +++ b/meson.build @@ -8,6 +8,8 @@ glfw3_dep = dependency('glfw3') assimp_dep = dependency('assimp') incdir = include_directories(['src', 'src/vendor']) +cpp_args = ['-pedantic', '-Wall', '-Wextra'] + executable( 'game', './src/common.cpp', @@ -20,5 +22,6 @@ executable( './src/main.cpp', './src/vendor/glad/glad.c', include_directories : incdir, - dependencies : [glfw3_dep, assimp_dep] + dependencies : [glfw3_dep, assimp_dep], + cpp_args : cpp_args ) diff --git a/shaders/light_cube_vertex.glsl b/shaders/light_cube_vertex.glsl index ae6d2c9..0bddd82 100644 --- a/shaders/light_cube_vertex.glsl +++ b/shaders/light_cube_vertex.glsl @@ -1,9 +1,12 @@ #version 330 core layout (location = 0) in vec3 aPos; +layout(std140, binding = 0) uniform Matrices +{ + mat4 projection; + mat4 view; +}; uniform mat4 model; -uniform mat4 view; -uniform mat4 projection; void main() { diff --git a/shaders/lighting_fragment.glsl b/shaders/lighting_fragment.glsl index e6a7b21..c061db2 100644 --- a/shaders/lighting_fragment.glsl +++ b/shaders/lighting_fragment.glsl @@ -59,10 +59,10 @@ uniform PointLight pointLights[POINT_LIGHTS_COUNT]; vec3 ComputeDirectionalLight(DirectionalLight light, vec3 norm, vec3 viewDir) { vec3 lightDir = normalize(-light.direction); - vec3 reflectDir = reflect(-lightDir, norm); + vec3 halfwayDir = normalize(lightDir + viewDir); float diff = max(dot(norm, lightDir), 0.0); - float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + float spec = pow(max(dot(norm, halfwayDir), 0.0), material.shininess); vec3 ambient = light.ambient * vec3(texture(material.texture_diffuse1, TexCoords)); vec3 diffuse = light.diffuse * diff * vec3(texture(material.texture_diffuse1, TexCoords)); @@ -77,10 +77,10 @@ vec3 ComputePointLight(PointLight light, vec3 norm, vec3 viewDir, vec3 fragPos) float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); vec3 lightDir = normalize(light.position - fragPos); - vec3 reflectDir = reflect(-lightDir, norm); + vec3 halfwayDir = normalize(lightDir + viewDir); float diff = max(dot(norm, lightDir), 0.0); - float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + float spec = pow(max(dot(norm, halfwayDir), 0.0), material.shininess); vec3 ambient = light.ambient * vec3(texture(material.texture_diffuse1, TexCoords)); vec3 diffuse = light.diffuse * diff * vec3(texture(material.texture_diffuse1, TexCoords)); @@ -91,7 +91,7 @@ vec3 ComputePointLight(PointLight light, vec3 norm, vec3 viewDir, vec3 fragPos) vec3 ComputeSpotLight(SpotLight light, vec3 norm, vec3 viewDir, vec3 fragPos) { vec3 lightDir = normalize(light.position - fragPos); - vec3 reflectDir = reflect(-lightDir, norm); + vec3 halfwayDir = normalize(lightDir + viewDir); float theta = dot(lightDir, normalize(-light.direction)); float epsilon = light.cutOff - light.outerCutOff; @@ -101,7 +101,7 @@ vec3 ComputeSpotLight(SpotLight light, vec3 norm, vec3 viewDir, vec3 fragPos) { float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); float diff = max(dot(norm, lightDir), 0.0); - float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + float spec = pow(max(dot(norm, halfwayDir), 0.0), material.shininess); vec3 ambient = light.ambient * vec3(texture(material.texture_diffuse1, TexCoords)); vec3 diffuse = light.diffuse * diff * vec3(texture(material.texture_diffuse1, TexCoords)); diff --git a/shaders/lighting_vertex.glsl b/shaders/lighting_vertex.glsl index fee85db..34ab7e8 100644 --- a/shaders/lighting_vertex.glsl +++ b/shaders/lighting_vertex.glsl @@ -7,9 +7,12 @@ out vec3 Normal; out vec3 FragPos; out vec2 TexCoords; +layout(std140, binding = 0) uniform Matrices +{ + mat4 projection; + mat4 view; +}; uniform mat4 model; -uniform mat4 view; -uniform mat4 projection; void main() { diff --git a/src/camera.cpp b/src/camera.cpp index 6752016..cb75072 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -32,11 +32,11 @@ void Camera::process_mouse(double x, double y) pitch = -89.0f; double pitch_factor = cos(glm::radians(pitch)); - front = (glm::vec3){ + front = glm::vec3( cos(glm::radians(yaw)) * pitch_factor, sin(glm::radians(pitch)), sin(glm::radians(yaw)) * pitch_factor - }; + ); last_mouse_x = x; last_mouse_y = y; diff --git a/src/defer.hpp b/src/defer.hpp index d23d726..a4751d3 100644 --- a/src/defer.hpp +++ b/src/defer.hpp @@ -1,7 +1,7 @@ #include "scope_guard.hpp" -#define __$CONCAT(a,b) a ## b -#define _$CONCAT(a, b) __$CONCAT(a, b) +#define __MACRO_CONCAT(a, b) a ## b +#define __MACRO_CONCAT_EXPAND(a, b) __MACRO_CONCAT(a, b) -#define guard(M_fn) auto _$CONCAT(__defer__, __LINE__) = (sg::make_scope_guard((M_fn))); -#define defer(M_stmt) auto _$CONCAT(__defer__, __LINE__) = (sg::make_scope_guard([&](void) -> void { M_stmt ; })); +#define guard(M_fn) auto __MACRO_CONCAT_EXPAND(__defer__, __LINE__) = (sg::make_scope_guard((M_fn))); +#define defer(M_stmt) auto __MACRO_CONCAT_EXPAND(__defer__, __LINE__) = (sg::make_scope_guard([&](void) -> void { M_stmt ; })); diff --git a/src/main.cpp b/src/main.cpp index b2b3731..bab35e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -26,9 +26,20 @@ std::function framebuffer_size_callback = nullptr; std::function cursor_position_callback = nullptr; +static const int samples = 4; void GLAPIENTRY gl_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { + (void)source; + (void)id; + (void)severity; + (void)length; + (void)userParam; + + if (severity == GL_DEBUG_SEVERITY_NOTIFICATION) { + return; + } + fprintf(stderr, "OPENGL MESSAGE: %s\n", message); if (type == GL_DEBUG_TYPE_ERROR) { fprintf(stderr, "exiting due to gl error above...\n"); @@ -45,6 +56,9 @@ int main() glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + if (samples > 0) { + glfwWindowHint(GLFW_SAMPLES, samples); + } GLFWwindow *window = glfwCreateWindow(800, 600, "gaming", NULL, NULL); if (!window) { @@ -55,14 +69,16 @@ int main() gladLoadGL(); glfwSwapInterval(0); - glfwSetFramebufferSizeCallback(window, [](GLFWwindow *_, int width, int height) { + glfwSetFramebufferSizeCallback(window, [](GLFWwindow *window, int width, int height) { + (void)window; if (framebuffer_size_callback) { framebuffer_size_callback(width, height); } }); glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - glfwSetCursorPosCallback(window, [](GLFWwindow *_, double x, double y) { + glfwSetCursorPosCallback(window, [](GLFWwindow *window, double x, double y) { + (void)window; if (cursor_position_callback) { cursor_position_callback(x, y); } @@ -71,6 +87,9 @@ int main() glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glEnable(GL_DEBUG_OUTPUT); + if (samples > 0) { + glEnable(GL_MULTISAMPLE); + } glDebugMessageCallback(gl_message_callback, NULL); glm::vec3 point_light_positions[] = { @@ -86,15 +105,6 @@ int main() glm::mat4 projection = glm::mat4(1.0f); Camera camera; - framebuffer_size_callback = [&projection](int width, int height) { - projection = glm::perspective(glm::radians(45.0f), (float)width / (float)height, 0.1f, 100.0f); - glViewport(0, 0, width, height); - }; - - cursor_position_callback = [&camera](double x, double y) { - camera.process_mouse(x, y); - }; - std::vector meshes; load_model_from_file(meshes, "./assets/model/backpack.obj"); @@ -130,6 +140,29 @@ int main() glBindVertexArray(0); } + unsigned int matrices_ubo; + { + glGenBuffers(1, &matrices_ubo); + glBindBuffer(GL_UNIFORM_BUFFER, matrices_ubo); + glBufferData(GL_UNIFORM_BUFFER, 2 * sizeof(glm::mat4), NULL, GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + glBindBufferRange(GL_UNIFORM_BUFFER, 0, matrices_ubo, 0, 2 * sizeof(glm::mat4)); + } + + framebuffer_size_callback = [&projection, matrices_ubo](int width, int height) { + projection = glm::perspective(glm::radians(45.0f), (float)width / (float)height, 0.1f, 100.0f); + glViewport(0, 0, width, height); + + glBindBuffer(GL_UNIFORM_BUFFER, matrices_ubo); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), &projection); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + }; + + cursor_position_callback = [&camera](double x, double y) { + camera.process_mouse(x, y); + }; + float delta_time = 0.0f; float last_frame_time = 0.0f; @@ -157,6 +190,10 @@ int main() camera.movement(delta_time, Camera::MovementDown); } + glBindBuffer(GL_UNIFORM_BUFFER, matrices_ubo); + glBufferSubData(GL_UNIFORM_BUFFER, sizeof(glm::mat4), sizeof(glm::mat4), &camera.view); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + glClearColor(0.5, 0.5, 0.5, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -165,11 +202,9 @@ int main() { glUseProgram(lit_program); program_set(lit_program, "model", glm::scale(glm::translate(glm::mat4(1.0), glm::vec3(0.0, 0.0, 0.0)), glm::vec3(1.0, 1.0, 1.0))); - program_set(lit_program, "projection", projection); - program_set(lit_program, "view", camera.view); program_set(lit_program, "viewPos", camera.position); - program_set(lit_program, "material.shininess", 32.0f); + program_set(lit_program, "material.shininess", 64.0f); program_set(lit_program, "directionalLight.direction", -0.2f, -1.0f, -0.3f); program_set(lit_program, "directionalLight.ambient", 0.05f, 0.05f, 0.05f); diff --git a/src/mesh.cpp b/src/mesh.cpp index 8b746b6..7bc01f2 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -20,27 +20,25 @@ void Mesh::init() { glGenVertexArrays(1, &vao); glGenBuffers(1, &vertices_vbo); + glGenBuffers(1, &indicies_ebo); glBindVertexArray(vao); + { + glBindBuffer(GL_ARRAY_BUFFER, vertices_vbo); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicies_ebo); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(unsigned int), &indicies[0], GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, vertices_vbo); - glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); - - glGenBuffers(1, &indicies_ebo); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicies_ebo); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicies.size() * sizeof(unsigned int), &indicies[0], GL_STATIC_DRAW); - - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position)); - - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal)); - - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, textureCoords)); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, normal)); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, textureCoords)); + } glBindVertexArray(0); } @@ -54,10 +52,13 @@ void Mesh::draw(unsigned int program_id) const glActiveTexture(GL_TEXTURE0 + i); - if (texture.type == "texture_diffuse") { + switch (texture.type) { + case Texture::TextureType::DIFFUSE: program_set(program_id, ("material.texture_diffuse" + std::to_string(diffuse_number++)).c_str(), static_cast(i)); - } else if (texture.type == "texture_specular") { + break; + case Texture::TextureType::SPECULAR: program_set(program_id, ("material.texture_specular" + std::to_string(specular_number++)).c_str(), static_cast(i)); + break; } glBindTexture(GL_TEXTURE_2D, texture.id); @@ -72,7 +73,7 @@ void Mesh::draw(unsigned int program_id) const static std::unordered_map file_path_to_loaded_texture_cache; -static void load_material_textures(std::vector &out_textures, const aiMaterial *material, const aiTextureType texture_type, const char *type_name, const std::string &texture_root_directory) +static void load_material_textures(std::vector &out_textures, const aiMaterial *material, const aiTextureType texture_type, Texture::TextureType type_name, const std::string &texture_root_directory) { unsigned int texture_count = aiGetMaterialTextureCount(material, texture_type); @@ -125,11 +126,9 @@ static void visit_node_process_mesh(const aiScene *scene, const aiMesh *mesh, co } } - if (mesh->mMaterialIndex >= 0) { - aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; - load_material_textures(textures, material, aiTextureType_DIFFUSE, "texture_diffuse", *texture_root_directory); - load_material_textures(textures, material, aiTextureType_SPECULAR, "texture_specular", *texture_root_directory); - } + aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; + load_material_textures(textures, material, aiTextureType_DIFFUSE, Texture::TextureType::DIFFUSE, *texture_root_directory); + load_material_textures(textures, material, aiTextureType_SPECULAR, Texture::TextureType::SPECULAR, *texture_root_directory); } static void visit_node(aiNode *node, const aiScene *scene, std::string *texture_root_directory, std::vector &out_meshes) @@ -161,5 +160,5 @@ void load_model_from_file(std::vector &out_meshes, const std::string &path auto t2 = std::chrono::high_resolution_clock::now(); std::chrono::duration delta = t2 - t1; - printf("processed %ld meshes in %fms\n", out_meshes.size(), delta.count()); + printf("load_model_from_file: processed %ld meshes (took %fms)\n", out_meshes.size(), delta.count()); } diff --git a/src/mesh.hpp b/src/mesh.hpp index 94b71ca..5f9afca 100644 --- a/src/mesh.hpp +++ b/src/mesh.hpp @@ -12,15 +12,19 @@ struct Vertex { }; struct Texture { + enum class TextureType { + DIFFUSE, + SPECULAR + }; + unsigned int id; - std::string type; + TextureType type; }; struct Mesh { std::vector vertices; std::vector textures; std::vector indicies; - bool use_indicies = true; unsigned int vao, vertices_vbo, indicies_ebo; diff --git a/src/texture.cpp b/src/texture.cpp index 883cba5..7698e6f 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -3,16 +3,24 @@ #include "common.hpp" #include "vendor/glad/glad.h" #include "vendor/stb_image.h" +#include #include +#include #include #include +#include #include #include +struct LoadedImage { + unsigned char *data = NULL; + int width, height = 0; +}; + unsigned int texture_from_file(const char *path) { - printf("texture_from_file: load %s\n", path); + auto t1 = std::chrono::high_resolution_clock::now(); int x, y; unsigned char *data = stbi_load(path, &x, &y, NULL, 4); @@ -37,27 +45,64 @@ unsigned int texture_from_file(const char *path) glBindTexture(GL_TEXTURE_2D, 0); + auto t2 = std::chrono::high_resolution_clock::now(); + std::chrono::duration delta = t2 - t1; + + printf("texture_from_file: loaded %s (took %fms)\n", path, delta.count()); + return texture; } unsigned int cubemap_from_files(std::vector &faces) { + auto t1 = std::chrono::high_resolution_clock::now(); + + assert(faces.size() == 6); + unsigned int thread_count = faces.size() / 2; + unsigned int images_per_thread = faces.size() / thread_count; + + std::vector loaded_images(faces.size()); + defer( + for (auto &image : loaded_images) { + free(image.data); + } + ) + + std::vector threads; + threads.reserve(thread_count); + for (unsigned int i = 0; i < thread_count; i++) { + unsigned int image_index = i * images_per_thread; + threads.push_back( + std::thread { + [](unsigned int images_per_thread, unsigned int image_index, std::vector *faces, std::vector *images) { + for (unsigned int i = image_index; i < (image_index + images_per_thread); i++) { + LoadedImage *image = &images->at(i); + image->data = stbi_load(faces->at(i), &image->width, &image->height, NULL, 3); + } + }, + images_per_thread, + image_index, + &faces, + &loaded_images + } + ); + } + + for (auto &task : threads) { + task.join(); + } + + unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_CUBE_MAP, texture); - int width, height = 0; - unsigned char *data = NULL; - for (unsigned int i = 0; i < faces.size(); i++) { - printf("cubemap_from_files: load %s\n", faces[i]); - - data = stbi_load(faces[i], &width, &height, NULL, 3); - defer(stbi_image_free(data)); - - if (data) { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); + for (unsigned int i = 0; i < loaded_images.size(); i++) { + LoadedImage *image = &loaded_images[i]; + if (image->data) { + glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data); } else { - fprintf(stderr, "cubemap_from_files: failed to load file %s\n", faces[i]); + fprintf(stderr, "cubemap_from_files: file was not loaded: %s\n", faces[i]); } } @@ -67,5 +112,10 @@ unsigned int cubemap_from_files(std::vector &faces) glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + auto t2 = std::chrono::high_resolution_clock::now(); + std::chrono::duration delta = t2 - t1; + + printf("cubemap_from_files: loaded %ld faces (took %fms)\n", faces.size(), delta.count()); + return texture; }