1

I am using the OpenGL v.3.3. and the full source code example from learnopengl.com. I am able to run the textured model from the tutorial (nanosuit.obj), but when i am trying to load another .obj models without textures, but with .mtl files it shows a dark color. I think the problem is maybe in shaders that i am using to load the models, but i dont know how to redo the shader's code to fix my issue.

here is my main functions i am using to render the model:

 void COpenGLControl::oglInitialize(void)
    {

        static PIXELFORMATDESCRIPTOR pfd =
        {
            sizeof(PIXELFORMATDESCRIPTOR),
            1,
            PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
            PFD_TYPE_RGBA,
            32, // bit depth
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            16, // z-buffer depth
            0, 0, 0, 0, 0, 0, 0,
        };

        // Get device context only once.
        hdc = GetDC()->m_hDC;

        // Pixel format.
        m_nPixelFormat = ChoosePixelFormat(hdc, &pfd);
        SetPixelFormat(hdc, m_nPixelFormat, &pfd);

        // Create the OpenGL Rendering Context.
        hrc = wglCreateContext(hdc);
        wglMakeCurrent(hdc, hrc);

        if (!gladLoadGL())
        {
            AfxMessageBox(L"Error loading glad");
            return;
        }
        // configure global opengl state
        // -----------------------------
        glEnable(GL_DEPTH_TEST);

        // build and compile shaders
        ourShader = Shader("resource/shaders/modelLoading.vs", "resource/shaders/modelLoading.frag");

        // load models
        ourModel = Model("resource/models/car12/LaFerrari.obj");



        // Send draw request
        OnDraw(NULL);
    }

    void COpenGLControl::oglDrawScene(void)
    {

        // render
        // ------
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // don't forget to enable shader before setting uniforms
        ourShader.use();

        // view/projection transformations
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)900 / (float)900, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        ourShader.setMat4("projection", projection);
        ourShader.setMat4("view", view);

        // render the loaded model
        glm::mat4 model = glm::mat4(1.0f);
        model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));
        model = glm::scale(model, glm::vec3(0.010f, 0.010f, 0.010f));
        model = glm::rotate(model, glm::radians(-30.0f), glm::vec3(0.0f, 1.0f, 0.0f));
        model = glm::rotate(model, glm::radians(20.0f), glm::vec3(1.0f, 0.0f, 0.0f));
        ourShader.setMat4("model", model);
        ourModel.Draw(ourShader);
    }

