From 8bc8033989eb2f384485f7f59d34055f0eb688ad Mon Sep 17 00:00:00 2001 From: Guillaume Sarthou Date: Wed, 14 Aug 2024 23:15:58 +0200 Subject: [PATCH] [OpenGl] add normal map render --- .../overworld/Engine/Common/Models/Vertex.h | 3 ++ .../Engine/Graphics/Assimp/ModelLoader.h | 6 +++ .../Engine/Graphics/OpenGL/MeshHandle.h | 12 +++-- shaders/light_shader.fs | 12 ++++- shaders/light_shader.vs | 18 +++++-- .../Common/Models/Loaders/ObjLoader.cpp | 3 +- .../Common/Models/Loaders/TinyObjReader.cpp | 46 +++++++++++----- src/Engine/Graphics/Assimp/ModelLoader.cpp | 53 +++++++++++++++++++ src/Engine/Graphics/OpenGL/Renderer.cpp | 2 +- 9 files changed, 128 insertions(+), 27 deletions(-) diff --git a/include/overworld/Engine/Common/Models/Vertex.h b/include/overworld/Engine/Common/Models/Vertex.h index dadc3ebe..7dfad906 100755 --- a/include/overworld/Engine/Common/Models/Vertex.h +++ b/include/overworld/Engine/Common/Models/Vertex.h @@ -9,9 +9,12 @@ namespace owds { class Vertex { public: + // Never change the attributes order glm::vec3 position_; glm::vec3 normal_; glm::vec2 uv_; + glm::vec3 tangent_; + glm::vec3 bitangent_; }; } // namespace owds diff --git a/include/overworld/Engine/Graphics/Assimp/ModelLoader.h b/include/overworld/Engine/Graphics/Assimp/ModelLoader.h index 199561d9..fb734fbd 100755 --- a/include/overworld/Engine/Graphics/Assimp/ModelLoader.h +++ b/include/overworld/Engine/Graphics/Assimp/ModelLoader.h @@ -3,6 +3,8 @@ #include +#include "overworld/Engine/Common/Models/Mesh.h" +#include "overworld/Engine/Common/Models/Model.h" #include "overworld/Engine/Common/Models/ModelLoader.h" namespace owds::assimp { @@ -10,6 +12,10 @@ namespace owds::assimp { { public: std::unique_ptr load(const std::filesystem::path& path) const override; + + private: + void computeTangentSpace(std::unique_ptr model); + void computeTangentSpace(Mesh& mesh); }; } // namespace owds::assimp diff --git a/include/overworld/Engine/Graphics/OpenGL/MeshHandle.h b/include/overworld/Engine/Graphics/OpenGL/MeshHandle.h index 62848a0f..2c6fba93 100644 --- a/include/overworld/Engine/Graphics/OpenGL/MeshHandle.h +++ b/include/overworld/Engine/Graphics/OpenGL/MeshHandle.h @@ -61,6 +61,7 @@ namespace owds { { name = "material.texture_normal"; number = std::to_string(normal_nr++); + shader.setFloat("material.use_normal", 1.); } else if(textures[i].type_ == texture_height) { @@ -96,6 +97,7 @@ namespace owds { glActiveTexture(GL_TEXTURE0 + texture_pose_offset + nb_used); glBindTexture(GL_TEXTURE_2D, 0); shader.setInt("material.texture_normal1", texture_pose_offset + nb_used); + shader.setFloat("material.use_normal", 0.); nb_used++; } @@ -150,11 +152,11 @@ namespace owds { glEnableVertexAttribArray(2); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, uv_)); // vertex tangent - // glEnableVertexAttribArray(3); - // glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Tangent)); - //// vertex bitangent - // glEnableVertexAttribArray(4); - // glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Bitangent)); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, tangent_)); + // vertex bitangent + glEnableVertexAttribArray(4); + glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, bitangent_)); //// ids // glEnableVertexAttribArray(5); // glVertexAttribIPointer(5, 4, GL_INT, sizeof(Vertex), (void*)offsetof(Vertex, m_BoneIDs)); diff --git a/shaders/light_shader.fs b/shaders/light_shader.fs index bc166114..3fb93f80 100644 --- a/shaders/light_shader.fs +++ b/shaders/light_shader.fs @@ -4,14 +4,17 @@ out vec4 FragColor; layout (location = 0) in vec3 FragPos; layout (location = 1) in vec3 Normal; layout (location = 2) in vec2 TexCoords; +layout (location = 3) in mat3 TBN; struct Material { sampler2D texture_diffuse1; sampler2D texture_specular1; - //sampler2D emission; + sampler2D texture_normal1; + float shininess; float specular; vec4 color; + float use_normal; // 0 if no normal map }; uniform Material material; @@ -71,6 +74,13 @@ void main() { // properties vec3 norm = normalize(Normal); + if(material.use_normal > 0) + { + norm = texture(material.texture_normal1, TexCoords).rgb; + norm = norm * 2.0 - 1.0; + norm = normalize(TBN * norm); + } + vec3 viewDir = normalize(view_pose - FragPos); // phase 1: Directional lighting diff --git a/shaders/light_shader.vs b/shaders/light_shader.vs index f4de5274..c162766b 100644 --- a/shaders/light_shader.vs +++ b/shaders/light_shader.vs @@ -2,10 +2,13 @@ layout (location = 0) in vec3 aPos; // the position variable has attribute position 0 layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords; +layout (location = 3) in vec3 aTangent; +layout (location = 4) in vec3 aBitangent; layout (location = 0) out vec3 FragPos; layout (location = 1) out vec3 Normal; layout (location = 2) out vec2 TexCoords; +layout (location = 3) out mat3 TBN; uniform mat4 model; uniform mat4 view; @@ -13,10 +16,15 @@ uniform mat4 projection; void main() { - FragPos = vec3(model * vec4(aPos, 1.0)); - Normal = mat3(transpose(inverse(model))) * aNormal; - TexCoords = aTexCoords; + FragPos = vec3(model * vec4(aPos, 1.0)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; - gl_Position = projection * view * vec4(FragPos, 1.0); - //gl_Position = projection * view * model * vec4(aPos, 1.0); + vec3 T = normalize(vec3(model * vec4(aTangent, 0.0))); + vec3 B = normalize(vec3(model * vec4(aBitangent, 0.0))); + vec3 N = normalize(vec3(model * vec4(aNormal, 0.0))); + TBN = mat3(T, B, N); + + gl_Position = projection * view * vec4(FragPos, 1.0); + //gl_Position = projection * view * model * vec4(aPos, 1.0); } \ No newline at end of file diff --git a/src/Engine/Common/Models/Loaders/ObjLoader.cpp b/src/Engine/Common/Models/Loaders/ObjLoader.cpp index 85aac0e2..2435f270 100644 --- a/src/Engine/Common/Models/Loaders/ObjLoader.cpp +++ b/src/Engine/Common/Models/Loaders/ObjLoader.cpp @@ -23,7 +23,7 @@ namespace owds { std::unique_ptr ObjLoader::read(const std::string& path) { std::string mtl_path; - size_t pos = path.find_last_of("/\\"); + auto pos = path.find_last_of("/\\"); if(pos != std::string::npos) mtl_path = path.substr(0, pos); @@ -80,7 +80,6 @@ namespace owds { std::vector& meshes, std::vector& materials) { - (void)materials; // TODO auto model = std::make_unique(Model::create()); for(const auto& tiny_mesh : meshes) diff --git a/src/Engine/Common/Models/Loaders/TinyObjReader.cpp b/src/Engine/Common/Models/Loaders/TinyObjReader.cpp index cd83aaa9..7b87c31c 100644 --- a/src/Engine/Common/Models/Loaders/TinyObjReader.cpp +++ b/src/Engine/Common/Models/Loaders/TinyObjReader.cpp @@ -984,7 +984,12 @@ namespace tinyobj { token += 7; parseTextureName(material.ambient_texname, token); if(material.ambient_texname.find('/') == std::string::npos) - material.ambient_texname = texture_path + "/" + material.ambient_texname; + { + if(texture_path.back() != '/') + material.ambient_texname = texture_path + "/" + material.ambient_texname; + else + material.ambient_texname = texture_path + material.ambient_texname; + } continue; } @@ -994,7 +999,12 @@ namespace tinyobj { token += 7; parseTextureName(material.diffuse_texname, token); if(material.diffuse_texname.find('/') == std::string::npos) - material.diffuse_texname = texture_path + "/" + material.diffuse_texname; + { + if(texture_path.back() != '/') + material.diffuse_texname = texture_path + "/" + material.diffuse_texname; + else + material.diffuse_texname = texture_path + material.diffuse_texname; + } // Set a decent diffuse default value if a diffuse texture is specified // without a matching Kd value. @@ -1014,7 +1024,12 @@ namespace tinyobj { token += 7; parseTextureName(material.specular_texname, token); if(material.specular_texname.find('/') == std::string::npos) - material.specular_texname = texture_path + "/" + material.specular_texname; + { + if(texture_path.back() != '/') + material.specular_texname = texture_path + "/" + material.specular_texname; + else + material.specular_texname = texture_path + material.specular_texname; + } continue; } @@ -1023,8 +1038,19 @@ namespace tinyobj { // continue; // bump texture - // if((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) - // continue; + if(((0 == strncmp(token, "map_Bump", 8)) || (0 == strncmp(token, "map_bump", 8))) && IS_SPACE(token[8])) + { + token += 9; + parseTextureName(material.normal_texname, token); + if(material.normal_texname.find('/') == std::string::npos) + { + if(texture_path.back() != '/') + material.normal_texname = texture_path + "/" + material.normal_texname; + else + material.normal_texname = texture_path + material.normal_texname; + } + continue; + } // bump texture // if((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) @@ -1063,14 +1089,8 @@ namespace tinyobj { // continue; // PBR: normal map texture - if((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) - { - token += 5; - parseTextureName(material.normal_texname, token); - if(material.normal_texname.find('/') == std::string::npos) - material.normal_texname = texture_path + "/" + material.normal_texname; - continue; - } + // if((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) + // continue } // flush last material. material_map->insert(std::pair(material.name, static_cast(materials->size()))); diff --git a/src/Engine/Graphics/Assimp/ModelLoader.cpp b/src/Engine/Graphics/Assimp/ModelLoader.cpp index 69478c42..900d119a 100755 --- a/src/Engine/Graphics/Assimp/ModelLoader.cpp +++ b/src/Engine/Graphics/Assimp/ModelLoader.cpp @@ -5,11 +5,14 @@ #include #include #include +#include +#include #include #include "overworld/Engine/Common/Models/Loaders/ColladaLoader.h" #include "overworld/Engine/Common/Models/Loaders/ObjLoader.h" #include "overworld/Engine/Common/Models/Loaders/StlLoader.h" +#include "overworld/Engine/Common/Models/Mesh.h" #include "overworld/Engine/Common/Models/Model.h" namespace owds::assimp { @@ -121,4 +124,54 @@ namespace owds::assimp { return model; } + + void ModelLoader::computeTangentSpace(std::unique_ptr model) + { + for(auto& mesh : model->meshes_) + computeTangentSpace(mesh); + } + + void ModelLoader::computeTangentSpace(Mesh& mesh) + { + for(unsigned int i = 0; i < mesh.indices_.size(); i = i + 3) + { + glm::vec3& vertex0 = mesh.vertices_.at(mesh.indices_.at(i)).position_; + glm::vec3& vertex1 = mesh.vertices_.at(mesh.indices_.at(i + 1)).position_; + glm::vec3& vertex2 = mesh.vertices_.at(mesh.indices_.at(i + 3)).position_; + + glm::vec3 normal = glm::cross((vertex1 - vertex0), (vertex2 - vertex0)); + + glm::vec3 delta_pos; + if(vertex0 == vertex1) + delta_pos = vertex2 - vertex0; + else + delta_pos = vertex1 - vertex0; + + glm::vec2& uv0 = mesh.vertices_.at(mesh.indices_.at(i)).uv_; + glm::vec2& uv1 = mesh.vertices_.at(mesh.indices_.at(i + 1)).uv_; + // lm::vec2& uv2 = mesh.vertices_.at(mesh.indices_.at(i + 2)).uv_; + + glm::vec2 delta_uv1 = uv1 - uv0; + + glm::vec3 tan; + // avoid divion with 0 + if(delta_uv1.s != 0) + tan = delta_pos / delta_uv1.s; + else + tan = delta_pos / 1.0f; + + tan = glm::normalize(tan - glm::dot(normal, tan) * normal); + + glm::vec3 bin = glm::normalize(glm::cross(tan, normal)); + + // write into array - for each vertex of the face the same value + mesh.vertices_[mesh.indices_.at(i)].tangent_ = tan; + mesh.vertices_[mesh.indices_.at(i + 1)].tangent_ = tan; + mesh.vertices_[mesh.indices_.at(i + 2)].tangent_ = tan; + + mesh.vertices_[mesh.indices_.at(i)].bitangent_ = bin; + mesh.vertices_[mesh.indices_.at(i + 1)].bitangent_ = bin; + mesh.vertices_[mesh.indices_.at(i + 2)].bitangent_ = bin; + } + } } // namespace owds::assimp \ No newline at end of file diff --git a/src/Engine/Graphics/OpenGL/Renderer.cpp b/src/Engine/Graphics/OpenGL/Renderer.cpp index e78cbb28..10aa299f 100644 --- a/src/Engine/Graphics/OpenGL/Renderer.cpp +++ b/src/Engine/Graphics/OpenGL/Renderer.cpp @@ -78,7 +78,7 @@ namespace owds { if(render_camera_.aa_setting_ != ViewAntiAliasing_e::off) setAntiAliasing(render_camera_.aa_setting_); - Shader::shaders_directory = "/home/gsarthou/Robots/Dacobot2/ros2_ws/src/overworld/shaders/"; + Shader::shaders_directory = "/home/gsarthou/Robots/Dacobot2/ros2_ws/src/overworld/shaders/"; // TODO not hard coded shaders_.insert({ "default", {"light_shader.vs", "light_shader.fs"} });