c++opengltransformationglm-math3d-model

OpenGL - Moving Object using Modelmatrix


I am working on an OpenGL project. In this project, I would like to simulate a 3D chess game in c++. Of course, the pieces should be movable at will. The chessboard and pieces are already displayed with textures included.

I have been working on transforming the pieces. In my model class, all important parameters such as vertices, indices, numvertices, numindices and more are read from the respective model. This happens in the constructor of this class.

I also create an identity matrix (glm::mat4(1.0f)) for each model in the constructor of the model class.

After all the parameters have been read, I push them into a vector<Mesh*>.

In the mesh class, the vertices and indices are first loaded into the vertex buffer/index buffer in the constructor.

The location of the uniform variable in the vertex shader for my model matrix is also calculated in the constructor.

In the render function of the mesh class, the vertex buffer/index buffer is bound and all uniforms are set. At the end of the function, I call glDrawElements.

In the vertex shader, I multiply the model matrix, the viewProjectionMatrix, and vec4(a_position, 1.0f) into gl_Position. If the identity matrix in the constructor of the model class is glm::mat4(1.0f), the model is displayed normally.

But if I transform, scale or rotate the matrix in the constructor for testing purposes, nothing is displayed anymore. Did I make a mistake in the way I'm doing it or am I on the right track?

#pragma once
#include <vector>
#include <fstream>

#include "libs/glm/glm.hpp"
#include "shader.h"
#include "vertexbuffer.h"
#include "indexbuffer.h"
#include "mesh.h"
#include <cassert>


class Model {

public:
    Model(const char* filename, Shader* shader) {

        this->shader = shader;
        modelmatrix = glm::mat4(1.0f); 
        //modelmatrix = glm::scale(modelmatrix, glm::vec3(10));
        std::ifstream input = std::ifstream(filename, std::ios::in | std::ios::binary); 
        uint64 numMeshes;
        uint64 numMaterials;

        
        input.read((char*)&numMaterials, sizeof(uint64));
        for (uint64 i = 0; i < numMaterials; i++) {
            Material material = {};
            input.read((char*)&material, sizeof(BMFMaterial)); 

            
            uint64 diffuseMapNameLenght = 0;
            input.read((char*)&diffuseMapNameLenght, sizeof(uint64)); 
            std::string diffuseMapName(diffuseMapNameLenght, '\0'); 
            input.read((char*)&diffuseMapName[0], diffuseMapNameLenght); 

            uint64 normalMapNameLenght = 0;
            input.read((char*)&normalMapNameLenght, sizeof(uint64)); 
            std::string normalMapName(normalMapNameLenght, '\0'); 
            input.read((char*)&normalMapName[0], normalMapNameLenght); 

            if (diffuseMapNameLenght > 0) {
                std::cout << filename << " successfully loaded" << std::endl;
            }
            else {
                std::cout << filename << " failed loading" << std::endl;
            }

            int32 textureWidth = 0;
            int32 textureHeight = 0;
            int32 bitsPerPixel = 0;
            glGenTextures(2, &material.diffuseMap); 
            stbi_set_flip_vertically_on_load(true);

            auto textureBuffer = stbi_load(diffuseMapName.c_str(), &textureWidth, &textureHeight, &bitsPerPixel, 4); //Map geladen und in Buffer drinnen      
            glBindTexture(GL_TEXTURE_2D, material.diffuseMap);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer); //Texture in Grafikkarte geladen

           
            if (textureBuffer) {
                stbi_image_free(textureBuffer);
            }

            auto textureBuffer2 = stbi_load(normalMapName.c_str(), &textureWidth, &textureHeight, &bitsPerPixel, 4); //Map geladen und in Buffer drinnen      
            glBindTexture(GL_TEXTURE_2D, material.normalMap);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureBuffer2); //Texture in Grafikkarte geladen

            
            if (textureBuffer2) {
                stbi_image_free(textureBuffer2);
            }

