Implement normal maps

This commit is contained in:
SeanOMik 2022-09-29 19:13:47 -04:00
parent dbc12e422c
commit da3c56af36
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
10 changed files with 209 additions and 22 deletions

View File

@ -172,13 +172,14 @@ int main(int argc, char *argv[]) {
// entity.add_component<se::ModelComponent>("examples/dev_testing/resources/viper/viper.obj");
// entity.add_component<se::ModelComponent>("examples/dev_testing/resources/halot/chief.fbx");
entity.add_component<se::ModelComponent>("examples/dev_testing/resources/planks/planks.fbx");
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/planks/planks.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE);
entity.add_component<se::ModelComponent>("examples/dev_testing/resources/bricks/bricks.fbx", simpleengine::gfx::ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE);
// entity.add_component<se::ModelComponent>("examples/dev_testing/resources/scientist/scientist.fbx");
// entity.add_component<se::ModelComponent>("examples/dev_testing/resources/paradigm/paradigm.fbx");
// entity.add_component<se::RotatingComponent>();
auto &transform_comp = entity.add_component<se::TransformComponent>();
transform_comp.translate(7.f, -4.f, 0.f);
transform_comp.translate(7.f, 0.f, 0.f);
// transform_comp.scale(0.05f);
// transform_comp.rotate_z(-90.f);
// transform_comp.rotate_y(-90.f);

View File

@ -8,6 +8,7 @@
#include "../renderable.h"
#include "../transformable.h"
#include "material.h"
#include "../vector.h"
#include <optional>
#include <vector>
@ -23,14 +24,23 @@ namespace simpleengine::gfx {
std::vector<LitVertex> vertices;
std::vector<GLuint> indicies;
std::vector<simpleengine::Vectorf> tangents;
std::vector<simpleengine::Vectorf> bitangents;
bool are_buffers_created = false;
gfx::VBO ebo;
gfx::VBO vbo;
gfx::VAO vao;
gfx::VBO tangent_vbo;
//gfx::VAO tangent_vao;
gfx::VBO bitangent_vbo;
//gfx::VAO bitangent_vao;
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material);
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies = std::vector<GLuint>(), std::optional<Material> material = std::nullopt);
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao);
Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao, gfx::VBO tangent_vbo, gfx::VBO bitangent_vbo);
virtual void destroy() override;
@ -45,5 +55,12 @@ namespace simpleengine::gfx {
*
*/
void calculate_normals();
/**
* @brief Calculate tangents and bi-tangents (tangent space) for the model.
*
* @note This **will** overwrite the existing tangents and bi-tangents.
*/
void calculate_tangents();
};
}

View File

