diff --git a/include/client/model.hpp b/include/client/model.hpp index fcfe28db..522359c6 100644 --- a/include/client/model.hpp +++ b/include/client/model.hpp @@ -31,6 +31,8 @@ struct Vertex { glm::vec2 textureCoords; int m_boneIDs[MAX_BONE_INFLUENCE]; /* Bone indices which influence the vertex */ float m_weights[MAX_BONE_INFLUENCE]; /* Weights for each bone */ + glm::vec3 tangent; + glm::vec3 bitangent; }; class Texture { @@ -115,6 +117,7 @@ class Mesh : public Renderable { // render data opengl needs GLuint VAO, VBO, EBO; + void setupNormalMaps(); }; diff --git a/src/client/client.cpp b/src/client/client.cpp index e2867082..8352c88a 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -202,13 +202,13 @@ bool Client::init() { auto deferred_light_box_frag_path = shaders_dir / "deferred_light_box.frag"; this->deferred_light_box_shader = std::make_shared(deferred_light_box_vert_path.string(), deferred_light_box_frag_path.string()); - auto floor_model_path = env_models_dir / "floor.obj"; + auto floor_model_path = env_models_dir / "floor-normal.obj"; this->floor_model = std::make_unique(floor_model_path.string(), true); - auto wall_model_path = env_models_dir / "wall.obj"; + auto wall_model_path = env_models_dir / "wall-normals.obj"; this->wall_model = std::make_unique(wall_model_path.string(), true); - auto pillar_model_path = env_models_dir / "pillar.obj"; + auto pillar_model_path = env_models_dir / "pillar-normals.obj"; this->pillar_model = std::make_unique(pillar_model_path.string(), true); auto torchlight_model_path = env_models_dir / "exit.obj"; diff --git a/src/client/model.cpp b/src/client/model.cpp index 7edfb15d..91ae8972 100644 --- a/src/client/model.cpp +++ b/src/client/model.cpp @@ -14,9 +14,11 @@ #include #include +#include "assimp/postprocess.h" #include "client/renderable.hpp" #include "client/constants.hpp" #include "client/util.hpp" +#include "glm/ext/quaternion_geometric.hpp" #include "server/game/torchlight.hpp" #include "shared/game/sharedobject.hpp" #include "shared/utilities/constants.hpp" @@ -44,11 +46,14 @@ Mesh::Mesh( const Material& material) : vertices(vertices), indices(indices), textures(textures), material(material) { + setupNormalMaps(); + glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); @@ -75,6 +80,14 @@ Mesh::Mesh( glEnableVertexAttribArray(4); glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, m_weights))); + // tangents + glEnableVertexAttribArray(5); + glVertexAttribPointer(5, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, tangent))); + + // bitangents + glEnableVertexAttribArray(6); + glVertexAttribPointer(6, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast(offsetof(Vertex, bitangent))); + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); @@ -85,6 +98,44 @@ Mesh::Mesh( // std::cout << "\t shininess" << this->material.shininess << std::endl; } +void Mesh::setupNormalMaps() { + for (int i=0; i + 2 < indices.size(); i+=3){ + // if (i + 1 > indices.size()) { + // } + // get 3 vertices for a triangle + Vertex& v0 = vertices.at(indices.at(i)); + Vertex& v1 = vertices.at(indices.at(i+1)); + Vertex& v2 = vertices.at(indices.at(i+2)); + + // Edges of the triangle : position delta + glm::vec3 deltaPos1 = v1.position - v0.position; + glm::vec3 deltaPos2 = v2.position - v0.position; + + // UV delta + glm::vec2 deltaUV1 = v1.textureCoords - v0.textureCoords; + glm::vec2 deltaUV2 = v2.textureCoords - v0.textureCoords; + + float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x); + glm::vec3 tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y)*r; + glm::vec3 bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x)*r; + + v0.tangent += tangent; + v1.tangent += tangent; + v2.tangent += tangent; + + v0.bitangent += bitangent; + v1.bitangent += bitangent; + v2.bitangent += bitangent; + } + + for (int i = 0; i < vertices.size(); i++) { + // vertices.at(i).tangent = glm::normalize(vertices.at(i).tangent); + // // std::cout << "tangent " << glm::to_string(vertices.at(i).tangent) << "\n"; + // // exit(1); + // vertices.at(i).bitangent = glm::normalize(vertices.at(i).bitangent); + } +} + void Mesh::draw( Shader* shader, glm::vec3 camPos, @@ -93,10 +144,12 @@ void Mesh::draw( auto model = this->getModelMat(); shader->setMat4("model", model); + shader->setVec3("viewPos", camPos); if (textures.size() != 0) { unsigned int diffuseNr = 1; unsigned int specularNr = 1; + unsigned int normalNr = 1; for(unsigned int i = 0; i < textures.size(); i++) { glActiveTexture(GL_TEXTURE0 + i); // activate proper texture unit before binding // retrieve texture number (the N in diffuse_textureN) @@ -106,11 +159,20 @@ void Mesh::draw( number = std::to_string(diffuseNr++); else if(name == "texture_specular") number = std::to_string(specularNr++); + else if(name == "texture_normal") + number = std::to_string(normalNr++); std::string shaderTextureName = name + number; shader->setInt(shaderTextureName, i); glBindTexture(GL_TEXTURE_2D, textures[i].getID()); } + + if (normalNr == 1) { + // never set any normal map texture uniform + shader->setBool("has_normal_map", false); + } else { + shader->setBool("has_normal_map", true); + } } else { // shader->setFloat("shininess", this->material.shininess); } @@ -142,13 +204,15 @@ Model::Model(const std::string& filepath, bool flip_uvs) { aiProcess_FlipUVs | aiProcess_SplitLargeMeshes | aiProcess_OptimizeMeshes | - aiProcess_GenBoundingBoxes); // needed to query bounding box of the model later + aiProcess_GenBoundingBoxes | // needed to query bounding box of the model later + aiProcess_CalcTangentSpace); } else { scene = importer.ReadFile(filepath, aiProcess_Triangulate | // flag to only creates geometry made of triangles aiProcess_SplitLargeMeshes | aiProcess_OptimizeMeshes | - aiProcess_GenBoundingBoxes); // needed to query bounding box of the model later + aiProcess_GenBoundingBoxes | // needed to query bounding box of the model later + aiProcess_CalcTangentSpace); } if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { @@ -355,6 +419,9 @@ Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene) { std::vector specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR); textures.insert(textures.end(), specularMaps.begin(), specularMaps.end()); + std::vector heightMaps = loadMaterialTextures(material, aiTextureType_HEIGHT); + textures.insert(textures.end(), heightMaps.begin(), heightMaps.end()); + if(AI_SUCCESS != material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse_color)) { std::cout << "couldn't get diffuse color" << std::endl; } @@ -442,7 +509,7 @@ void Model::extractBoneWeight(std::vector& vertices, aiMesh* mesh, const std::vector Model::loadMaterialTextures(aiMaterial* mat, const aiTextureType& type) { std::vector textures; - // std::cout << "material has " << mat->GetTextureCount(type) << " textures of type " << aiTextureTypeToString(type) << std::endl; + std::cout << "material has " << mat->GetTextureCount(type) << " textures of type " << aiTextureTypeToString(type) << std::endl; for(unsigned int i = 0; i < mat->GetTextureCount(type); i++) { aiString str; mat->GetTexture(type, i, &str); @@ -467,6 +534,9 @@ Texture::Texture(const std::string& filepath, const aiTextureType& type) { case aiTextureType_SPECULAR: this->type = "texture_specular"; break; + case aiTextureType_HEIGHT: + this->type = "texture_normal"; + break; default: throw std::invalid_argument(std::string("Unimplemented texture type ") + aiTextureTypeToString(type)); } diff --git a/src/client/shaders/deferred_geometry.frag b/src/client/shaders/deferred_geometry.frag index 64df8343..b7269600 100644 --- a/src/client/shaders/deferred_geometry.frag +++ b/src/client/shaders/deferred_geometry.frag @@ -1,23 +1,52 @@ #version 330 core + +#define NR_POINT_LIGHTS 32 + layout (location = 0) out vec3 gPosition; layout (location = 1) out vec3 gNormal; layout (location = 2) out vec4 gAlbedoSpec; +// layout (location = 3) out vec3 gTangentNormal; in vec3 FragPos; in vec3 FragNormal; in vec2 TexCoords; +in mat3 TBN; uniform sampler2D texture_diffuse1; uniform sampler2D texture_specular1; +uniform sampler2D texture_normal1; +uniform bool has_normal_map; void main() { // store the fragment position vector in the first gbuffer texture gPosition = FragPos; // also store the per-fragment normals into the gbuffer - gNormal = normalize(FragNormal); + if (has_normal_map) { + // obtain normal from normal map in range [0,1] + gNormal = texture(texture_normal1, TexCoords).rgb; + // transform normal vector to range [-1,1] + gNormal = gNormal * 2.0 - 1.0; + gNormal = normalize(TBN * gNormal); + gNormal = normalize(gNormal); + // if (gNormal == vec3(0.0, 0.0, 0.0)) { + // gAlbedoSpec.rgb = vec3(1.0, 0.0, 0.0); + // } else { + // gAlbedoSpec.rgb = vec3(0.0, 1.0, 0.0); + // } + + // gNormal = normalize(TBN * (texture(texture_normal1, TexCoords).rgb * 2.0 - 1.0)); + // gNormal = vec3(0.0, 1.0, 0.0); + // gAlbedoSpec.rgb = texture(texture_normal1, TexCoords).rgb; + } else { + gNormal = normalize(FragNormal); + // gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb; + } + // and the diffuse per-fragment color gAlbedoSpec.rgb = texture(texture_diffuse1, TexCoords).rgb; // store specular intensity in gAlbedoSpec's alpha component gAlbedoSpec.a = texture(texture_specular1, TexCoords).r; -} + + // gTangentNormal = texture(texture_normal, TexCoords).rgb; +} diff --git a/src/client/shaders/deferred_geometry.vert b/src/client/shaders/deferred_geometry.vert index 8f208927..07b4e461 100644 --- a/src/client/shaders/deferred_geometry.vert +++ b/src/client/shaders/deferred_geometry.vert @@ -1,16 +1,22 @@ #version 330 core + +#define NR_POINT_LIGHTS 32 + layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords; layout (location = 3) in ivec4 boneIds; layout (location = 4) in vec4 weights; +layout (location = 5) in vec3 aTangent; +layout (location = 6) in vec3 aBitangent; const int MAX_BONES = 100; const int MAX_BONE_INFLUENCE = 4; out vec3 FragPos; -out vec2 TexCoords; out vec3 FragNormal; +out vec2 TexCoords; +out mat3 TBN; uniform mat4 model; uniform mat4 viewProj; @@ -49,9 +55,26 @@ void main() vec4 worldPos = model * totalPosition; FragPos = worldPos.xyz; TexCoords = aTexCoords; - + + vec3 normal0 = (model * vec4(aNormal, 1.0)).xyz; + vec3 tangent0 = (model * vec4(aTangent, 1.0)).xyz; + + vec3 Normal = normalize(normal0); + vec3 Tangent = normalize(tangent0); + Tangent = normalize(Tangent - dot(Tangent, Normal) * Normal); + vec3 Bitangent = cross(Normal, Tangent); + TBN = mat3(Tangent, Bitangent, Normal); + mat3 normalMatrix = transpose(inverse(mat3(model))); - FragNormal = normalMatrix * vec3(totalNormal); + // vec3 T = normalize(normalMatrix * aTangent); + // vec3 B = normalize(normalMatrix * aBitangent); + // vec3 N = normalize(normalMatrix * aNormal); + // // T = normalize(T - dot(T, N) * N); + // // vec3 B = cross(N, T); + // + // TBN = mat3(T, B, N); + + FragNormal = normalMatrix * aNormal; gl_Position = viewProj * worldPos;