Add specular and normal mapping
authorLuke Lau <luke_lau@icloud.com>
Fri, 2 Nov 2018 11:32:54 +0000 (11:32 +0000)
committerLuke Lau <luke_lau@icloud.com>
Fri, 2 Nov 2018 11:32:54 +0000 (11:32 +0000)
main.cpp
material.cpp
material.hpp
model.cpp
normalfrag.glsl
texturefrag.glsl
texturevertex.glsl [new file with mode: 0644]
vertex.glsl

index 727c5ff..f7b2f85 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -23,7 +23,7 @@ using namespace std;
 
 GLuint pyramidVao, lightVao, teapotVao;
 GLuint gradientProgId, plainProgId, normalProgId, solidProgId, textureProgId;
-glm::vec3 camPos   = glm::vec3(0.0f, 0.0f,  -10.0f);
+glm::vec3 camPos   = glm::vec3(0.0f, 0.0f,  0.0f);
 glm::vec3 camFront = glm::vec3(0.0f, 0.0f, 1.0f);
 glm::vec3 camUp    = glm::vec3(0.0f, 1.0f,  0.0f);
 float yaw = 1.57, pitch = 0;
@@ -48,10 +48,10 @@ void setLightColorAndPos(GLuint progId, glm::vec3 lightPos, glm::vec4 lightColor
        GLuint lightColorLoc = glGetUniformLocation(progId, "lightColor");
        glUniform4fv(lightColorLoc, 1, glm::value_ptr(lightColor));
 
-       GLuint lightPosLoc = glGetUniformLocation(progId, "lightPos");
+       GLuint lightPosLoc = glGetUniformLocation(progId, "vLightPos");
        glUniform3fv(lightPosLoc, 1, glm::value_ptr(lightPos));
 
-       GLuint viewPosLoc = glGetUniformLocation(progId, "viewPos");
+       GLuint viewPosLoc = glGetUniformLocation(progId, "vViewPos");
        glUniform3fv(viewPosLoc, 1, glm::value_ptr(camPos));
 }
 