            glBindTexture(GL_TEXTURE_2D, 0); 
            materials.push_back(material); 
        }


        input.read((char*)&numMeshes, sizeof(uint64)); 
        for (uint64 i = 0; i < numMeshes; i++) {

            input.read((char*)&materialIndex, sizeof(uint64)); 
            input.read((char*)&numVertices, sizeof(uint64)); 
            input.read((char*)&numIndices, sizeof(uint64)); 


            for (uint64 i = 0; i < numVertices; i++) {
                Vertex vertex;
                input.read((char*)&vertex.position.x, sizeof(float));
                input.read((char*)&vertex.position.y, sizeof(float));
                input.read((char*)&vertex.position.z, sizeof(float));
                input.read((char*)&vertex.normal.x, sizeof(float));
                input.read((char*)&vertex.normal.y, sizeof(float));
                input.read((char*)&vertex.normal.z, sizeof(float));
                input.read((char*)&vertex.textureCoord.x, sizeof(float));
                input.read((char*)&vertex.textureCoord.y, sizeof(float));

                vertices.push_back(vertex);
            }
            for (uint64 i = 0; i < numIndices; i++) {
                uint32 index;
                input.read((char*)&index, sizeof(uint32));
                indices.push_back(index);
            }

            Mesh* mesh = new Mesh(vertices, numVertices, indices, numIndices, materials[materialIndex], shader, modelmatrix); //Mesh laden 
            meshes.push_back(mesh);
        }

    }

    
    void render() {

       
        for (Mesh* mesh : meshes) {
            mesh->render();
        }

    }

    ~Model() {
        for (Mesh* mesh : meshes) {
            delete mesh;
        }
    }

private:
    std::vector<Mesh*> meshes; 
    std::vector<Material> materials; 

    glm::mat4 modelmatrix;
    std::vector<Vertex> vertices;
    uint64 numVertices = 0;
    std::vector<uint32> indices;
    uint64 numIndices = 0;
    uint64 materialIndex = 0;
    Shader* shader;
};
#pragma once
#include <iostream>
#include <vector>
#include <fstream>
#include "libs/glm/mat4x4.hpp"
#include "libs/glm/glm.hpp"
#include "shader.h"
#include "vertexbuffer.h"
#include "indexbuffer.h"
#include "libs/stb_image.h"

struct BMFMaterial {
    glm::vec3 diffuse;
    glm::vec3 specular;
    glm::vec3 emissive;
    float shininess;
};

struct Material {       
    BMFMaterial material;
    GLuint diffuseMap; 
    GLuint normalMap;
};

class Mesh {
        
public:
    
    Mesh(std::vector<Vertex>& vertices, uint64 numVertices, std::vector<uint32>&indices, uint64 numIndices, Material material, Shader* shader, glm::mat4 modelmatrix) {
        this->material = material;
        this->shader = shader;
        this->numIndices = numIndices;
        this->modelmatrix = modelmatrix;

       
        vertexBuffer = new VertexBuffer(vertices.data(), numVertices);
        indexBuffer = new IndexBuffer(indices.data(), numIndices, sizeof(indices[0]));

        
        diffuseLocation = glGetUniformLocation(shader->getShaderID(), "u_material.diffuse");
        specularLocation = glGetUniformLocation(shader->getShaderID(), "u_material.specular");
        emissiveLocation = glGetUniformLocation(shader->getShaderID(), "u_material.emissive");
        shininessLocation = glGetUniformLocation(shader->getShaderID(), "u_material.shininess");
        diffuseMapLocation = glGetUniformLocation(shader->getShaderID(), "u_diffuse_map");
        modelmatrixlocation = glGetUniformLocation(shader->getShaderID(), "u_modelmatrix");
    }

    ~Mesh() { 
        delete vertexBuffer;
        delete indexBuffer;
    }

    inline void render() {
        std::cout << glm::to_string(modelmatrix) << std::endl;
        vertexBuffer->bind();
        indexBuffer->bind();
        
        glUniformMatrix4fv(modelmatrixlocation, 1, GL_FALSE, &modelmatrix[0][0]);
        glUniform3fv(diffuseLocation, 1, (float*)&material.material.diffuse[0]);
        glUniform3fv(specularLocation, 1, (float*)&material.material.specular[0]);
        glUniform3fv(emissiveLocation, 1, (float*)&material.material.emissive[0]);
        glUniform1f(shininessLocation, material.material.shininess);
        glBindTexture(GL_TEXTURE_2D, material.diffuseMap);
        glUniform1i(diffuseMapLocation, 0); 
       glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, 0); 

    }