@ -2,6 +2,7 @@
#include "mesh.h"
#include "simpleengine/gfx/texture.h"
#include "../vector.h"
#include <assimp/material.h>
#include <assimp/mesh.h>
@ -14,6 +15,7 @@ namespace simpleengine::gfx {
MdlProcFlag_NONE = 0b00000000,
MdlProcFlag_FLIP_TEX_COORDS_VERTICALLY = 0b00000001,
MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY = 0b00000010,
MdlProcFlag_CALCULATE_TANGENT_SPACE = 0b00000100,
};
/**
@ -39,12 +41,12 @@ namespace simpleengine::gfx {
std::unordered_map<aiTextureType, std::vector<Texture>> load_all_textures(aiMaterial* material);
std::vector<std::shared_ptr<Texture>> load_material_texture(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMaterial* material, aiTextureType type, TextureFlags texture_color);
protected:
void post_process();
public:
void vertically_flip_tex_coords();
void horizontally_flip_tex_coords();
void calculate_tangents();
};
}

View File

@ -51,6 +51,10 @@ namespace simpleengine::gfx {
glBindVertexArray(handle);
}
void unbind() const {
glBindVertexArray(0);
}
// TODO: Fix this.
void enable_attrib(const VBO& vbo, GLuint index, GLint size, GLenum type, GLsizei stride, size_t offset, bool should_bind = true) const {
if (should_bind) {

View File

@ -40,6 +40,10 @@ namespace simpleengine::gfx {
glBindBuffer(type, handle);
}
void unbind() const {
glBindBuffer(type, 0);
}
void buffer(void *data, size_t offset, size_t size) {
bind();
glBufferData(type, size - offset, data, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);

View File

@ -12,6 +12,11 @@ in vec3 vs_view_pos;
in vec3 vs_light_pos;
in vec3 vs_frag_pos;
in mat3 vs_tbn;
in vec3 vs_tangent;
in vec3 vs_bitangent;
const int SAMP_DIFFUSE = 0;
const int SAMP_SPECULAR = 1;
@ -21,6 +26,9 @@ struct Material {
bool has_specular_map;
sampler2D specular_map;
// TODO: Make Optional
sampler2D normal_map;
float ambient_strength;
float diffuse_strength;
float specular_strength;
@ -37,14 +45,12 @@ out vec4 fs_color;
vec3 calculate_lighting();
void main() {
// Combine diffuse lighting, specular, and the texture into one color.
//fs_color = vec4(diffuse, 1.f) * texture(u_material.diffuse, vs_texcoord) + vec4(final_specular, 1.f);
vec3 lighting = calculate_lighting();
fs_color = vec4(lighting, 1.f) * texture(u_material.diffuse, vs_texcoord);
//fs_color = vec4(vs_tangent, 1.f);
//fs_color = vec4(vs_bitangent, 1.f);
}
vec3 calculate_lighting() {
@ -52,8 +58,14 @@ vec3 calculate_lighting() {
//float ambient_strength = 0.1;
vec3 ambient = u_material.ambient_strength * u_light_color;
// Apply the normal map to the lighting.
vec3 normal = vs_normal;
normal = texture(u_material.normal_map, vs_texcoord).rgb;
normal = normal * 2.0 - 1.0;
normal = normalize(vs_tbn * normal);
// Diffuse
vec3 norm = normalize(vs_world_normal);
vec3 norm = normalize(normal);
vec3 light_dir = normalize(vs_light_pos - vs_frag_pos);
float diff = max(dot(norm, light_dir), 0.f);
vec3 diffuse = (diff * u_material.diffuse_strength) * u_light_color;

View File

@ -4,6 +4,9 @@ layout (location = 0) in vec3 vertex_position;
layout (location = 1) in vec3 vertex_color;
layout (location = 2) in vec3 vertex_normal;
layout (location = 3) in vec2 vertex_texcoord;
// TODO: Make these optional
layout (location = 4) in vec3 vertex_tangent;
layout (location = 5) in vec3 vertex_bitangent;
out vec3 vs_position;
out vec3 vs_color;
@ -17,6 +20,11 @@ out vec3 vs_view_pos;
out vec3 vs_light_pos;
out vec3 vs_frag_pos;
out mat3 vs_tbn;
out vec3 vs_tangent;
out vec3 vs_bitangent;
uniform mat4 u_transform_matrix;
uniform mat4 u_view_matrix;
uniform mat4 u_projection_matrix;
@ -41,4 +49,13 @@ void main() {
vs_light_pos = u_light_position;
vs_frag_pos = vec3(u_transform_matrix * vec4(vertex_position, 1.f));
vs_world_normal = mat3(transpose(inverse(u_transform_matrix))) * vertex_normal; // TODO: Do this calculation on the CPU then send to GPU via a uniform
// Calculate the TBN (Tangent, Bi-tangent, and normal matrix)
vec3 T = normalize(vec3(u_transform_matrix * vec4(vertex_tangent, 1.f)));
vec3 B = normalize(vec3(u_transform_matrix * vec4(vertex_bitangent, 1.f)));
vec3 N = normalize(vec3(u_transform_matrix * vec4(vertex_normal, 1.f)));
vs_tbn = mat3(T, B, N);
vs_tangent = vertex_tangent;
vs_bitangent = vertex_bitangent;
}

View File

@ -1,23 +1,26 @@
#include "gfx/mesh.h"
#include "gfx/vbo.h"
#include "vector.h"
#include <optional>
namespace simpleengine::gfx {
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material) :
material(std::make_optional(material)), vertices(vertices), indicies(indicies),
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()) {
vao(gfx::VAO::init()), tangent_vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), bitangent_vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)) {
}
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, std::optional<Material> material) :
material(material), vertices(vertices), indicies(indicies),
vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), ebo(gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false)),
vao(gfx::VAO::init()) {
vao(gfx::VAO::init()), tangent_vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)), bitangent_vbo(gfx::VBO::init(GL_ARRAY_BUFFER, false)) {
}
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao) :
vertices(vertices), indicies(indicies), material(material), ebo(ebo), vbo(vbo), vao(vao) {
Mesh::Mesh(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, Material material, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao,
gfx::VBO tangent_vbo, gfx::VBO bitangent_vbo) :
vertices(vertices), indicies(indicies), material(material), ebo(ebo), vbo(vbo), vao(vao), tangent_vbo(tangent_vbo), bitangent_vbo(bitangent_vbo) {
}
@ -76,4 +79,52 @@ namespace simpleengine::gfx {
vertices[i].normal = normals[i];
}
}
void Mesh::calculate_tangents() {
// Resize the tangent and bitangents
//tangents.clear();
tangents.resize(vertices.size());
//bitangents.clear();
bitangents.resize(vertices.size());
/* std::vector<simpleengine::Vectorf> tangents;
std::vector<simpleengine::Vectorf> bitangents; */
for (int i = 0; i < indicies.size(); i += 3) {
int index0 = indicies[i];
int index1 = indicies[i+1];
int index2 = indicies[i+2];
LitVertex& lit_vertex0 = vertices[index0];
LitVertex& lit_vertex1 = vertices[index1];
LitVertex& lit_vertex2 = vertices[index2];
glm::vec3 pos0 = lit_vertex0.position;
glm::vec3 pos1 = lit_vertex1.position;
glm::vec3 pos2 = lit_vertex2.position;
// Edges of the triangle : postion delta
glm::vec3 delta_pos1 = pos1 - pos0;
glm::vec3 delta_pos2 = pos2 - pos0;
// UV delta
glm::vec2 delta_uv1 = lit_vertex1.tex_coord - lit_vertex0.tex_coord;
glm::vec2 delta_uv2 = lit_vertex2.tex_coord - lit_vertex0.tex_coord;
float r = 1.0f / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
glm::vec3 tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y)*r;
glm::vec3 bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x)*r;
tangents[index0] = tangent;
tangents[index1] = tangent;
tangents[index2] = tangent;
bitangents[index0] = bitangent;
bitangents[index1] = bitangent;
bitangents[index2] = bitangent;
}
/* this->tangents = std::optional<std::pair<std::vector<simpleengine::Vectorf>, std::vector<simpleengine::Vectorf>>>(
{tangents, bitangents}
); */
}
}