model.cpp

 #include "pch.h"
    #include "model.h"

    Model::Model(void)
    {

    }

    Model::Model(string const& path, bool gamma) 
    {
        loadModel(path);
    }


    void Model::Draw(Shader shader)
    {
        for (unsigned int i = 0; i < meshes.size(); i++)
            meshes[i].Draw(shader);
    }

    void Model::loadModel(string const& path)
    {
        // read file via ASSIMP
        Assimp::Importer importer;
        const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_CalcTangentSpace);
        // check for errors
        if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) // if is Not Zero
        {
            cout << "ERROR::ASSIMP:: " << importer.GetErrorString() << endl;
            return;
        }
        // retrieve the directory path of the filepath
        directory = path.substr(0, path.find_last_of('/'));

        // process ASSIMP's root node recursively
        processNode(scene->mRootNode, scene);
    }

    void Model::processNode(aiNode* node, const aiScene* scene)
    {
        // process each mesh located at the current node
        for (unsigned int i = 0; i < node->mNumMeshes; i++)
        {
            // the node object only contains indices to index the actual objects in the scene. 
            // the scene contains all the data, node is just to keep stuff organized (like relations between nodes).
            aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
            meshes.push_back(processMesh(mesh, scene));
        }
        // after we've processed all of the meshes (if any) we then recursively process each of the children nodes
        for (unsigned int i = 0; i < node->mNumChildren; i++)
        {
            processNode(node->mChildren[i], scene);
        }
    }

    Mesh Model::processMesh(aiMesh* mesh, const aiScene* scene)
    {
        // data to fill
        vector<Vertex> vertices;
        vector<unsigned int> indices;
        vector<Texture> textures;

        // Walk through each of the mesh's vertices
        for (unsigned int i = 0; i < mesh->mNumVertices; i++)
        {
            Vertex vertex;
            glm::vec3 vector; // we declare a placeholder vector since assimp uses its own vector class that doesn't directly convert to glm's vec3 class so we transfer the data to this placeholder glm::vec3 first.

            // positions
            if (!mesh->mVertices == NULL)
            {
                vector.x = mesh->mVertices[i].x;
                vector.y = mesh->mVertices[i].y;
                vector.z = mesh->mVertices[i].z;
                vertex.Position = vector;
            }
            else
            {
                vertex.Position = glm::vec3(0.0f, 0.0f, 0.0f);
                //AfxMessageBox(L"Positions is NULL");
            }

            // normals
            if (!mesh->mNormals == NULL)
            {
                vector.x = mesh->mNormals[i].x;
                vector.y = mesh->mNormals[i].y;
                vector.z = mesh->mNormals[i].z;
                vertex.Normal = vector;
            }
            else
            {
                vertex.Normal = glm::vec3(0.0f, 0.0f, 0.0f);
                //AfxMessageBox(L"Normals is NULL");
            }

            // texture coordinates
            if (mesh->mTextureCoords[0]) // does the mesh contain texture coordinates?
            {
                glm::vec2 vec;
                // a vertex can contain up to 8 different texture coordinates. We thus make the assumption that we won't 
                // use models where a vertex can have multiple texture coordinates so we always take the first set (0).
                vec.x = mesh->mTextureCoords[0][i].x;
                vec.y = mesh->mTextureCoords[0][i].y;
                vertex.TexCoords = vec;
            }
            else
            {
                vertex.TexCoords = glm::vec2(0.0f, 0.0f);
                //AfxMessageBox(L"TextCoords is NULL");
            }

            // tangent
            if (!mesh->mTangents == NULL)
            {
                vector.x = mesh->mTangents[i].x;
                vector.y = mesh->mTangents[i].y;
                vector.z = mesh->mTangents[i].z;
                vertex.Tangent = vector;
            }
            else
            {
                vertex.Tangent = glm::vec3(0.0f, 0.0f, 0.0f);
                //AfxMessageBox(L"TextCoords is NULL");
            }

            // bitangent
            if (!mesh->mBitangents == NULL)
            {
                vector.x = mesh->mBitangents[i].x;
                vector.y = mesh->mBitangents[i].y;
                vector.z = mesh->mBitangents[i].z;
                vertex.Bitangent = vector;
            }
            else
            {
                vertex.Bitangent = glm::vec3(0.0f, 0.0f, 0.0f);
                //AfxMessageBox(L"Bitangent is NULL");
            }

            vertices.push_back(vertex);
        }
        // now wak through each of the mesh's faces (a face is a mesh its triangle) and retrieve the corresponding vertex indices.
        for (unsigned int i = 0; i < mesh->mNumFaces; i++)
        {
            aiFace face = mesh->mFaces[i];
            // retrieve all indices of the face and store them in the indices vector
            for (unsigned int j = 0; j < face.mNumIndices; j++)
                indices.push_back(face.mIndices[j]);
        }
        // process materials
        aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];


        aiColor3D color;
        Material mat;
        // Read mtl file vertex data
        material->Get(AI_MATKEY_COLOR_AMBIENT, color);
        mat.Ka = glm::vec4(color.r, color.g, color.b, 1.0);
        material->Get(AI_MATKEY_COLOR_DIFFUSE, color);
        mat.Kd = glm::vec4(color.r, color.g, color.b, 1.0);
        material->Get(AI_MATKEY_COLOR_SPECULAR, color);
        mat.Ks = glm::vec4(color.r, color.g, color.b, 1.0);

        // we assume a convention for sampler names in the shaders. Each diffuse texture should be named
        // as 'texture_diffuseN' where N is a sequential number ranging from 1 to MAX_SAMPLER_NUMBER. 
        // Same applies to other texture as the following list summarizes:
        // diffuse: texture_diffuseN
        // specular: texture_specularN
        // normal: texture_normalN

        // 1. diffuse maps
        vector<Texture> diffuseMaps = loadMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse");
        textures.insert(textures.end(), diffuseMaps.begin(), diffuseMaps.end());
        // 2. specular maps
        vector<Texture> specularMaps = loadMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular");
        textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
        // 3. normal maps
        std::vector<Texture> normalMaps = loadMaterialTextures(material, aiTextureType_HEIGHT, "texture_normal");
        textures.insert(textures.end(), normalMaps.begin(), normalMaps.end());
        // 4. height maps
        std::vector<Texture> heightMaps = loadMaterialTextures(material, aiTextureType_AMBIENT, "texture_height");
        textures.insert(textures.end(), heightMaps.begin(), heightMaps.end());

        // return a mesh object created from the extracted mesh data
        return Mesh(vertices, indices, textures, mat);
    }

    vector<Texture> Model::loadMaterialTextures(aiMaterial* mat, aiTextureType type, string typeName)
    {
        vector<Texture> textures;
        for (unsigned int i = 0; i < mat->GetTextureCount(type); i++)
        {
            aiString str;
            mat->GetTexture(type, i, &str);
            // check if texture was loaded before and if so, continue to next iteration: skip loading a new texture
            bool skip = false;
            for (unsigned int j = 0; j < textures_loaded.size(); j++)
            {
                if (std::strcmp(textures_loaded[j].path.data(), str.C_Str()) == 0)
                {
                    textures.push_back(textures_loaded[j]);
                    skip = true; // a texture with the same filepath has already been loaded, continue to next one. (optimization)
                    break;
                }
            }
            if (!skip)
            {   // if texture hasn't been loaded already, load it
                Texture texture;
                texture.id = TextureFromFile(str.C_Str(), this->directory, false);
                texture.type = typeName;
                texture.path = str.C_Str();
                textures.push_back(texture);
                textures_loaded.push_back(texture);  // store it as texture loaded for entire model, to ensure we won't unnecesery load duplicate textures.
            }
        }
        return textures;
    }

    unsigned int Model::TextureFromFile(const char* path, const string& directory, bool gamma)
    {
        //string filename = string(path);
        //filename = directory + '/' + filename;

        string filename = string(path);
        if (directory.find(R"(/)") != std::string::npos)
        {
            filename = directory + R"(/)" + filename;
        }
        else if (directory.find(R"(\\)") != std::string::npos)
        {
            filename = directory + R"(\\)" + filename;
        }

        stbi_set_flip_vertically_on_load(false);
        unsigned int textureID;
        glGenTextures(1, &textureID);

        int width, height, nrComponents;
        unsigned char* data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);
        if (data)
        {
            GLenum format;
            if (nrComponents == 1)
                format = GL_RED;
            else if (nrComponents == 3)
                format = GL_RGB;
            else if (nrComponents == 4)
                format = GL_RGBA;

            glBindTexture(GL_TEXTURE_2D, textureID);
            glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
            glGenerateMipmap(GL_TEXTURE_2D);

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

            stbi_image_free(data);

        }
        else
        {
            std::cout << "Texture failed to load at path: " << path << std::endl;
            stbi_image_free(data);
        }
        return textureID;

    }