private:
    VertexBuffer* vertexBuffer;
    IndexBuffer* indexBuffer;
    Shader* shader;
    Material material;
    uint64 numIndices = 0;
    glm::mat4 modelmatrix;
    
    int diffuseLocation; 
    int specularLocation;
    int emissiveLocation;
    int shininessLocation;
    int diffuseMapLocation;
    int modelmatrixlocation;
};
    while (!close) { //GAMELOOP
        camera.update();
            
            //model = glm::rotate(model, glm::radians(rotation = 1.0f), glm::vec3(1.0f, 0.0f, 0.0f));
            modelViewProj = camera.getViewProj();//*model //Pro Frame jedes mal neu berechnen
            glm::mat4 modelView = camera.getView(); //*model
            glm::mat4 invModelView = glm::transpose(glm::inverse(modelView));
    
            glm::vec4 sunDirectionCamera = glm::transpose(glm::inverse(camera.getView())) * glm::vec4(sunDirection, 1.0f); //sunDirection ist abhänging von Kamera, weil Licht wird im viewspace berechnet
            glUniform3fv(directionLocationDirection, 1, (float*)&sunDirectionCamera[0]); //aktivieren
            glm::mat4 pointLightMatrix = glm::mat4(1.0f); //Für Matrixmultiplikation weil der Punkt nicht immer am gleichen Punkt bleiben soll
            pointLightPosition = pointLightPosition * pointLightMatrix; //Position ändert sich, rotiert nun um y-Achse
            glm::vec3 transformedPointLightPosition = (glm::vec3)(camera.getView() * pointLightPosition); //Transformierte Position im Viewspace 
            glUniform3fv(positionLocationPoint, 1, (float*)&transformedPointLightPosition[0]);
    
            glUniformMatrix4fv(modelViewProjMatrixLocation, 1, GL_FALSE, &modelViewProj[0][0]); //Matrix setzen/aktivieren
            glUniformMatrix4fv(modelViewLocation, 1, GL_FALSE, &modelView[0][0]);
            glUniformMatrix4fv(invModelViewLocation, 1, GL_FALSE, &invModelView[0][0]);
            
            //Brett und Figuren rendern/zeichnen
            whitepawnA2.render();
            board.render();
            
            /*
            whitebishopright.render();
            whitebishopleft.render();
            whiteking.render();
            whiteknightleft.render();
            whiteknightright.render();
            whiterookleft.render();
            whiterookright.render();
            whitequeen.render();
            whitepawnH2.render();
            whitepawnG2.render();
            whitepawnF2.render();
            whitepawnE2.render();
            whitepawnD2.render();
            whitepawnC2.render();
            whitepawnB2.render();
    
            blackknightleft.render();
            blackknightright.render();
            blackking.render();
            blackbishopleft.render();
            blackbishopright.render();
            blackqueen.render();
            blackrookleft.render();
            blackrookright.render();
            blackpawnA.render();
            blackpawnB.render();
            blackpawnC.render();
            blackpawnD.render();
            blackpawnE.render();
            blackpawnF.render();
            blackpawnG.render();
            blackpawnH.render();
    */
            SDL_GL_SwapWindow(window); //double buffer aktivieren
    
            //FPS COUNTER
            uint64 endCounter = SDL_GetPerformanceCounter();
            uint64 counterElapsed = endCounter - lastCounter;
            delta = ((float32)counterElapsed) / (float32)perfCounterFrequency; //Zeit die seit letztem Frame vergangen ist
            uint32 FPS = (uint32)((float32)perfCounterFrequency / (float32)counterElapsed);
            //std::cout << FPS << std::endl;
            lastCounter = endCounter;
        }
    return 0;
    }
    #include "shader.h"
    #include <fstream>
    #include <iostream>
    
    
    Shader::Shader(const char* vertexShaderFilename, const char* fragmentShaderFilename) {
        shaderID = createShader(vertexShaderFilename, fragmentShaderFilename);
    }
    
        Shader::~Shader() {
            glDeleteProgram(shaderID);
        }
    
        void Shader::bind() {
            glUseProgram(shaderID);
        }
    
        void Shader::unbind() {
            glUseProgram(0);
        }
    
        GLuint Shader::getShaderID() {
            return shaderID;
        }
    
        GLuint Shader::compile(std::string shaderSource, GLenum type) { //Shadersourcecode so kompiliert werden
            GLuint id = glCreateShader(type);
            const char* src = shaderSource.c_str();
            glShaderSource(id, 1, &src, 0);
            glCompileShader(id);
    
            int result;
            glGetShaderiv(id, GL_COMPILE_STATUS, &result);
            if (result != GL_TRUE) {
                int length = 0;
                glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
                char* message = new char[length];
                glGetShaderInfoLog(id, length, &length, message);
                std::cout << "Shader compilation error: " << message << std::endl;
                delete[] message;
                return 0;
            }
            return id;
            
        }
    
        std::string Shader::parse(const char* filename) {
            FILE* file;
    #ifdef _WIN32
            if (fopen_s(&file, filename, "rb") != 0) {
                std::cout << "File " << filename << " not found" << std::endl;
                return "";
            }
    #else
            file = fopen(filename, "rb");
            if (file == nullptr) {
                std::cout << "File " << filename << " not found" << std::endl;
                return "";
            }
    #endif
            std::string content; //Dateininhalt
            fseek(file, 0, SEEK_END); //ans ende der datei gehen
            size_t filesize = ftell(file); //wie weit ist man im file
            rewind(file); //wieder zurückgehen
            content.resize(filesize); //file resizen damit man nicht zu wenig oder zu viel speicher reserviert
            fread(&content[0], 1, filesize, file); //&content[0] gibt adresse auf speicherbereich - 1 ganze filesize soll gelesen werden - 
            fclose(file); //file wieder schließen
            return content; //code returnen
        }
        GLuint Shader::createShader(const char* vertexShaderFilename, const char* fragmentShaderFilename) {
            std::string vertexShaderSource = parse(vertexShaderFilename); //Sourcecode speichern
            std::string fragmentShaderSource = parse(fragmentShaderFilename);
    
            GLuint program = glCreateProgram(); //program erstellen
            GLuint vs = compile(vertexShaderSource, GL_VERTEX_SHADER); //shader erstellen
            GLuint fs = compile(fragmentShaderSource, GL_FRAGMENT_SHADER);
    
            glAttachShader(program, vs); //shader wird program hinzugefügt
            glAttachShader(program, fs);
    
            glLinkProgram(program); //executable erstellt und läuft auf vertex
            
            //Solange program gelinked ist, kann alles wieder deattached und gelöscht werden - spart speicherplatz
    #ifdef _RELEASE
            glDetachShader(progam, vs);
            glDetachShader(program, fs);
    
            glDeleteShader(vs);
            glDeleteShader(fs);
    #endif
            return program;
    }
    //Vertexshader soll Position von Vertex berechnen
    #version 330 core
    //INPUTS IN DEN SHADER
    layout(location = 0) in vec3 a_position; //layout location 0 -> 1 Attribut
    layout(location = 1) in vec3 a_normal;
    layout(location = 2) in vec2 a_tex_coord;
    
    //OUTPUTS
    out vec3 v_normal;
    out vec3 v_position;
    out vec2 v_tex_coord;
    
    uniform mat4 u_modelmatrix;
    uniform mat4 u_modelView;
    uniform mat4 u_modelViewProj;
    
    uniform mat4 u_invModelView;
    
    
    void main()
    {
        gl_Position = u_modelmatrix * u_modelViewProj * vec4(a_position, 1.0f); //vec4 für Matrixmultiplikationen - Position in positionsvariable - 1.0f -> 4 Koordinate
        v_normal = mat3(u_invModelView) * a_normal; //Normalevktor berechnen
        v_position = vec3(u_modelView * vec4(a_position, 1.0f)); //Position des Fragments berechnen
        v_tex_coord = a_tex_coord;
    } 

Solution

  • For any future reader:

    A more accurate question would be.

    Why did my objects disappear after applying transformation?

    After my initial guess - which you can see below - OP posted more detailed code and in the end I found an error in the shader program.

    The matrix multiplicaton was in the wrong order:

    // OP's original code
    gl_Position = u_modelmatrix * u_modelViewProj * vec4(a_position, 1.0f);
    // Good order
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    

    Reverse order might also be good depending on matrix ordering

    Good tutorial about transformations and coordinate systems


    Original answer

    You didn't provide every part of your code so I can only guess. I assume in your shader class you create a shader program and then you do the linking correctly.

    But in order to update uniforms in a shader, you have to tell that to OpenGL, which shader program to use, by calling:

    glUseProgram(shader->getShaderID());
    //and then
    glUniformMatrix4fv(...);
    ...
    

    That is usually done in a the render() function (especially crucial if you use multiple shaders) and I can't see that in your code.

    Additional code might be needed to find the issue if it's something else (where do you call the render() function and what is happening there? how does you shader program look like?).