View File

@ -10,6 +10,7 @@
#include <optional>
#include <sstream>
#include <stdexcept>
#include <unordered_map>
#include <vector>
@ -27,13 +28,18 @@ namespace simpleengine::gfx {
if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY) {
horizontally_flip_tex_coords();
}
if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_CALCULATE_TANGENT_SPACE) {
calculate_tangents();
}
}
void Model::load_model(std::string path) {
Assimp::Importer importer;
// assimp post processing options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
const aiScene *scene = importer.ReadFile(path, additional_assimp_flags | aiProcess_Triangulate | aiProcess_FlipUVs);
const aiScene *scene = importer.ReadFile(path, additional_assimp_flags | aiProcess_Triangulate | aiProcess_FlipUVs
| aiProcess_CalcTangentSpace);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
@ -63,7 +69,7 @@ namespace simpleengine::gfx {
gfx::Mesh Model::process_mesh(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMesh* mesh, const aiScene* scene) {
std::vector<LitVertex> vertices;
std::vector<unsigned int> indices;
std::vector<unsigned int> indicies;
for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
LitVertex vertex;
@ -93,7 +99,7 @@ namespace simpleengine::gfx {
aiFace face = mesh->mFaces[i];
for (int j = 0; j < face.mNumIndices; j++) {
indices.push_back(face.mIndices[j]);
indicies.push_back(face.mIndices[j]);
}
}
@ -121,8 +127,13 @@ namespace simpleengine::gfx {
// Load normals texture maps
loading_type = aiTextureType_NORMALS;
std::vector<std::shared_ptr<Texture>> normal_maps = load_material_texture(processed_textures, material, loading_type, TextureFlags::TexFlags_NO_COLOR);
if (!normal_maps.empty()) textures.emplace(loading_type, normal_maps);
std::vector<std::shared_ptr<Texture>> normal_maps = load_material_texture(processed_textures, material, loading_type, TextureFlags::TexFlags_RGB);
if (!normal_maps.empty()) {
textures.emplace(loading_type, normal_maps);
// Force calculation of tangent space.
model_processing_flags |= MdlProcFlag_CALCULATE_TANGENT_SPACE;
}
// TODO Handle other types of texture maps
@ -156,7 +167,7 @@ namespace simpleengine::gfx {
}
}
Mesh se_mesh(vertices, indices, mat);
Mesh se_mesh(vertices, indicies, mat);
if (!mesh->HasNormals()) {
se_mesh.calculate_normals();
@ -222,4 +233,50 @@ namespace simpleengine::gfx {
}
}
}
void Model::calculate_tangents() {
for (auto& mesh : meshes) {
// TODO: Find a way to check if normals and UVs are set before calculating tangents
mesh.calculate_tangents();
}
}
/* std::vector<std::pair<simpleengine::Vectorf, simpleengine::Vectorf>> calculate_tangent_space(
std::vector<LitVertex> vertices, std::vector<unsigned int> indices) {
std::vector<std::pair<simpleengine::Vectorf, simpleengine::Vectorf>> tangents;
tangents.resize(vertices.size());
for (int i = 0; i < indices.size(); i += 3) {
int index0 = indices[i];
int index1 = indices[i+1];
int index2 = indices[i+2];
LitVertex& lit_vertex0 = vertices[index0];
LitVertex& lit_vertex1 = vertices[index1];
LitVertex& lit_vertex2 = vertices[index2];
glm::vec3 pos0 = lit_vertex0.position;
glm::vec3 pos1 = lit_vertex1.position;
glm::vec3 pos2 = lit_vertex2.position;
// Edges of the triangle : postion delta
glm::vec3 delta_pos1 = pos1 - pos0;
glm::vec3 delta_pos2 = pos2 - pos0;
// UV delta
glm::vec2 delta_uv1 = lit_vertex1.tex_coord - lit_vertex0.tex_coord;
glm::vec2 delta_uv2 = lit_vertex2.tex_coord - lit_vertex0.tex_coord;
float r = 1.0f / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
glm::vec3 tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y)*r;
glm::vec3 bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x)*r;
tangents[0] = {tangent, bitangent};
tangents[1] = {tangent, bitangent};
tangents[2] = {tangent, bitangent};
}
return tangents;
} */
} // namespace simpleengine::gfx

View File

@ -5,6 +5,7 @@
#include "ecs/component/mesh_component.h"
#include "ecs/component/model_component.h"
#include "vector.h"
#include <algorithm>
#include <assimp/material.h>
@ -60,8 +61,14 @@ namespace simpleengine::gfx {
vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal), false);
vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord), false);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
rendering_mesh.tangent_vbo.buffer(rendering_mesh.tangents.data(), 0, rendering_mesh.tangents.size() * sizeof(Vectorf));
vao.enable_attrib(rendering_mesh.tangent_vbo, 4, 3, GL_FLOAT, sizeof(Vectorf), 0, false);
rendering_mesh.bitangent_vbo.buffer(rendering_mesh.bitangents.data(), 0, rendering_mesh.bitangents.size() * sizeof(Vectorf));
vao.enable_attrib(rendering_mesh.bitangent_vbo, 5, 3, GL_FLOAT, sizeof(Vectorf), 0, false);
vbo.unbind();
vao.unbind();
rendering_mesh.are_buffers_created = true;
@ -125,6 +132,21 @@ namespace simpleengine::gfx {
} else {
shader.set_uniform_int("u_material.has_specular_map", 0, false);
}
// Apply the normal map if it exists
auto normal_maps = material->textures.find(aiTextureType_NORMALS);
if (normal_maps != material->textures.end()) {
auto normal = normal_maps->second.front();
//shader.set_uniform_int("u_material.has_normal_map", 1, false);
shader.set_uniform_int("u_material.normal_map", 2, false);
glActiveTexture(GL_TEXTURE2);
normal->bind();
} else {
//shader.set_uniform_int("u_material.has_normal_map", 0, false);
std::cout << "No normal map for model!" << std::endl;
}
}
mesh.vao.bind();