Shaders: frag

#version 330 core
in vec2 TexCoords;
out vec4 color;

uniform sampler2D texture_diffuse;
void main( )
{
    color = vec4( texture( texture_diffuse, TexCoords ));
}

vs

#version 330 core
layout ( location = 0 ) in vec3 position;
layout ( location = 1 ) in vec3 normal;
layout ( location = 2 ) in vec2 texCoords;

out vec2 TexCoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main( )
{
    gl_Position = projection * view * model * vec4( position, 1.0f );
    TexCoords = texCoords;
}

Also attached pictures (dark picture is the result of my program, color picture is AbViewer free obj viewer result). Please help me to find a solution, what am i doing wrong?

enter image description here

enter image description here

al072
  • 79
  • 2
  • 14
  • Have you tried running you program through renderdoc to see if there are any warnings or errors? You can also check VS output to see if they make sense, check if all the textures/samplers/buffers are bound, etc – Borgleader Jun 12 '20 at 19:24
  • No i didnt try renderdoc, never used it before!!! There is no errors in debug mode in VS – al072 Jun 12 '20 at 19:52
  • If you believe the issue to be in your shader code, you should probably show that shader code. – IInspectable Jun 12 '20 at 20:08
  • Supposedly, GLSL program expects texture to be bound for rendering. Two approaches can be used in this case - modify GLSL code to (optionally) ignore texture fetches, or to create and bind dummy white textures for missing texture maps. – gkv311 Jun 12 '20 at 20:09
  • I am sorry, there is not enough space to post it one message, sure i will post it – al072 Jun 12 '20 at 20:10
  • What i need to change in my code or shaders to load the 3d model without textures?, only using .obj and .mtl – al072 Jun 12 '20 at 20:45
  • you would need to pass the color from mtl or obj to shaders on CPU side. Read it in Vertex and pass to fragment so it get interpolated in fragment compute lighting and mix with color...see: [complete GL+GLSL+VAO/VBO C++ example](https://stackoverflow.com/a/31913542/2521214) and output that instead of yours `texture( texture_diffuse, TexCoords )` – Spektre Jun 13 '20 at 09:25

1 Answers1

0

You need to bind the current texture before your draw-calls. You have no code to set any color information for your vertices for your vertexbuffer.

So without having any lights, no color, and no bonded texture your model will be black :-).

KimKulling
  • 2,654
  • 1
  • 15
  • 26