@@ -156,25 +156,12 @@ void display() {
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
        float d = (float)glutGet(GLUT_ELAPSED_TIME) * 0.001f;
 
-       /* glm::vec3 lightPos = glm::vec3(sin(d) * 10, 0, cos(d) * 10); */
-       glm::vec3 lightPos = glm::vec3(0, 10, 10);
+       /* glm::vec3 lightPos = glm::vec3(sin(d / 10.f) * 10, 0, cos(d / 10.f) * 10); */
+       glm::vec3 lightPos = chestPos;
        glm::vec4 lightColor(1, 1, 1, 1);
 
-       glViewport(0, 0, WIDTH, HEIGHT);
-       /* drawPyramids(d, lightPos, lightColor); */
        drawLight(d, lightPos, lightColor);
 
-       glUseProgram(solidProgId);
-       setProjectionAndViewUniforms(solidProgId);
-       setLightColorAndPos(solidProgId, lightPos, lightColor);
-       
-       GLuint colorLoc = glGetUniformLocation(solidProgId, "color");
-       glm::vec4 c(1);
-       glUniform4fv(colorLoc, 1, glm::value_ptr(c));
-
-       monkeyHead->getRoot()->model = glm::translate(glm::mat4(1), glm::vec3(2, 0, 0));
-       monkeyHead->draw();
-
        glUseProgram(textureProgId);
        setProjectionAndViewUniforms(textureProgId);
        setLightColorAndPos(textureProgId, lightPos, lightColor);
@@ -187,34 +174,17 @@ void display() {
        /* Model::Node *jewels = chest->find("jewels"); */
        /* jewels->model = glm::scale(glm::mat4(1), glm::vec3((sin(d) + 1.2f) / 2.f)); */
 
-       Model::Node *lock = chest->find("lock");
-       lock->model = glm::translate(glm::mat4(1), glm::vec3(0.22610, 3.36478, -0.75649));
-       lock->model = glm::rotate(lock->model, (d / 2.5f), glm::vec3(0, 1, 0.4));
-       lock->model = glm::translate(lock->model, -glm::vec3(0.22610, 3.36478, -0.75649));
+       /* Model::Node *lock = chest->find("lock"); */
+       /* lock->model = glm::translate(glm::mat4(1), glm::vec3(0.22610, 3.36478, -0.75649)); */
+       /* lock->model = glm::rotate(lock->model, (d / 2.5f), glm::vec3(0, 1, 0.4)); */
+       /* lock->model = glm::translate(lock->model, -glm::vec3(0.22610, 3.36478, -0.75649)); */
 
-       Model::Node *key = chest->find("key");
-       key->model = glm::translate(glm::mat4(1), glm::vec3(0, 0, sin(d)));
+       /* Model::Node *key = chest->find("key"); */
+       /* key->model = glm::translate(glm::mat4(1), glm::vec3(0, 0, sin(d))); */
        
-       chest->getRoot()->model = glm::translate(glm::mat4(1), chestPos);
+       /* chest->getRoot()->model = glm::translate(glm::mat4(1), chestPos); */
        chest->draw();
 
-       /* drawTeapot(teapotCamera, false, d, lightPos, lightColor); */
-
-       /* glClear(GL_DEPTH_BUFFER_BIT); */
-
-       /* glViewport(0, 0, WIDTH / 4, HEIGHT / 4); */
-       /* drawTeapot(teapotOrtho, false, d, lightPos, lightColor); */
-
-       /* glViewport(WIDTH / 4, 0, WIDTH / 4, HEIGHT / 4); */
-       /* drawTeapot(teapotOrtho, true, d, lightPos, lightColor); */
-
-       /* glViewport(WIDTH / 4 * 2, 0, WIDTH / 4, HEIGHT / 4); */
-       /* drawTeapot(teapotPerspStatic, false, d, lightPos, lightColor); */
-
-       /* glViewport(WIDTH / 4 * 3, 0, WIDTH / 4, HEIGHT / 4); */
-       /* drawTeapot(teapotPerspAnimated, false, d, lightPos, lightColor); */
-
-
        glutSwapBuffers();
 }
 
@@ -511,7 +481,7 @@ void init() {
        glUseProgram(solidProgId);
        validateProgram(solidProgId);
 
-       textureProgId = compileShaders((char*)"vertex.glsl", (char*)"texturefrag.glsl");
+       textureProgId = compileShaders((char*)"texturevertex.glsl", (char*)"texturefrag.glsl");
        glUseProgram(textureProgId);
        validateProgram(textureProgId);
 
@@ -557,6 +527,8 @@ void timer(int _) {
        if (keyStates['k']) chestPos.z -= 0.1f;
        if (keyStates['h']) chestPos.x -= 0.1f;
        if (keyStates['l']) chestPos.x += 0.1f;
+       if (keyStates['m']) chestPos.y -= 0.1f;
+       if (keyStates['n']) chestPos.y += 0.1f;
 
        camPos.x += xSpeed * sin(yaw) + zSpeed * cos(yaw);
        camPos.y += ySpeed;
index 470f1a2..06bf6fd 100644 (file)
@@ -1,12 +1,23 @@
 #include "material.hpp"
 #include <iostream>
+#include <fstream>
 #include <CoreGraphics/CoreGraphics.h>
 
 Material::Material(const aiMaterial &ai, GLuint progId): progId(progId) {
        if (ai.GetTextureCount(aiTextureType_DIFFUSE) > 0) {
                aiString path;
                ai.GetTexture(aiTextureType_DIFFUSE, 0, &path);
-               texture = new Texture(std::string(path.C_Str()));
+               diffuseMap = new Texture(std::string(path.C_Str()));
+       }
+       if (ai.GetTextureCount(aiTextureType_SPECULAR) > 0) {
+               aiString path;
+               ai.GetTexture(aiTextureType_SPECULAR, 0, &path);
+               specularMap = new Texture(std::string(path.C_Str()));
+       }
+       if (ai.GetTextureCount(aiTextureType_NORMALS) > 0) {
+               aiString path;
+               ai.GetTexture(aiTextureType_NORMALS, 0, &path);
+               normalMap = new Texture(std::string(path.C_Str()));
        }
        ai.Get(AI_MATKEY_SHININESS, shininess);
        ai.Get(AI_MATKEY_COLOR_AMBIENT, ambient);
@@ -15,8 +26,20 @@ Material::Material(const aiMaterial &ai, GLuint progId): progId(progId) {
 }
 
 Material::Texture::Texture(const std::string &path) {
+
        auto provider = CGDataProviderCreateWithFilename(path.c_str());
-       auto ref = CGImageCreateWithJPEGDataProvider(provider, nullptr, false, kCGRenderingIntentDefault);
+       std::ifstream file(path);
+       long magic;
+       file.read((char*)&magic, 8);
+       file.close();
+
+       CGImageRef ref;
+
+       if (magic == 0x0a1a0a0d474e5089) // png magic number
+               ref = CGImageCreateWithPNGDataProvider(provider, nullptr, false, kCGRenderingIntentDefault);
+       else
+               ref = CGImageCreateWithJPEGDataProvider(provider, nullptr, false, kCGRenderingIntentDefault);
+       
        auto dataRef = CGDataProviderCopyData(CGImageGetDataProvider(ref));
        auto img = (unsigned char*) CFDataGetBytePtr(dataRef);
 
@@ -35,8 +58,23 @@ void Material::bind() const {
        glUniform3f(glGetUniformLocation(progId, "material.diffuse"), diffuse.r, diffuse.g, diffuse.b);
        glUniform3f(glGetUniformLocation(progId, "material.specular"), specular.r, specular.g, specular.b);
        glUniform1f(glGetUniformLocation(progId, "material.shininess"), shininess);
-       glUniform1i(glGetUniformLocation(progId, "material.hasTexture"), texture != nullptr);
+       glUniform1i(glGetUniformLocation(progId, "material.hasTexture"), diffuseMap != nullptr);
+       glUniform1i(glGetUniformLocation(progId, "material.hasSpecularMap"), specularMap != nullptr);
+       glUniform1i(glGetUniformLocation(progId, "material.hasNormalMap"), normalMap != nullptr);
 
-       if (texture) 
-               glBindTexture(GL_TEXTURE_2D, texture->texId);
+       if (diffuseMap) {
+               glUniform1i(glGetUniformLocation(progId, "material.diffuseMap"), 0);
+               glActiveTexture(GL_TEXTURE0);
+               glBindTexture(GL_TEXTURE_2D, diffuseMap->texId);
+       }
+       if (specularMap) {
+               glUniform1i(glGetUniformLocation(progId, "material.specularMap"), 1);
+               glActiveTexture(GL_TEXTURE1);
+               glBindTexture(GL_TEXTURE_2D, specularMap->texId);
+       }
+       if (normalMap) {
+               glUniform1i(glGetUniformLocation(progId, "material.normalMap"), 2);
+               glActiveTexture(GL_TEXTURE2);
+               glBindTexture(GL_TEXTURE_2D, normalMap->texId);
+       }
 }
index 968ea36..c28bcb4 100644 (file)
@@ -19,6 +19,8 @@ class Material {
                        Texture(const std::string &path);
                        GLuint texId;
                };
-               Texture *texture = nullptr;
+               Texture *diffuseMap = nullptr;
+               Texture *specularMap = nullptr;
+               Texture *normalMap = nullptr;
 };
 
index 4c5e2d3..8046e29 100644 (file)
--- a/model.cpp
+++ b/model.cpp
@@ -5,7 +5,7 @@
 
 Model::Mesh::Mesh(const aiMesh *aiMesh, GLuint progId) {
 
-       std::vector<glm::vec3> vertices, normals;
+       std::vector<glm::vec3> vertices, normals, tangents, bitangents;
        std::vector<glm::vec2> texCoords;
 
        for (int i = 0; i < aiMesh->mNumVertices; i++) {
@@ -14,11 +14,20 @@ Model::Mesh::Mesh(const aiMesh *aiMesh, GLuint progId) {
                        vertices.push_back(glm::vec3(v.x, v.y, v.z));
                }
                if (aiMesh->HasNormals()) {
-                       const aiVector3D v = aiMesh->mNormals[i];
+                       aiVector3D v = aiMesh->mNormals[i];
                        normals.push_back(glm::vec3(v.x, v.y, v.z));
                } else {
                        normals.push_back(glm::vec3(0));
                }
+               if (aiMesh->HasTangentsAndBitangents()) {
+                       aiVector3D t = aiMesh->mTangents[i];
+                       tangents.push_back(glm::vec3(t.x, t.y, t.z));
+                       aiVector3D b = aiMesh->mBitangents[i];
+                       bitangents.push_back(glm::vec3(b.x, b.y, b.z));
+               } else {
+                       tangents.push_back(glm::vec3(0));
+                       bitangents.push_back(glm::vec3(0));
+               }
                // check for texture coord set 0
                if (aiMesh->HasTextureCoords(0)) {
                        const aiVector3D v = aiMesh->mTextureCoords[0][i];
@@ -45,29 +54,41 @@ Model::Mesh::Mesh(const aiMesh *aiMesh, GLuint progId) {
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
        
-       GLuint vbos[4];
-       glGenBuffers(4, vbos);
+       GLuint vbos[6];
+       glGenBuffers(6, vbos);
        GLuint vertexVbo = vbos[0], normalVbo = vbos[1], texCoordVbo = vbos[2], indicesVbo = vbos[3];
+       GLuint tangentVbo = vbos[4], bitangentVbo = vbos[5];
        
        GLuint posLoc = glGetAttribLocation(progId, "vPosition");
-       GLuint normalLoc = glGetAttribLocation(progId, "vNormal");
-       GLuint texCoordLoc = glGetAttribLocation(progId, "vTexCoord");
-       
        glBindBuffer(GL_ARRAY_BUFFER, vertexVbo);
        glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);
        glEnableVertexAttribArray(posLoc);
        glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
        
+       GLuint normalLoc = glGetAttribLocation(progId, "vNormal");
        glBindBuffer(GL_ARRAY_BUFFER, normalVbo);
        glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(glm::vec3), &normals[0], GL_STATIC_DRAW);
        glEnableVertexAttribArray(normalLoc);
        glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
 
+       GLuint texCoordLoc = glGetAttribLocation(progId, "vTexCoord");
        glBindBuffer(GL_ARRAY_BUFFER, texCoordVbo);
        glBufferData(GL_ARRAY_BUFFER, texCoords.size() * sizeof(glm::vec2), &texCoords[0], GL_STATIC_DRAW);
        glEnableVertexAttribArray(texCoordLoc);
        glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
 
+       GLuint tangentLoc = glGetAttribLocation(progId, "tangent");
+       glBindBuffer(GL_ARRAY_BUFFER, tangentVbo);
+       glBufferData(GL_ARRAY_BUFFER, tangents.size() * sizeof(glm::vec3), &tangents[0], GL_STATIC_DRAW);
+       glEnableVertexAttribArray(tangentLoc);
+       glVertexAttribPointer(tangentLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
+
+       GLuint bitangentLoc = glGetAttribLocation(progId, "bitangent");
+       glBindBuffer(GL_ARRAY_BUFFER, bitangentVbo);
+       glBufferData(GL_ARRAY_BUFFER, bitangents.size() * sizeof(glm::vec3), &bitangents[0], GL_STATIC_DRAW);
+       glEnableVertexAttribArray(bitangentLoc);
+       glVertexAttribPointer(bitangentLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
+
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesVbo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), &indices[0], GL_STATIC_DRAW);
 };
@@ -113,8 +134,7 @@ void Model::Node::draw(     const std::vector<Mesh> &meshes,
 
 Model::Model(const std::string &path, GLuint progId): progId(progId) {
        const aiScene *scene = importer.ReadFile(path, 
-                       aiProcess_Triangulate |
-                       aiProcess_GenNormals);
+                       aiProcess_Triangulate | aiProcess_CalcTangentSpace | aiProcess_GenNormals);
        if (!scene) {
                std::cerr << importer.GetErrorString() << std::endl;
                exit(1);
index 1683592..f78fd81 100644 (file)
@@ -19,6 +19,6 @@ void main() {
 
        vec4 lighting = (ambient + diffuse + specular) * lightColor;
 
-       FragColor = lighting * vec4(normalEye, 1);
+       FragColor = lighting * vec4((normal + 1) / 2, 1);
 }
 
index 347cc01..83cce42 100644 (file)
@@ -3,37 +3,65 @@
 struct Material {
        vec3 ambient;
        vec3 diffuse;
+       sampler2D diffuseMap;
        vec3 specular;
+       sampler2D specularMap;
        float shininess;
+       
        bool hasTexture;
+       bool hasSpecularMap;
+
+       sampler2D normalMap;
+       bool hasNormalMap;
 };
 
-in vec3 normal;
-in vec3 fragPos;
+in vec3 defNormal;
 in vec2 texCoord;
 
-uniform vec3 lightPos;
-uniform vec3 viewPos;
+// These are all in tangent space
+in vec3 lightPos;
+in vec3 viewPos;
+in vec3 fragPos;
+
 uniform vec4 lightColor;
 
 uniform Material material;
-uniform sampler2D tex;
 
 out vec4 FragColor;
 
 void main() {
-       vec3 lightDir = normalize(fragPos - lightPos);
-       vec3 diffuse = max(0, dot(-normal, lightDir)) * material.diffuse;
 
-       vec3 viewDir = normalize(fragPos - viewPos);
-       vec3 reflectDir = reflect(-lightDir, normal);
-       vec3 specular = pow(max(0, dot(viewDir, reflectDir)), material.shininess) * material.specular;
+       vec3 normal;
+       if (material.hasNormalMap) {
+               normal = texture(material.normalMap, texCoord).rgb;
+               normal = normalize(normal * 2 - 1);
+       } else {
+               normal = defNormal;
+       }
 
-       vec4 lighting = vec4(material.ambient + diffuse + specular, 1) * lightColor;
+       vec3 lightDir = normalize(lightPos - fragPos);
        
+       vec4 diffTex;
        if (material.hasTexture)
-               FragColor = lighting * texture(tex, texCoord);
+               diffTex = texture(material.diffuseMap, texCoord);
        else
+               diffTex = vec4(1);
+       vec4 diffuse = vec4(max(0, dot(lightDir, normal)) * material.diffuse, 1) * diffTex;
+
+       vec3 viewDir = normalize(viewPos - fragPos);
+       vec3 reflectDir = reflect(-lightDir, normal);
+
+
+       vec4 specTex;
+       if (material.hasSpecularMap)
+               specTex = texture(material.specularMap, texCoord);
+       else
+               specTex = vec4(1);
+
+       vec4 specular = vec4(pow(max(0, dot(viewDir, reflectDir)), material.shininess) * material.specular, 1) * specTex;
+
+       vec4 lighting = (vec4(material.ambient, 1) + diffuse + specular) * lightColor;
+
        FragColor = lighting;
 }
 
diff --git a/texturevertex.glsl b/texturevertex.glsl
new file mode 100644 (file)
index 0000000..d8c97f4
--- /dev/null
@@ -0,0 +1,61 @@
+#version 330
+in vec3 vPosition;
+in vec4 vColor;
+in vec3 vNormal;
+in vec3 tangent;
+in vec3 bitangent;
+in vec2 vTexCoord;
+uniform mat4 model;
+uniform mat4 view;
+uniform mat4 projection;
+out vec3 defNormal;
+out vec2 texCoord;
+
+uniform vec3 vLightPos;
+uniform vec3 vViewPos;
+
+out vec3 lightPos;
+out vec3 viewPos;
+out vec3 fragPos;
+
+struct Material {
+       vec3 ambient;
+       vec3 diffuse;
+       sampler2D diffuseMap;
+       vec3 specular;
+       sampler2D specularMap;
+       float shininess;
+       
+       bool hasTexture;
+       bool hasSpecularMap;
+
+       sampler2D normalMap;
+       bool hasNormalMap;
+};
+
+uniform Material material;
+
+
+void main() {
+       vec4 pos = model * vec4(vPosition, 1.f);
+       defNormal = mat3(transpose(inverse(model))) * vNormal;
+       texCoord = vTexCoord;
+
+       //tangent space stuff
+       if (material.hasNormalMap) {
+               vec3 T = normalize(vec3(model * vec4(tangent,   0.0)));
+               vec3 B = normalize(vec3(model * vec4(bitangent, 0.0)));
+               vec3 N = normalize(vec3(model * vec4(vNormal,    0.0)));
+               // convert TBN to world->tangent space
+               mat3 TBN = transpose(mat3(T, B, N));
+               lightPos = TBN * vLightPos;
+               viewPos = TBN * vViewPos;
+               fragPos = TBN * vec3(pos);
+       } else {
+               lightPos = vLightPos;
+               viewPos = vViewPos;
+               fragPos = vec3(pos);
+       }
+       
+       gl_Position = projection * view * pos;
+}
index f886c9b..341b26a 100644 (file)
@@ -17,7 +17,7 @@ void main() {
        gl_Position = projection * view * pos;
        fragPos = vec3(pos);
        color = vColor;
-       normal = vNormal;
+       normal = mat3(transpose(inverse(model))) * vNormal;
        normalEye = vec3(view * vec4(normal, 0.0));
        texCoord = vTexCoord;
 }